Laravel 5 — Чистый код, где хранить бизнес-логику (пример контроллера)

Ниже приведен пример метода «store» моего контроллера Admin / MoviesController. Это уже кажется довольно большим, и метод «обновления» будет еще больше.

Алгоритм это:

  1. Проверьте данные запроса в CreateMovieRequest и создайте новый фильм
    со всеми заполняемыми полями.
  2. Загрузить постер
  3. Заполните и сохраните все важные, но не обязательные поля (Meta title, Meta Description ..)
  4. Затем 4 блока кода с разбором и прикреплением к фильму жанров, актеров, режиссеров, стран.
  5. Запрос рейтинга IMDB с использованием стороннего API

Мои вопросы:

  • Должен ли я просто переместить весь этот код в Model и разделить его на более мелкие методы, такие как: removeGenres ($ id), addGenres (Request $ request), …
  • Есть ли лучшие практики? Я говорю не о MVC, а о возможностях Laravel. На данный момент, чтобы сохранить некоторую логику за сценой, я использую только Запрос на проверку.

    public function store(CreateMovieRequest $request) {
    
    $movie = Movies::create($request->except('poster'));
    
    /* Uploading poster */
    if ($request->hasFile('poster')) {
    $poster = \Image::make($request->file('poster'));
    
    $poster->fit(250, 360, function ($constraint) {
    $constraint->upsize();
    });
    
    $path = storage_path() . '/images/movies/'.$movie->id.'/';
    
    if(! \File::exists($path)) {
    \File::makeDirectory($path);
    }
    
    $filename = time() . '.' . $request->file('poster')->getClientOriginalExtension();
    $poster->save($path . $filename);
    $movie->poster = $filename;
    }
    
    /* If 'Meta Title' is empty, then fill it with the name of the movie */
    if ( empty($movie->seo_title) ) {
    $movie->seo_title = $movie->title;
    }
    
    /* If 'Meta Description' is empty, then fill it with the description of the movie */
    if ( empty($movie->seo_description) ) {
    $movie->seo_description = $movie->description;
    }
    
    // Apply all changes
    $movie->save();
    
    /* Parsing comma separated string of genres
    * and attaching them to movie */
    if (!empty($request->input('genres'))) {
    
    $genres = explode(',', $request->input('genres'));
    
    foreach($genres as $item) {
    $name = mb_strtolower(trim($item), 'UTF-8');
    
    $genre = Genre::where('name', $name)->first();
    
    /* If such genre doesn't exists in 'genres' table
    * then we create a new one */
    if ( empty($genre) ) {
    $genre = new Genre();
    $genre->fill(['name' => $name])->save();
    }
    
    $movie->genres()->attach($genre->id);
    }
    }
    
    /* Parsing comma separated string of countries
    * and attaching them to movie */
    if (!empty($request->input('countries'))) {
    $countries = explode(',', $request->input('countries'));
    foreach($countries as $item) {
    $name = mb_strtolower(trim($item), 'UTF-8');
    
    $country = Country::where('name', $name)->first();
    
    if ( empty($country) ) {
    $country = new Country();
    $country->fill(['name' => $name])->save();
    }
    
    $movie->countries()->attach($country->id);
    }
    }
    
    /* Parsing comma separated string of directors
    * and attaching them to movie */
    if (!empty($request->input('directors'))) {
    $directors = explode(',', $request->input('directors'));
    foreach($directors as $item) {
    $name = mb_strtolower(trim($item), 'UTF-8');
    
    // Actors and Directors stored in the same table 'actors'
    $director = Actor::where('fullname', trim($name))->first();
    
    if ( empty($director) ) {
    $director = new Actor();
    $director->fill(['fullname' => $name])->save();
    }
    // Save this relation to 'movie_director' table
    $movie->directors()->attach($director->id);
    }
    }
    
    /* Parsing comma separated string of actors
    * and attaching them to movie */
    if (!empty($request->input('actors'))) {
    
    $actors = explode(',', $request->input('actors'));
    foreach($actors as $item) {
    $name = mb_strtolower(trim($item), 'UTF-8');
    
    $actor = Actor::where('fullname', $name)->first();
    
    if ( empty($actor) ) {
    $actor = new Actor();
    $actor->fill(['fullname' => $name])->save();
    }
    
    // Save this relation to 'movie_actor' table
    $movie->actors()->attach($actor->id);
    }
    }
    
    // Updating IMDB and Kinopoisk ratings
    if (!empty($movie->kinopoisk_id)) {
    $content = Curl::get('http://rating.kinopoisk.ru/'.$movie->kinopoisk_id.'.xml');
    
    $xml = new \SimpleXMLElement($content[0]->getContent());
    
    $movie->rating_kinopoisk = (double) $xml->kp_rating;
    $movie->rating_imdb = (double) $xml->imdb_rating;
    $movie->num_votes_kinopoisk = (int) $xml->kp_rating['num_vote'];
    $movie->num_votes_imdb = (int) $xml->imdb_rating['num_vote'];
    
    $movie->save();
    }
    
    return redirect('/admin/movies');
    }
    

3

Решение

Вам нужно подумать о том, как вы могли бы повторно использовать код, если вам нужно использовать его в других классах или модулях проекта. Для начала вы можете сделать что-то вроде этого:

Модель фильма, может быть улучшен для того, чтобы:

  • Управляйте тем, как установлены атрибуты
  • Создавайте приятные функции в функциях, включайте / управляйте данными отношений

Посмотрите, как Movie реализует функции:

class Movie{

public function __construct(){

//If 'Meta Title' is empty, then fill it with the name of the movie
$this->seo_title = empty($movie->seo_title)
? $movie->title
: $otherValue;

//If 'Meta Description' is empty,
//then fill it with the description of the movie
$movie->seo_description = empty($movie->seo_description)
? $movie->description
: $anotherValue;

$this->updateKinopoisk();
}

/*
* Parsing comma separated string of countries and attaching them to movie
*/
public function attachCountries($countries){

foreach($countries as $item) {
$name = mb_strtolower(trim($item), 'UTF-8');

$country = Country::where('name', $name)->first();

if ( empty($country) ) {
$country = new Country();
$country->fill(['name' => $name])->save();
}

$movie->countries()->attach($country->id);
}
}

/*
* Update Kinopoisk information
*/
public function updateKinopoisk(){}

/*
* Directors
*/
public function attachDirectors($directors){ ... }

/*
* Actores
*/
public function attachActors($actors){ ... }

/*
* Genders
*/
public function attachActors($actors){ ... }
}

плакат, Вы можете рассмотреть возможность использования поставщика услуг (я покажу этот пример, потому что я не знаю вашу модель плаката
похоже):

public class PosterManager{

public static function upload($file, $movie){
$poster = \Image::make($file);
$poster->fit(250, 360, function ($constraint) {
$constraint->upsize();
});

$path = config('app.images') . $movie->id.'/';

if(! \File::exists($path)) {
\File::makeDirectory($path);
}

$filename = time() . '.' . $file->getClientOriginalExtension();
$poster->save($path . $filename);

return $poster;
}
}

Конфиг файл
Попробуйте использовать файлы конфигурации для хранения соответствующих констант / данных приложения, например, для хранения пути к изображениям фильмов:

'images' => storage_path() . '/images/movies/';

Теперь вы можете позвонить $path = config('app.images'); во всем мире. Если вам нужно изменить путь, необходима только настройка файла конфигурации.

Контроллеры как внедренный класс.
Наконец, контроллер используется как класс, где вам нужно только ввести код:

public function store(CreateMovieRequest $request) {
$movie = Movies::create($request->except('poster'));

/* Uploading poster */
if ($request->hasFile('poster')) {
$file = $request->file('poster');
$poster = \PosterManager::upload($file, $movie);
$movie->poster = $poster->filename;
}

if (!empty($request->input('genres'))) {
$genres = explode(',', $request->input('genres'));

$movie->attachGenders($genders);
}

// movie->attachDirectors();
// movie->attachCountries();

// Apply all changes
$movie->save();

return redirect('/admin/movies');
}
10

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]