Laravel将Controller逻辑移至模型

时间:2018-08-20 11:45:05

标签: php laravel

我正处于重构代码的阶段,并且遇到了一个有趣的难题。

在我的ArticleController中,我有一个沼泽标准存储方法,用于将文章存储在我的商品数据库表中。

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(StoreArticle $request)
{
    $article = new Article();

    $defauultPublished = "draft";
    $IntranetOnly = false;
    $isFeatured = false;

    $isFeatured = ($request->get('featuredArticle') == "1" ? true : false);
    $IntranetOnly = ($request->get('IntranetOnly') == "1" ? true : false);

    $article->title = $request->get('title');
    $article->slug = str_slug($request->get('title'));
    $article->author = $request->get('author');
    $article->category = $request->get('category');
    $article->excerpt = $request->get('excerpt');
    $article->content = clean($request->get('content'));
    $article->featuredImage = $request->get('featuredImage');
    $article->featuredVideo = $request->get('featuredVideo');
    $article->readingTime = $this->calculateReadTime($request);
    $article->featuredArticle = $isFeatured;
    $article->IntranetOnly = $IntranetOnly;
    $article->published = $defauultPublished;

    $article->save();

    $article->handleTags($request);

    return redirect('editable/news-and-updates')->with('success', 'Article has been added');
}

我还有一个用于计算读取时间的函数:

/**
 * Calculate a rough reading time for an articles by counting the words present
 * These words are then divided by a given reading time and rounded to the nearest whole number
 * Reading time average is roughly 267 words per minute, so this also accounts for relatively slow readers
 *
 * @param Request $request
 * @return void
 */
public function calculateReadTime(Request $request)
{
    $readingSpeed = 200;

    $title = str_word_count(strip_tags($request->get('title')));
    $excerpt = str_word_count(strip_tags($request->get('excerpt')));
    $content = str_word_count(strip_tags($request->get('content')));

    $words = ($title + $excerpt + $content);

    $minutes = round($words / $readingSpeed);

    return $minutes . ' minute' . ($minutes == 1 ? '' : 's');
}

我的问题是这些方法应该移至Article模型吗?

3 个答案:

答案 0 :(得分:2)

控制器应尽可能小巧。遵循一种足智多谋的方法(您似乎正在这样做),您的store()类中的ArticleController方法应尽力使其看起来像这样:

class ArticleController extends Controller
{
    public function store(CreateArticleRequest $request)
    {
        $article = Article::create($request->validated());

        // Redirect with success message
    }
}

这里,您的请求数据在到达控制器方法之前已经在表单请求类中进行了验证;然后根据该经过验证的数据创建一个Article模型实例。

其他一些注释...

诸如($data['featuredArticle'] == "1" ? true : false)之类的陈述过于冗长。您正在进行条件检查,其结果将为truefalse;您无需在三元运算符中手动返回每个值。因此,可以将其简化为$data['featuedArticle'] == '1'。此外,如果默认情况下传递了0值,则可以完全摆脱检查。如果在Blade模板中,您在复选框前放置了隐藏的输入:

<input type="hidden" name="featuredArticle" value="0" />

<input type="checkbox" name="featuredArticle" value="1" />

然后,如果选中了该复选框,则会发送1(因为它会覆盖隐藏的输入的值,如果未选中此复选框,则会发送0)。

此外,请尝试遵守Laravel约定以使您的生活更轻松。如果您使用snake_case作为输入名称,则使它们与模型属性和表列名称的匹配变得更加容易。因此,使用featured_article,在模型中拥有一个具有相同名称的属性,该属性将再次映射到具有相同名称的数据库列。这使您可以进行create()(根据我的控制器示例)和update()之类的速记电话。

最后,诸如计算阅读时间之类的方法肯定属于您的模型。模型在您的应用程序中表示某物。因此,您可以对模型进行操作。因此,计算读取 Article 模型实例的时间可以使自己在calculateReadingTime()模型上拥有Article方法。

有点long,但希望上面有一些对您有用的指示。我从事Laravel项目已有大约五年的时间,发现这种方法和约定是最有效的。

答案 1 :(得分:1)

您的控制器的store文章很好,因为它可以根据请求数据填充您的文章实例。它可能需要一些重构,并且您可以将更多逻辑封装到Article中(例如,只要更改标题,就在slug模型内分配Article字段)。

但是$article->handleTags($request);行是可疑的,因为您的模型永远都不能对请求进行操作-它会很快用您不想要的非常特殊的依赖项污染您的模型代码(当您从缓存并且没有请求实例?如果其他类型的请求包含不同的标记会发生什么呢?您的模型不应该有关请求或应用程序其他部分的知识。您的控制器正在连接点之间的点,因此请确保handleTags将一些基本抽象类型/结构作为参数(例如,数组),并确保控制器在提要之前从请求中获取并转换数据您的文章。

关于您的calculateReadTime困境,它绝对应该在模型中。以这种方式进行思考-在Article模型中,您是否具备计算文章阅读时间所需的一切?答案是肯定的,它是商品对象的属性,无论是将其存储在数据库中还是根据其他属性进行计算都无关紧要。制作getReadTime方法。您不希望控制器计算有关模型的信息,因为它会将逻辑绑定到应用程序中的特定位置,这很不好(当您需要在其他控制器中计算文章的读取时间时,会发生什么情况?其他模型?以及等等)。

请确保您已阅读有关面向对象设计的hasis概念,它将极大地帮助您。

答案 2 :(得分:-1)

我认为您应该将这些分配移至服务类别。您也可以继续创建存储库类。因此,这将成为您的代码结构:

控制器->服务->存储库->模型。

执行$article = new Article();是不好的。为控制器存储方法编写测试时,您将度过一会儿。

我建议您这样做:

创建一个Service类,例如ArticleService.php。在其中定义一个存储方法。

    ArticleService.php

    use Article;

    class ArticleService {

        protected $article;

        public function __construct(Article $article){
            $this->article = $article;
        }

        public function store(array $data){
            $defauultPublished = "draft";
            $IntranetOnly = false;
            $isFeatured = false;

            $isFeatured = ($data['featuredArticle'] == "1" ? true : false);
            $IntranetOnly = ($data['IntranetOnly'] == "1" ? true : false);
            $this->article->title = $data['title'];
            $this->article->slug = str_slug($data['title']);
            $this->article->author = $data['author'];
            $this->article->category = $data['category'];
            $this->article->excerpt = $data['excerpt'];
            $this->article->content = clean($data['content']);
            $this->article->featuredImage = $data['featuredImage'];
            $this->article->featuredVideo = $data['featuredVideo'];
            $this->article->readingTime = $data['reading_time'];
            $this->article->featuredArticle = $isFeatured;
            //Capital letter I? You should be consistent with your naming convention                
            $this->article->IntranetOnly = $IntranetOnly;
            $this->article->published = $defauultPublished;

            if($this->article->save()){
                $this->article->handleTags($request);
                return true;
            }
            return false;
        }
    }

您的控制器现在变为:

    class ArticleController{

        protected $articleService;

        public function __construct(ArticleService $articleService){
            $this->articleService = $articleService;
        }

        public function store(Request $request){

            //Some Validation Logic
            $readingTime = $this->calculateReadTime($request)
            $data = array_merge(['reading_time' => $readTime], $request->all());
            return $this->articleService->store($request->all());
        }
    }

我还看到您没有验证传入的请求。您应该始终这样做,因为您可以/永远不应该信任您的用户始终提供/输入正确的数据。您有义务强迫他们这样做。例如,我作为您的用户可能决定在您的电子邮件字段中输入我的名字。如果您不验证该数据,那么最终将得到错误的数据。

还存在将您的请求参数分别分配给其相应的Model属性的问题。我决定采用这种方式,以免信息过多。

总而言之,请看以下资源以获得更多见解。 https://laravel.com/docs/5.1/quickstart-intermediate https://laravel.com/docs/5.6/validation 简而言之,请阅读整个Laravel文档!祝你好运!