Laravel 5 - 干净的代码,在哪里保存业务逻辑(控制器示例)
Laravel 5 - Clean code, where to keep business logic (controller example)
下面是我的控制器 Admin/MoviesController 的 'store' 方法示例。它已经看起来很大了,'update'方法会更大。
算法是:
- 验证 CreateMovieRequest 中的请求数据并创建新电影
包含所有可填写字段。
- 上传海报
- 填写并保存所有重要但非必需的字段(元标题、元描述..)
- 然后是 4 个代码块,解析并附加到流派、演员、导演、国家/地区的电影。
- 使用 third-party API
请求 IMDB 评级
我的问题:
- 我是否应该将所有这些代码移动到模型并将其分成更小的方法,例如: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');
}
如果您需要在另一个 classes 或项目模块中使用它,您需要考虑如何重新利用代码。首先,您可以这样做:
电影模型,可以改进为:
- 管理属性的设置方式
- 在函数中创建漂亮的函数include/manage关系数据
看看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;
}
}
配置文件
尝试使用配置文件存储相关应用程序constanst/data,例如存储电影图像路径:
'images' => storage_path() . '/images/movies/';
现在,您可以在全球范围内调用 $path = config('app.images');
。如果需要更改路径,只需设置配置文件即可。
注入的控制器 class。
最后,控制器用作class,您只需要注入代码:
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');
}
下面是我的控制器 Admin/MoviesController 的 'store' 方法示例。它已经看起来很大了,'update'方法会更大。
算法是:
- 验证 CreateMovieRequest 中的请求数据并创建新电影 包含所有可填写字段。
- 上传海报
- 填写并保存所有重要但非必需的字段(元标题、元描述..)
- 然后是 4 个代码块,解析并附加到流派、演员、导演、国家/地区的电影。
- 使用 third-party API 请求 IMDB 评级
我的问题:
- 我是否应该将所有这些代码移动到模型并将其分成更小的方法,例如: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'); }
如果您需要在另一个 classes 或项目模块中使用它,您需要考虑如何重新利用代码。首先,您可以这样做:
电影模型,可以改进为:
- 管理属性的设置方式
- 在函数中创建漂亮的函数include/manage关系数据
看看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;
}
}
配置文件 尝试使用配置文件存储相关应用程序constanst/data,例如存储电影图像路径:
'images' => storage_path() . '/images/movies/';
现在,您可以在全球范围内调用 $path = config('app.images');
。如果需要更改路径,只需设置配置文件即可。
注入的控制器 class。 最后,控制器用作class,您只需要注入代码:
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');
}