followloop/laravel-repositories

此包的最新版本(v0.1.12)没有可用的许可信息。

一个提供将Repository模式与Laravel & Eloquent集成的整洁实现的包。

这个包的官方仓库似乎已经消失,因此包已被冻结。

v0.1.12 2017-11-08 13:48 UTC

README

一个提供将Repository模式与Laravel & Eloquent集成的整洁实现的包。

目标

与仓库一起工作可以提供一种很好的方式来解耦代码,同时分离关注点并隔离组件,以及分离和分组责任。大多数时候,我们将在数据库表上执行非常通用的操作,如创建、更新、过滤或删除。

然而,使用仓库并不总是好主意,特别是在Laravel及其ORM Eloquent中,因为有时它迫使我们为了 更好的架构 而放弃一些出色的功能(这取决于具体情况)。对于某些项目来说,这可能是一种过度设计,因此开发者需要自行判断是否需要这种复杂度。

此包旨在在Laravel的Eloquent ORM上实现Repository模式时提供一个模板。它通过提供一个RepositoryInterface和一个基本的Repository实现来实现,该实现可以与Eloquent模型一起工作,提供基本的覆盖85%数据库操作的方法。

安装

  1. 通过将其添加到composer.json或通过在项目文件夹中运行composer require followloop/laravel-repositories来安装此包。
  2. 将提供者添加到config/app.php文件中:LOOP\LaravelRepositories\LaravelRepositoriesServiceProvider::class
  3. 通过运行php artisan vendor:publish --provider="LOOP\LaravelRepositories\LaravelRepositoriesServiceProvider"发布配置文件。
  4. 打开配置文件(config/repositories.php),并根据您的需求配置路径。
  5. 准备就绪!

用法

要开始使用此包,您只需要创建一个文件夹,在该文件夹中放置所有您的仓库接口和仓库实现,并从包提供的EloquentRepository类扩展每个仓库。

您创建的仓库要处理的Eloquent模型也应通过Repo构造函数注入。

然后,该包将尝试加载所有仓库接口,并根据config/repositories.php文件中指定的参数将它们绑定到仓库实现。

示例

注意:虽然该包包含一个生成器,但请仔细阅读所有文档,因为一些事情可能看起来很“神奇”。

让我们假设我们将在名为"MyWebApp"的文件夹(位于"app"文件夹内的"MyWebApp"文件夹中)的"Repositories"文件夹中拥有所有我们的仓库:app/MyWebApp/Repositories。

在此文件夹的根目录中,我们将有所有我们的接口,遵循以下命名约定:[RepositoryName]Interface.php

注意:只要我们使用“接口”作为后缀名,我们使用的名字并不重要。这很重要,因为自动绑定器将尝试找到所有匹配此模式的文件。

在这个存储库文件夹内部,我们必须有一个名为Eloquent的另一个文件夹,它将包含我们所有存储库的实现,遵循以下命名规范:[RepositoryName].php

我们的结构应该是这样的

+-- app
|   +-- MyApp
|       +-- Repositories
|           +-- UsersRepositoryInterface.php
|           +-- RolesRepositoryInterface.php
|           +-- CommentsRepositoryInterface.php
|           +-- PostsRepositoryInterface.php
|           +-- Eloquent
|               +-- UsersRepository.php
|               +-- RolesRepository.php
|               +-- CommentsRepository.php
|               +-- PostsRepository.php  

让我们看看UsersRepositoryInterface.phpUsersRepository.php会有什么。

<?php

namespace MyApp\Repositories;

use LOOP\LaravelRepositories\RepositoryInterface;

interface UsersRepositoryInterface extends RepositoryInterface {

    // here you would write the contract for methods that your repository will implement.
}
<?php

namespace MyApp\Repositories\Eloquent;

use LOOP\LaravelRepositories\src\EloquentRepository;
use MyApp\Repositories\UsersRepositoryInterface;
use MyApp\Models\User;

class UsersRepositoryInterface extends EloquentRepository implements UsersRepositoryInterface  {

    public function __construct( User $user ) {
        parent::__construct( $user );
    }
    
    // methods that your repository should implement...
}

现在我们需要配置config/repositories.php文件以匹配我们的路径和命名空间

    'path' => app_path('MyApp/Repositories'),
    
    'namespace' => 'MyApp\Repositories',

    'implementation' => 'Eloquent',

现在存储库已经准备好可以使用,并可以在其他服务或控制器中注入

<?php

namespace MyApp\Services\Users;

use MyApp\Repositories\UsersRepositoryInterface;

class UsersService implements UsersServicesInterface  {

    protected $usersRepository;

    public function __construct( UsersRepositoryInterface $usersRepositoryInterface ) {
        $this->usersRepository = $usersRepositoryInterface;
    }
   
    // other methods in your service.
}

注意:此示例假设您已配置了您的composer.json以自动加载app/MyApp中的文件,并使用MyApp命名空间。

默认提供的方法。

存储库包默认提供了一系列方法。这些是

    /**
     * Finds one item by the provided field.
     *
     * @param $value mixed Value used for the filter. If NULL passed then it will take ONLY the criteria.
     * @param string $field Field on the database that you will filter by. Default: id.
     * @param array $columns Columns to retrieve with the object.
     * @return mixed Model|NULL An Eloquent object when there is a result, NULL when there are no matches.
     */
    public function findOneBy( $value = NULL, $field = 'id', array $columns = ['*'] );

    /**
     * Finds ALL items the repository abstract without any kind of filter.
     *
     * @param array $columns Columns to retrieve with the objects.
     * @return mixed Collection Laravel Eloquent's Collection that may or may not be empty.
     */
    public function findAll( array $columns = ['*'] );

    /**
     * Finds ALL items by the provided field. If NULL specified for the first 2 parameters, then it will take ONLY the
     * criteria.
     *
     * @param $value mixed Value used for the filter.
     * @param string $field Field on the database that you will filter by. Default: id.
     * @param array $columns Columns to retrieve with the objects.
     * @return mixed Collection Laravel Eloquent's Collection that may or may not be empty.
     */
    public function findAllBy( $value = NULL, $field = NULL, array $columns = ['*'] );

    /**
     * Finds ALL the items in the repository where the given field is inside the given values.
     *
     * @param array $value mixed Array of values used for the filter.
     * @param string $field Field on the database that you will filter by.
     * @param array $columns Columns to retrieve with the objects.
     * @return mixed Collection Laravel Eloquent's Collection that may or may not be empty.
     */
    public function findAllWhereIn( array $value, $field,  array $columns = ['*'] );

    /**
     * Allows you to eager-load entity relationships when retrieving entities, either with or without criterias.
     *
     * @param array|string $relations Relations to eager-load along with the entities.
     * @return mixed The current repository object instance.
     */
    public function with( $relations );

    /**
     * Adds a criteria to the query.
     * 
     * @param CriteriaInterface $criteria Object that declares and implements the criteria used.
     * @return mixed The current repository object instance.
     */
    public function addCriteria( CriteriaInterface $criteria );

    /**
     * Skips the current criteria (all of them). Useful when you don't want to reset the object but just not use the
     * filters applied so far.
     *
     * @param bool|TRUE $status If you want to skip the criteria or not.
     * @return mixed The current repository object instance.
     */
    public function skipCriteria( $status = TRUE );

    /**
     * Returns a Paginator that based on the criteria or filters given.
     *
     * @param int $perPage Number of results to return per page.
     * @param array $columns Columns to retrieve with the objects.
     * @return Paginator object with the results and the paginator.
     */
    public function paginate( $perPage, array $columns = ['*'] );

    /**
     * Allows you to set the current page with using the paginator. Useful when you want to overwrite the $_GET['page']
     * parameter and retrieve a specific page directly without using HTTP.
     *
     * @param int $page The page you want to retrieve.
     * @return mixed The current repository object instance.
     */
    public function setCurrentPage( $page );

    /**
     * Creates a new entity of the entity type the repository handles, given certain data.
     *
     * @param array $data Data the entity will have.
     * @return mixed Model|NULL An Eloquent object when the entity was created, NULL in case of error.
     */
    public function create( array $data );

    /**
     * Updates as many entities as the filter matches with the given $data.
     *
     * @param array $data Fields & new values to be updated on the entity/entities.
     * @param $value mixed Value used for the filter.
     * @param string $field Field on the database that you will filter by. Default: id.
     * @return mixed Model|NULL An Eloquent object representing the updated entity, NULL in case of error.
     */
    public function updateBy( array $data, $value = NULL, $field = 'id' );

    /**
     * Removes as many entities as the filter matches. If softdelete is applied, then they will be soft-deleted.
     *
     * @param $value
     * @param $value mixed Value used for the filter.
     * @param string $field Field on the database that you will filter by. Default: id.
     * @return boolean TRUE It will always return TRUE.
     */
    public function delete( $value, $field = 'id' );

    /**
     * @return int number of records matching the criteria (or total amount of records).
     */
    public function count();

    /**
     * Resets the current scope of the repository. That is: clean the criteria, and all other properties that could have
     * been modified, like current page, etc.
     *
     * @return mixed The current repository object instance.
     */
    public function resetScope();
    
    /**
     * Permanently removes a record (or set of records) from the database.
     *
     * @param $value mixed Value used for the filter.
     * @param string $field Field on the database that you will filter by.
     * @return mixed
     */
    public function destroy( $value, $field = 'id' );

标准

为了避免我们的存储库充满了像findActiveUsers()findActiveUsersOlderThan( $date )等方法,我们将使用标准来应用搜索或查询的过滤器。

标准只是一个实现了由本包提供的CriteriaInterface的PHP对象,它操作Eloquent模型来应用我们想要应用的具体搜索的查询或查询集。

要创建自己的标准,请将它们放在任何您想要的位置,并使它们实现提供的CriteriaInterface。

例如:想象我们有一个应用程序,用户可以通过Facebook、Twitter或电子邮件注册。我们需要根据用户注册的方法检索所有用户。我们将有一个像这样的标准

<?php

namespace MyApp\Repositories\Eloquent\Criteria\Users;

use LOOP\LaravelRepositories\Criteria\CriteriaInterface;
use MyApp\Models\User;

class RegisteredVia implements CriteriaInterface  {

    protected $registeredVia;
    protected $onlyActive;

    public function __construct( $registeredVia, $onlyActive = TRUE ) {
    
        $this->registeredVia = $registeredVia;
        $this->onlyActive = $onlyActive;
        
    }
    
    
    public function apply( $queryBuilder ) {
    
        if ( $this->onlyActive ) $queryBuilder = $queryBuilder->where( 'active', TRUE );
        
        return $queryBuilder->where( 'registered_via', $this->registered_via );
        
    }
   
}

现在在您的服务或控制器中,您可以使用这个标准像这样

    $registeredViaFacebookCriteria = new RegisteredVia( 'facebook' );
    
    return $this->usersRepository->addCriteria( $registeredViaFacebookCriteria )->findAllBy();

我们甚至可以链式使用不同的标准

    $registeredViaFacebookCriteria = new RegisteredVia( 'facebook' );
    $orderByCreationDate = new OrderBy( 'created_at', 'ASC' );
    
    return $this->usersRepository
                ->addCriteria( $registeredViaFacebookCriteria )
                ->addCriteria( $orderByCreationDate )
                ->findAllBy();

默认提供的标准

  • OrderBy( $orderBy, $direction = 'ASC' ):根据给定的列和方向对结果进行排序。
  • FilterByColumns( array $filter ):通过多个列进行过滤(AND过滤)。应用过滤器可以很复杂。例如
    $maxDate = Carbon::now()->subDays( 7 );
    $filter = [
        [ 'balance', '>=', 10 ],
        [ 'created_at', '<=', $date ],
        [ 'is_premium_user', TRUE ]
    ];
    
    $filterCriteria = new FilterByColumns( $filter );
    
    // This will return all premium users created more than 1 week ago and that have more than 10 (€,$, whatever) of balance.
    $users = $this->usersRepository->addCriteria( $filterCriteria )->findAllBy();

对于更复杂的查询,必须创建自定义标准。

现在让我们直接跳到一些有趣的内容,这将在您的末端节省一些时间...

生成器!

是的,创建所有这些文件,将它们链接起来,注入模型等等,真的很烦人。对我也是。所以我创建了一个生成器,它为我们的模型创建接口和Eloquent实现(基本)。酷,不是吗?

提供了2个生成器:用于存储库和用于标准

存储库

只需执行php artisan make:repository ModelName

ModelName是要为它生成存储库的模型(类)的名称。这将为此存储库生成一个接口,一个实现该接口的eloquent实现,并将模型注入到存储库中。文件将放置在您在repositories.php配置文件中配置的目录中,并且也将有在那里指定的命名空间。

标准

执行php artisan make:criteria CriteriaClassName --folder=

这将根据您在repositories.php配置文件中配置的参数生成一个标准。另外,如果您想在特定的文件夹(已配置的文件夹内)中放置这个标准,可以在命令中指定这个选项:php artisan make:criteria FilterByCreationDate --folder=Accounts

尝试一下,看看它们在传递不同参数时的表现!

生成器可以做很多魔法,但它们在错误描述上也很有帮助,所以如果失败了,请注意错误;D。

特性

包含了一些函数,这些函数不一定需要包含在所有仓库中,但它们依赖于注入的模型。这些是针对涉及的不同类型实体可用的特性

仓库

  • 可恢复的:如果您在模型中使用软删除并且希望可以通过仓库恢复它们,只需在您的仓库中包含此特性。您还必须在您的仓库和仓库接口中实现RestorableInterface
  • 可计数的:如果您有任何用作计数器的列,可以使用此特性为您的仓库添加计数功能(在给定数量中增加/减少值)。您还必须在您的仓库和仓库接口中实现CountableInterface

模型

  • 使用UUID作为ID:有时我们可能希望使用UUID而不是自增ID。要做到这一点,只需在您的模型中使用UuidAsId特性。您必须将使用此特性的模型的public $incrementing属性设置为FALSE

变更日志

--尚未发布任何版本--

致谢

错误和贡献

  • 发现了错误?这是好事也是坏事。请通过GitHub上的问题让我知道。
  • 需要功能或者有有趣的内容想要贡献?太好了!打开一个pull request。

许可证

??