V
V
vjun2016-12-16 22:28:52
Design patterns
vjun, 2016-12-16 22:28:52

What is the approach to handle the entity in the controller?

I use the Laravel 5 framework. There is a real example on which I would like to understand what design pattern can be applied, or some other approach to remove code duplication .
There are many controllers in the project with the same code to create/update an entity (news, comments, reviews, etc.).
Let's take the news:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;

class PostsController
{
    /**
     * Создание новой записи.
     *
     * @param  Request  $request
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function store(Request $request)
    {
        $post = new Post;
        $post->author_id = $request->user()->id;
        $post->title = trim($request->get('title'));
        $post->slug  = str_slug($post->title);
        $post->description = trim($request->get('description'));
        $post->content = $request->get('content');

        if ($request->hasFile('cover')) {
            $file = $request->file('cover');
            $fileName = str_random(8) . '.' . $file->getClientOriginalExtension();
            $fileDirectory = '/storage/' . strtolower(substr($fileName, 0, 2));
            $file->move(public_path($fileDirectory), $fileName);
            $post->cover = "$fileDirectory/$fileName";
        }

        $post->save();
    }

    /**
     * Обновление записи.
     *
     * @param  Request  $request
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function update(Request $request, $id)
    {
        $post = Post::find($id);
        $post->title = trim($request->get('title'));
        $post->slug  = str_slug($post->title);
        $post->description = trim($request->get('description'));
        $post->content = $request->get('content');

        if ($request->hasFile('cover')) {
            $file = $request->file('cover');
            $fileName = str_random(8) . '.' . $file->getClientOriginalExtension();
            $fileDirectory = '/storage/' . strtolower(substr($fileName, 0, 2));
            $file->move(public_path($fileDirectory), $fileName);
            $post->cover = "$fileDirectory/$fileName";
        }

        $post->save();
    }
}

The first thing that comes to mind is to create a private method in the controller:
class PostsController
{
    public function store(Request $request)
    {
        $post = new Post;

        $this->save($request, $post);
    }

    public function update(Request $request, $id)
    {
        $post = Post::find($id);

        $this->save($request, $post);
    }

    private function save($request, $post)
    {
        $post->title = trim($request->get('title'));
        $post->slug  = str_slug($post->title);
        $post->description = trim($request->get('description'));
        $post->content = $request->get('content');

        if ($request->hasFile('cover')) {
            $file = $request->file('cover');
            $fileName = str_random(8) . '.' . $file->getClientOriginalExtension();
            $fileDirectory = '/storage/' . strtolower(substr($fileName, 0, 2));
            $file->move(public_path($fileDirectory), $fileName);
            $post->cover = "$fileDirectory/$fileName";
        }

        $post->save();
    }
}

I have seen several projects on GitHub that solved this problem using a repository:
App\Repositories\PostRepository.php
<?php
class PostRepository
{
    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    public function store(Request $request)
    {
        $post = new $this->post;

        $this->save($request, $post);

        return $post;
    }

    public function update(Request $request, Post $post)
    {
        return $this->save($request, $post);
    }

    private function save($request, $post)
    {
        $post->title = trim($request->get('title'));
        $post->slug  = str_slug($post->title);
        $post->description = trim($request->get('description'));
        $post->content = $request->get('content');

        if ($request->hasFile('cover')) {
            $file = $request->file('cover');
            $fileName = str_random(8) . '.' . $file->getClientOriginalExtension();
            $fileDirectory = '/storage/' . strtolower(substr($fileName, 0, 2));
            $file->move(public_path($fileDirectory), $fileName);
            $post->cover = "$fileDirectory/$fileName";
        }

        return $post->save();
    }
}

App\Http\Controllers\PostController.php
class PostController extends Controller
{
    protected $postRepository;

    public function __construct(PostRepository $postRepository)
    {
        $this->postRepository = $postRepository;
    }

    public function store(Request $request)
    {
        if ($this->postRepository->create($request)) {
            // Created
        } else {
            // Fail
        }
    }

    public function update(Request $request, $id)
    {
        $post = Post::find($id);

        if ($this->postRepository->update($request, $post)) {
            // Updated
        } else {
            // Fail
        }
    }
}

but because I'm new to design patterns, so I'm not sure if this is the right approach.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
Vladimir Balin, 2016-12-22
@krocos

You can make a model (entity) manager or a context manager, such as a post manager, which will be a facade (have the necessary api) for working with posts or mail or anything ... And he, in turn, will pull repositories, apply processing strategies, specifications ...
With this method, you create a layer between the controller and the low-level api that will apply the logic. And they are repositories mainly for sampling and saving processed models.
If you have complex logic, then you can use the following approach in the manager: select a processing strategy based on composite specifications (or some other, there are several of them), apply the strategy section with the processing context and then the already processed model to the repository for saving.

E
Eugene, 2016-12-16
@Nc_Soft

Cram logic into models, make controllers thin and models thick.
To the account of the repository https://habrahabr.ru/post/316836/

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question