首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使控制器变薄。Laravel 8

使控制器变薄。Laravel 8
EN

Stack Overflow用户
提问于 2021-01-22 17:00:38
回答 2查看 663关注 0票数 1

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

代码语言:javascript
运行
复制
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');
}

方法handleUploadedImage()现在存储在PostController本身中。但我会用在其他控制器上。我应该把它搬到哪里去?在请求类中没有,因为这与验证无关。不是在模特/邮报,因为这不仅仅是为邮政模式。对于类来说,它并不是一个全局函数。

方法firstSentence()randomTemplate()也存储在该控制器中。它们只会在里面使用。也许我应该把它们搬到模特/邮政?这样,如何在方法store() (更具体地说,在方法create()中)中准确地调用它们?

我读过这个理论,我理解(希望)瘦控制器和Fat模型的概念,但我需要一些实际的具体建议这个例子。请你建议一下,搬到哪里去,怎么称呼这些方法?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-01-23 08:10:56

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

事实上,控制器应该始终保持精简。但这也应该适用于模型层。通过将特定于应用程序的逻辑移动到应用程序服务(所以,不是进入模型层,使模型变胖!),这两个目标都是可以实现的。它们是所谓的服务层的组件.也可以阅读

在您的示例中,您似乎可以优雅地将上传图像的处理逻辑推送到服务中,例如,包含App\Service\Http\Upload\ImageHandler方法的服务。但是,可以更好地选择类和方法的名称,这取决于确切的类责任。

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

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

关于第一项任务,App\Service\Post服务的两种方法可能是有用的:

  • generatePostTitle,封装从用户提供的“内容”中提取第一句的逻辑,以便从其中设置post实体的标题;
  • generatePostTemplate,包含您在注释中描述的关于randomTemplate()的逻辑。

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

服务:

代码语言:javascript
运行
复制
<?php

namespace App\Service;

use Carbon;
use Domain\Model\Post\Post;
use Domain\Model\Post\PostCollection;

/**
 * Post service.
 */
class Post {

    /**
     * Post collection.
     * 
     * @var PostCollection
     */
    private $postCollection;

    /**
     * @param PostCollection $postCollection Post collection.
     */
    public function __construct(PostCollection $postCollection) {
        $this->postCollection = $postCollection;
    }

    /**
     * Create a post.
     * 
     * @param string $content Post content.
     * @param string $template The template used for the post.
     * @param bool $isPublished (optional) Indicate if the post is published.
     * @return Post Post.
     */
    public function createPost(
        string $content,
        string $template,
        bool $isPublished
        /* , ... */
    ) {
        $title = $this->generatePostTitle($content);
        $slug = $this->generatePostSlug();
        $template = $this->generatePostTemplate($template);

        $post = new Post();

        $post
            ->setTitle($title)
            ->setContent($content)
            ->setIsPublished($isPublished ? 1 : 0)
            ->setSlug($slug)
            ->setTemplate($template)
        ;

        return $post;
    }

    /**
     * Store a post.
     * 
     * @param Post $post Post.
     * @return Post Post.
     */
    public function storePost(Post $post) {
        return $this->postCollection->storePost($post);
    }

    /**
     * Generate the title of a post by extracting 
     * a certain part from the given post content.
     * 
     * @return string Generated post title.
     */
    private function generatePostTitle(string $content) {
        return substr($content, 0, 300) . '...';
    }

    /**
     * Generate the slug of a post.
     * 
     * @return string Generated slug.
     */
    private function generatePostSlug() {
        return Carbon::now()->format('Y-m-d-His');
    }

    /**
     * Generate the template assignable to 
     * a post based on the given template.
     * 
     * @return string Generated post template.
     */
    private function generatePostTemplate(string $template) {
        return 'the-generated-template';
    }

}

存储库接口:

代码语言:javascript
运行
复制
<?php

namespace Domain\Model\Post;

use Domain\Model\Post\Post;

/**
 * Post collection interface.
 */
interface PostCollection {

    /**
     * Store a post.
     * 
     * @param Post $post Post.
     * @return Post Post.
     */
    public function storePost(Post $post);

    /**
     * Find a post by id.
     * 
     * @param int $id Post id.
     * @return Post|null Post.
     */
    public function findPostById(int $id);

    /**
     * Find all posts.
     * 
     * @return Post[] Post list.
     */
    public function findAllPosts();

    /**
     * Check if the given post exists.
     * 
     * @param Post $post Post.
     * @return bool True if post exists, false otherwise.
     */
    public function postExists(Post $post);
}

存储库实现:

代码语言:javascript
运行
复制
<?php

namespace Domain\Infrastructure\Repository\Post;

use Domain\Model\Post\Post;
use Domain\Infrastructure\Mapper\Post\PostMapper;
use Domain\Model\Post\PostCollection as PostCollectionInterface;

/**
 * Post collection.
 */
class PostCollection implements PostCollectionInterface {

    /**
     * Posts list.
     * 
     * @var Post[]
     */
    private $posts;

    /**
     * Post mapper.
     * 
     * @var PostMapper
     */
    private $postMapper;

    /**
     * @param PostMapper $postMapper Post mapper.
     */
    public function __construct(PostMapper $postMapper) {
        $this->postMapper = $postMapper;
    }

    /**
     * Store a post.
     * 
     * @param Post $post Post.
     * @return Post Post.
     */
    public function storePost(Post $post) {
        $savedPost = $this->postMapper->savePost($post);

        $this->posts[$savedPost->getId()] = $savedPost;

        return $savedPost;
    }

    /**
     * Find a post by id.
     * 
     * @param int $id Post id.
     * @return Post|null Post.
     */
    public function findPostById(int $id) {
        //... 
    }

    /**
     * Find all posts.
     * 
     * @return Post[] Post list.
     */
    public function findAllPosts() {
        //...
    }

    /**
     * Check if the given post exists.
     * 
     * @param Post $post Post.
     * @return bool True if post exists, false otherwise.
     */
    public function postExists(Post $post) {
        //...
    }

}

数据映射器接口:

代码语言:javascript
运行
复制
<?php

namespace Domain\Infrastructure\Mapper\Post;

use Domain\Model\Post\Post;

/**
 * Post mapper.
 */
interface PostMapper {

    /**
     * Save a post.
     * 
     * @param Post $post Post.
     * @return Post Post entity with id automatically assigned upon persisting.
     */
    public function savePost(Post $post);

    /**
     * Fetch a post by id.
     * 
     * @param int $id Post id.
     * @return Post|null Post.
     */
    public function fetchPostById(int $id);

    /**
     * Fetch all posts.
     * 
     * @return Post[] Post list.
     */
    public function fetchAllPosts();

    /**
     * Check if a post exists.
     * 
     * @param Post $post Post.
     * @return bool True if the post exists, false otherwise.
     */
    public function postExists(Post $post);
}

数据映射器PDO实现:

代码语言:javascript
运行
复制
<?php

namespace Domain\Infrastructure\Mapper\Post;

use PDO;
use Domain\Model\Post\Post;
use Domain\Infrastructure\Mapper\Post\PostMapper;

/**
 * PDO post mapper.
 */
class PdoPostMapper implements PostMapper {

    /**
     * Database connection.
     * 
     * @var PDO
     */
    private $connection;

    /**
     * @param PDO $connection Database connection.
     */
    public function __construct(PDO $connection) {
        $this->connection = $connection;
    }

    /**
     * Save a post.
     * 
     * @param Post $post Post.
     * @return Post Post entity with id automatically assigned upon persisting.
     */
    public function savePost(Post $post) {
        /*
         * If $post->getId() is set, then call $this->updatePost() 
         * to update the existing post record in the database.
         * Otherwise call $this->insertPost() to insert a new 
         * post record in the database.
         */
        // ...
    }

    /**
     * Fetch a post by id.
     * 
     * @param int $id Post id.
     * @return Post|null Post.
     */
    public function fetchPostById(int $id) {
        //...    
    }

    /**
     * Fetch all posts.
     * 
     * @return Post[] Post list.
     */
    public function fetchAllPosts() {
        //...
    }

    /**
     * Check if a post exists.
     * 
     * @param Post $post Post.
     * @return bool True if the post exists, false otherwise.
     */
    public function postExists(Post $post) {
        //...
    }

    /**
     * Update an existing post.
     * 
     * @param Post $post Post.
     * @return Post Post entity with the same id upon updating.
     */
    private function updatePost(Post $post) {
        // Persist using SQL and PDO statements...
    }

    /**
     * Insert a post.
     * 
     * @param Post $post Post.
     * @return Post Post entity with id automatically assigned upon persisting.
     */
    private function insertPost(Post $post) {
        // Persist using SQL and PDO statements...
    }

}

到头来,你的控制器看起来会像咆哮一样。通过读取它的代码,它的作用变得很明显:只为了将用户输入推到服务层。服务层的使用提供了可重用性的巨大优势。

代码语言:javascript
运行
复制
<?php

namespace App\Controller;

use App\Service\Post;
use App\Service\Http\Upload\ImageHandler;

class PostController {

    private $imageHandler;
    private $postService;

    public function __construct(ImageHandler $imageHandler, Post $postService) {
        $this->imageHandler = $imageHandler;
        $this->postService = $postService;
    }

    public function storePost(Request $request) {
        $this->imageHandler->handle(
            $request->file('upload'),
            $request->input('CKEditorFuncNum')
        );

        $post = $this->postService->createPost(
            request('content'),
            request('template'),
            request('is_published')
            /* , ... */
        );

        return redirect()->route('posts');
    }

}

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

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

资源

在sitepoint.com上有一个很好的4部分的Gervasio系列:

票数 5
EN

Stack Overflow用户

发布于 2021-01-22 17:33:20

我通常做的事

代码语言:javascript
运行
复制
public function store(ValidationRequest $request)
{
    $result = $this->dispatchNow($request->validated())
    return redirect()->route('posts');
}

因此,我创建了一个处理这些注册步骤的作业,并且可以在我的系统的另一个部分中重用它。

我将转到一个名为firstSentence app\helpers\stringshelper (不要忘记在composer.json中更新它,您可以在系统的任何部分使用firstSentence($var) )。

randomTemplate可以很好地适应一个特性,但我不知道这种方法能做什么。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65849520

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档