repository implementation

repository implementation

Repository reason

This article will introduce the implementation of Repository. The github project based on it is: l5-repository , the source code is a well-made textbook, and all design intents in front of the code are invisible.

Let's first clarify what the problem needs to be solved and why the l5-repository project appears .

I think you must have encountered this problem: at the beginning our Model was only a few hundred lines, but as the project functions continue to become more complex, the model needs to implement more and more functions. When we suddenly look back one day When I looked at it, I found that Model has thousands of lines, and we can't remember what functions are in it.

Then why does the model get fatter ? Let's take a look at the possible functions in the model.

  • Business logic written in php
  • According to the display requirements, we do the data format conversion, the selective return of the fields
  • Various conditions of inquiry
  • Verification of creation parameters and query parameters
  • ..

According to the classification, we can be summarized into the following categories

  1. presenter, display requirements
  2. repository, access requirements
  3. validator, parameter verification requirements
  4. services, business logic

So we have the following architecture diagram:


Picture from Dianlampfang

With this picture, we will look at the implementation of the l5-repository project, it will be easier to understand.

Below I will explain the implementation of l5-repository from three aspects : repository, presenter, validator. Let's first look at the implementation of the repository.

Principle of repository

At the beginning, I couldn t avoid the usual routines, let s first define the repository.

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes.

There are two core points

  • Repository is similar to a collection, responsible for the access of objects, hiding the implementation of specific data mapping
  • The repository can return specific data according to the query conditions constructed by the client

Let's first look at the first point, object access, in the final analysis, CRUD operations, so there is the interface definition of the repository RepositoryInterface, the specific interfaces are:


For CRUD, the most difficult thing is the C operation, because there are various query methods, and the query interfaces provided are:

public function find($id, $columns = ['*']);
public function findByField($field, $value, $columns = ['*']);
public function findWhere(array $where, $columns = ['*']);
public function findWhereIn($field, array $values, $columns = ['*']);
public function findWhereNotIn($field, array $values, $columns = ['*']); 

Then the default implementation is Prettus\Repository\Eloquent\BaseRepositorybasically the right Eloquent\Builderpackage.

But how can some more complex queries be satisfied? Do we need to add findCondition interface every time there is a new query? Obviously we can't do this, Criteriait's a grand debut at this time, let's take a look at the interface:

interface CriteriaInterface
{
   /**
     * Apply criteria in query repository
     *
     * @param                     $model
     * @param RepositoryInterface $repository
     *
     * @return mixed
     */
    public function apply($model, RepositoryInterface $repository);
} 

All Criteria need to implement the apply method, see one may be implemented:

class LengthOverTwoHours implements CriteriaInterface {
    public function apply($model, Repository $repository)
    {
        $query = $model->where('length', '>', 120);
        return $query;
    }
} 

By defining LengthOverTwoHours, we can add new queries to the Model. In this way, every time we have a new query condition, we only need to create a new Criteria, which meets the open and closed principle.

Then we come to use l5-repository . First php artisan make:entity Bookgenerate files through commands , and then add new ones in AppServiceProvider@register

$this->app->register( RepositoryServiceProvider::class); 

Then generate a Controller

php artisan make:controller -r BookController 

In it, we can use injection into the Repository

public function __construct( BookRepository $bookRepository )
{
      $this->bookRepository = $bookRepository;
} 

Then you can go to github.com/andersao/l5 for some specific operations ...

Finally, I will introduce how to generate criteria, through the following command

php artisan make:criteria My 

Then add the following code

class MyCriteria implements CriteriaInterface
{
   /**
     * Apply criteria in query repository
     *
     * @param Builder                    $model
     * @param RepositoryInterface $repository
     *
     * @return mixed
     */
    public function apply($model, RepositoryInterface $repository)
    {
        $model = $model->where('user_id','=',/Auth::user()->id );
        return $model;
    }
} 

It can be used, and then in the controller, we query in the following way

public function index()
{
  $this->repository->pushCriteria(new MyCriteria());
  $books = $this->repository->all();
  ...
} 

Presenters optimization

Then we talk about the Presenters part.

Let's emphasize the problem that Presenter solves again: extracting presentation logic such as dates, amounts, and names. In fact, the function of l5-repository is not very satisfactory. What we hope is the appearance introduced in 1 Presenter . The original appearance is:

class Article extends Eloquent
{
    public function getDate(){/*...*/}

    public function getTaiwaneseDateTime(){/*...*/}

    public function getWesternDateTime(){/*...*/}

    public function getTaiwaneseDate(){/*...*/}

    public function getWesternDate(){/*...*/}
} 

After being pulled out:

class Article extends Eloquent
{
    public function present()
    {
        return new ArticlePresenter($this);
    }
}

class ArticlePresenter extends Presenter {

    public function getTaiwaneseDateTime(){/*...*/}

    public function getWesternDateTime(){/*...*/}

    public function getTaiwaneseDate(){/*...*/}

    public function getWesternDate(){/*...*/}
} 

Here are some implementation schemes

github.com/laracasts/P...

github.com/robclancy/p...

github.com/laravel-aut...

Parameter verification

Finally, we introduce validator

The logic of the validator is extracted from the model and put into a class separately,

use/Prettus\Validator\Contracts\ValidatorInterface;
use/Prettus\Validator\LaravelValidator;

class PostValidator extends LaravelValidator {

    protected $rules = [
        ValidatorInterface::RULE_CREATE => [
            'title' => 'required',
            'text'  => 'min:3',
            'author'=> 'required'
        ],
        ValidatorInterface::RULE_UPDATE => [
            'title' => 'required'
        ]
   ];

} 

Be able to formulate different verification rules for create and update operations.

summary

The above is all about the repository. At the beginning of the article, the model is getting fatter and fatter in the actual project, leading to how to slim the model, and then the functions in the model are divided, and a reasonable project organization method is given, and then through the repository, presenter, validator Some specific optimization methods are analyzed.

Finally, this article is just a simple analysis of l5-repository introduction . For more detailed functions and more implementation details, you can clone the project and watch it yourself. I believe you will learn a lot.

reference

5.ways to lose weight on a fat model

Using Repository Pattern in Laravel 5

Weight loss method of fat model: PRESENTER

Laravel's medium and large project architecture