使控制器更薄。拉拉维尔 8

Fam*_*xis 1 php model-view-controller laravel

我有 PostController,这是它的方法store()

public function store(Request $request)
{
    $this->handleUploadedImage(
        $request->file('upload'),
        $request->input('CKEditorFuncNum')
    );

    $post = Post::create([
        'content'      => request('content'),
        'is_published' => request('is_published'),
        'slug'         => Carbon::now()->format('Y-m-d-His'),
        'title'        => $this->firstSentence(request('content')),
        'template'     => $this->randomTemplate(request('template')),
    ]);
    $post->tag(explode(',', $request->tags));

    return redirect()->route('posts');
}
Run Code Online (Sandbox Code Playgroud)

方法handleUploadedImage()现在存储在 PostController 本身中。但我将在其他控制器中使用它。我应该把它移到哪里?不在 Request 类中,因为它与验证无关。不在 Models/Post 中,因为它不仅适用于 Post 模型。对于服务提供者类来说,它并不是那么全局的函数。

方法firstSentence()randomTemplate()也存储在该控制器中。它们只会在其中使用。也许我应该将它们移到模型/帖子中?那么,到底如何在 method 中调用它们store()(更具体地说,在 method 中create())?

我阅读了该理论,并且(希望)理解了瘦控制器和胖模型的概念,但我需要针对此示例提供一些实用的具体建议。您能否建议移动到哪里以及如何调用这些方法?

dak*_*kis 5

首先,请注意:我不使用 Laravel,所以我将向您展示一个与所有框架相关的通用解决方案。

\n

事实上,控制器应该始终保持薄型。但这也应该适用于模型层。这两个目标都可以通过将特定于应用程序的逻辑移动到应用程序服务中来实现(因此,不要移动到模型层中,从而使模型变得臃肿!)。它们是所谓服务层的组件。读这个

\n

在您的情况下,您似乎可以优雅地将上传图像的处理逻辑推送到服务中,例如App\\Service\\Http\\Upload\\ImageHandler包含一个handle方法。不过,根据具体的类职责,可以更好地选择类和方法的名称。

\n

创建和存储帖子的逻辑将进入另一个应用程序服务:App\\Service\\Post例如。原则上,该服务将执行以下任务:

\n
    \n
  1. 创建一个实体 -Domain\\Model\\Post\\Post例如 - 并根据用户输入设置其属性( titlecontentis_published、等)。例如,template这可以在方法中完成。App\\Service\\Post::createPost
  2. \n
  3. 将实体作为数据库记录存储在数据库中。App\\Service\\Post::storePost例如,这可以在方法中完成。
  4. \n
  5. 其他任务...
  6. \n
\n

对于第一个任务,有两种服务方法App\\Service\\Post可能有用:

\n
    \n
  • generatePostTitle,封装从用户提供的“内容”中提取第一句话的逻辑,以便从中设置帖子实体的标题;
  • \n
  • generatePostTemplate,包含您在评论中描述的有关 的逻辑randomTemplate()
  • \n
\n

关于第二个任务,就我个人而言,我将通过使用特定的数据映射器(直接与数据库 API 通信)以及其上的特定存储库(作为帖子对象集合的抽象)将实体存储在数据库中。

\n

服务:

\n
<?php\n\nnamespace App\\Service;\n\nuse Carbon;\nuse Domain\\Model\\Post\\Post;\nuse Domain\\Model\\Post\\PostCollection;\n\n/**\n * Post service.\n */\nclass Post {\n\n    /**\n     * Post collection.\n     * \n     * @var PostCollection\n     */\n    private $postCollection;\n\n    /**\n     * @param PostCollection $postCollection Post collection.\n     */\n    public function __construct(PostCollection $postCollection) {\n        $this->postCollection = $postCollection;\n    }\n\n    /**\n     * Create a post.\n     * \n     * @param string $content Post content.\n     * @param string $template The template used for the post.\n     * @param bool $isPublished (optional) Indicate if the post is published.\n     * @return Post Post.\n     */\n    public function createPost(\n        string $content,\n        string $template,\n        bool $isPublished\n        /* , ... */\n    ) {\n        $title = $this->generatePostTitle($content);\n        $slug = $this->generatePostSlug();\n        $template = $this->generatePostTemplate($template);\n\n        $post = new Post();\n\n        $post\n            ->setTitle($title)\n            ->setContent($content)\n            ->setIsPublished($isPublished ? 1 : 0)\n            ->setSlug($slug)\n            ->setTemplate($template)\n        ;\n\n        return $post;\n    }\n\n    /**\n     * Store a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post.\n     */\n    public function storePost(Post $post) {\n        return $this->postCollection->storePost($post);\n    }\n\n    /**\n     * Generate the title of a post by extracting \n     * a certain part from the given post content.\n     * \n     * @return string Generated post title.\n     */\n    private function generatePostTitle(string $content) {\n        return substr($content, 0, 300) . \'...\';\n    }\n\n    /**\n     * Generate the slug of a post.\n     * \n     * @return string Generated slug.\n     */\n    private function generatePostSlug() {\n        return Carbon::now()->format(\'Y-m-d-His\');\n    }\n\n    /**\n     * Generate the template assignable to \n     * a post based on the given template.\n     * \n     * @return string Generated post template.\n     */\n    private function generatePostTemplate(string $template) {\n        return \'the-generated-template\';\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

存储库接口:

\n
<?php\n\nnamespace Domain\\Model\\Post;\n\nuse Domain\\Model\\Post\\Post;\n\n/**\n * Post collection interface.\n */\ninterface PostCollection {\n\n    /**\n     * Store a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post.\n     */\n    public function storePost(Post $post);\n\n    /**\n     * Find a post by id.\n     * \n     * @param int $id Post id.\n     * @return Post|null Post.\n     */\n    public function findPostById(int $id);\n\n    /**\n     * Find all posts.\n     * \n     * @return Post[] Post list.\n     */\n    public function findAllPosts();\n\n    /**\n     * Check if the given post exists.\n     * \n     * @param Post $post Post.\n     * @return bool True if post exists, false otherwise.\n     */\n    public function postExists(Post $post);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

存储库实施:

\n
<?php\n\nnamespace Domain\\Infrastructure\\Repository\\Post;\n\nuse Domain\\Model\\Post\\Post;\nuse Domain\\Infrastructure\\Mapper\\Post\\PostMapper;\nuse Domain\\Model\\Post\\PostCollection as PostCollectionInterface;\n\n/**\n * Post collection.\n */\nclass PostCollection implements PostCollectionInterface {\n\n    /**\n     * Posts list.\n     * \n     * @var Post[]\n     */\n    private $posts;\n\n    /**\n     * Post mapper.\n     * \n     * @var PostMapper\n     */\n    private $postMapper;\n\n    /**\n     * @param PostMapper $postMapper Post mapper.\n     */\n    public function __construct(PostMapper $postMapper) {\n        $this->postMapper = $postMapper;\n    }\n\n    /**\n     * Store a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post.\n     */\n    public function storePost(Post $post) {\n        $savedPost = $this->postMapper->savePost($post);\n\n        $this->posts[$savedPost->getId()] = $savedPost;\n\n        return $savedPost;\n    }\n\n    /**\n     * Find a post by id.\n     * \n     * @param int $id Post id.\n     * @return Post|null Post.\n     */\n    public function findPostById(int $id) {\n        //... \n    }\n\n    /**\n     * Find all posts.\n     * \n     * @return Post[] Post list.\n     */\n    public function findAllPosts() {\n        //...\n    }\n\n    /**\n     * Check if the given post exists.\n     * \n     * @param Post $post Post.\n     * @return bool True if post exists, false otherwise.\n     */\n    public function postExists(Post $post) {\n        //...\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

数据映射器接口:

\n
<?php\n\nnamespace Domain\\Infrastructure\\Mapper\\Post;\n\nuse Domain\\Model\\Post\\Post;\n\n/**\n * Post mapper.\n */\ninterface PostMapper {\n\n    /**\n     * Save a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post entity with id automatically assigned upon persisting.\n     */\n    public function savePost(Post $post);\n\n    /**\n     * Fetch a post by id.\n     * \n     * @param int $id Post id.\n     * @return Post|null Post.\n     */\n    public function fetchPostById(int $id);\n\n    /**\n     * Fetch all posts.\n     * \n     * @return Post[] Post list.\n     */\n    public function fetchAllPosts();\n\n    /**\n     * Check if a post exists.\n     * \n     * @param Post $post Post.\n     * @return bool True if the post exists, false otherwise.\n     */\n    public function postExists(Post $post);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

数据映射器 PDO 实现:

\n
<?php\n\nnamespace Domain\\Infrastructure\\Mapper\\Post;\n\nuse PDO;\nuse Domain\\Model\\Post\\Post;\nuse Domain\\Infrastructure\\Mapper\\Post\\PostMapper;\n\n/**\n * PDO post mapper.\n */\nclass PdoPostMapper implements PostMapper {\n\n    /**\n     * Database connection.\n     * \n     * @var PDO\n     */\n    private $connection;\n\n    /**\n     * @param PDO $connection Database connection.\n     */\n    public function __construct(PDO $connection) {\n        $this->connection = $connection;\n    }\n\n    /**\n     * Save a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post entity with id automatically assigned upon persisting.\n     */\n    public function savePost(Post $post) {\n        /*\n         * If $post->getId() is set, then call $this->updatePost() \n         * to update the existing post record in the database.\n         * Otherwise call $this->insertPost() to insert a new \n         * post record in the database.\n         */\n        // ...\n    }\n\n    /**\n     * Fetch a post by id.\n     * \n     * @param int $id Post id.\n     * @return Post|null Post.\n     */\n    public function fetchPostById(int $id) {\n        //...    \n    }\n\n    /**\n     * Fetch all posts.\n     * \n     * @return Post[] Post list.\n     */\n    public function fetchAllPosts() {\n        //...\n    }\n\n    /**\n     * Check if a post exists.\n     * \n     * @param Post $post Post.\n     * @return bool True if the post exists, false otherwise.\n     */\n    public function postExists(Post $post) {\n        //...\n    }\n\n    /**\n     * Update an existing post.\n     * \n     * @param Post $post Post.\n     * @return Post Post entity with the same id upon updating.\n     */\n    private function updatePost(Post $post) {\n        // Persist using SQL and PDO statements...\n    }\n\n    /**\n     * Insert a post.\n     * \n     * @param Post $post Post.\n     * @return Post Post entity with id automatically assigned upon persisting.\n     */\n    private function insertPost(Post $post) {\n        // Persist using SQL and PDO statements...\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

最后,您的控制器将如下所示。通过阅读它的代码,它的作用就很明显了:只是将用户输入推送到服务层。服务层的使用提供了可重用性的巨大优势。

\n
<?php\n\nnamespace App\\Controller;\n\nuse App\\Service\\Post;\nuse App\\Service\\Http\\Upload\\ImageHandler;\n\nclass PostController {\n\n    private $imageHandler;\n    private $postService;\n\n    public function __construct(ImageHandler $imageHandler, Post $postService) {\n        $this->imageHandler = $imageHandler;\n        $this->postService = $postService;\n    }\n\n    public function storePost(Request $request) {\n        $this->imageHandler->handle(\n            $request->file(\'upload\'),\n            $request->input(\'CKEditorFuncNum\')\n        );\n\n        $post = $this->postService->createPost(\n            request(\'content\'),\n            request(\'template\'),\n            request(\'is_published\')\n            /* , ... */\n        );\n\n        return redirect()->route(\'posts\');\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

PS:请记住,模型层不得知道其数据来自何处,也不知道传递给它的数据是如何创建的。因此,模型层必须不知道有关浏览器、请求、控制器、视图、响应等的任何信息。它只接收原始值、对象或 DTO(“数据传输对象”):参数 - 例如,请参阅上面的存储库和数据映射器。

\n

PS 2:请注意,许多框架都在谈论存储库,但实际上,他们在谈论数据映射器。我的建议是在你的头脑和代码中遵循福勒的约定。因此,创建数据映射器以便直接访问持久性空间(数据库、文件系统等)。如果您的项目变得更加复杂,或者您只想拥有类似集合的抽象,那么您可以在映射器之上添加一个新的抽象层:存储

\n

资源

\n\n

sitepoint.com 上还有一个由 4 部分组成的精彩 Gervasio 系列:

\n\n