我有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');
}
方法handleUploadedImage()
现在存储在PostController本身中。但我会用在其他控制器上。我应该把它搬到哪里去?在请求类中没有,因为这与验证无关。不是在模特/邮报,因为这不仅仅是为邮政模式。对于类来说,它并不是一个全局函数。
方法firstSentence()
和randomTemplate()
也存储在该控制器中。它们只会在里面使用。也许我应该把它们搬到模特/邮政?这样,如何在方法store()
(更具体地说,在方法create()
中)中准确地调用它们?
我读过这个理论,我理解(希望)瘦控制器和Fat模型的概念,但我需要一些实际的具体建议这个例子。请你建议一下,搬到哪里去,怎么称呼这些方法?
发布于 2021-01-23 08:10:56
首先,注意:我不使用Laravel,所以我将向您展示一个与所有框架相关的通用解决方案。
事实上,控制器应该始终保持精简。但这也应该适用于模型层。通过将特定于应用程序的逻辑移动到应用程序服务(所以,不是进入模型层,使模型变胖!),这两个目标都是可以实现的。它们是所谓的服务层的组件.也可以阅读这。
在您的示例中,您似乎可以优雅地将上传图像的处理逻辑推送到服务中,例如,包含App\Service\Http\Upload\ImageHandler
方法的服务。但是,可以更好地选择类和方法的名称,这取决于确切的类责任。
创建和存储post的逻辑将进入另一个应用程序服务:例如,App\Service\Post
。原则上,该服务将执行以下任务:
Domain\Model\Post\Post
),并根据用户输入设置其属性(title
、content
、is_published
、template
等)。例如,这可以在方法App\Service\Post::createPost
中完成。App\Service\Post::storePost
中完成。关于第一项任务,App\Service\Post
服务的两种方法可能是有用的:
generatePostTitle
,封装从用户提供的“内容”中提取第一句的逻辑,以便从其中设置post实体的标题;generatePostTemplate
,包含您在注释中描述的关于randomTemplate()
的逻辑。关于第二项任务,我个人将通过使用特定的数据映射器 (直接与数据库API通信)以及在其之上的特定存储库来将实体存储在数据库中,作为post对象集合的抽象。
服务:
<?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';
}
}
存储库接口:
<?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);
}
存储库实现:
<?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) {
//...
}
}
数据映射器接口:
<?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实现:
<?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...
}
}
到头来,你的控制器看起来会像咆哮一样。通过读取它的代码,它的作用变得很明显:只为了将用户输入推到服务层。服务层的使用提供了可重用性的巨大优势。
<?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系列:
发布于 2021-01-22 17:33:20
我通常做的事
public function store(ValidationRequest $request)
{
$result = $this->dispatchNow($request->validated())
return redirect()->route('posts');
}
因此,我创建了一个处理这些注册步骤的作业,并且可以在我的系统的另一个部分中重用它。
我将转到一个名为firstSentence
app\helpers\strings
的helper
(不要忘记在composer.json
中更新它,您可以在系统的任何部分使用firstSentence($var) )。
randomTemplate
可以很好地适应一个特性,但我不知道这种方法能做什么。
https://stackoverflow.com/questions/65849520
复制相似问题