salehhashemi/laravel-repository

为 Laravel 项目实现仓库模式。

0.5.2 2024-05-18 07:15 UTC

This package is auto-updated.

Last update: 2024-09-18 07:57:18 UTC


README

Laravel 仓库模式

Latest Version on Packagist Total Downloads GitHub Actions GitHub Actions codecov PHPStan

这个 Laravel 扩展包通过实现仓库模式简化并优化了数据访问。它提供了一个强大的抽象层来处理数据库交互,并增加了灵活的筛选、搜索和基于条件的查询。

Header Image

特性

  • 仓库抽象:将数据访问逻辑与应用代码解耦。
  • 动态筛选:应用筛选器以细化数据库查询。
  • 基于条件的筛选:定义可重用的条件以应用于常见的查询约束。
  • 搜索功能:根据用户输入实现自定义搜索逻辑。
  • 模块化仓库接口:确保一致性和可维护性。
  • 易于集成:无缝集成到 Laravel 应用程序中。
  • 开发工具:使用 make:repository 命令自动创建仓库、接口和筛选器。

安装

要安装此包,您可以运行以下命令

composer require salehhashemi/laravel-repository

开发工具

该包包含一个有用的命令,可以加速您的开发工作流程

  • make:repository 此命令将自动为指定的模型创建以下组件

    • 仓库类
    • 仓库接口
    • 筛选器类

使用示例

php artisan make:repository User 

php artisan make:repository App\Models\User 

示例用法

使用此包管理和管理 Post 模型的实际示例。

它包括自定义仓库、筛选器、条件和控制器使用的示例。

Post 仓库

PostRepository 类扩展了 BaseEloquentRepository,并展示了如何为 Post 模型使用此包的功能。

<?php

namespace App\Repositories;

use App\Filters\PostFilter;
use App\Models\Post;
use App\Repositories\Criteria\FeaturedPostCriteria;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Salehhashemi\Repository\BaseEloquentRepository;
use Salehhashemi\Repository\Traits\Searchable;

/**
 * @method \App\Models\Post getModel()
 * @method \App\Models\Post|null findOne(int|string $primaryKey = null)
 * @method \App\Models\Post findOneOrFail(int|string $primaryKey = null)
 */
class PostRepository extends BaseEloquentRepository implements PostRepositoryInterface
{
    use Searchable;

    protected function getFilterManager(): PostFilter
    {
        $filterManager = new PostFilter();
        $filterManager->setQuery($this->getQuery());

        return $filterManager;
    }

    public function findAllFeatured(): EloquentCollection
    {
        $this->addCriteria(new FeaturedPostCriteria());

        return $this->findAll(['limit' => 20]);
    }

    public function searchVisible(array $queryParams, int $perPage): Paginator
    {
        $this->orderBy('sort');
        $this->withCategories();

        return $this->search($queryParams, $perPage);
    }

    public function findOnePublishedOrFail(int $postId): Post
    {
        $this->applyConditions([
            'is_published' => 1,
        ]);

        return $this->findOneOrFail($postId);
    }

    public function withComments(): static
    {
        return $this->with(['comments']);
    }

    public function withCategories(): static
    {
        return $this->with(['categories']);
    }

    /**
     * {@inheritdoc}
     */
    protected function getModelClass(): string
    {
        return Post::class;
    }
}

Post 仓库接口

PostRepositoryInterface 定义了我们的仓库合约,确保方法签名的一致性和清晰性。

<?php
declare(strict_types=1);

namespace App\Repositories;

use App\Models\Post;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Salehhashemi\Repository\Contracts\RepositoryInterface;
use Salehhashemi\Repository\Contracts\SearchableRepositoryInterface;

/**
 * @method \App\Models\Post|null findOne(int|string $primaryKey = null)
 * @method \App\Models\Post findOneOrFail(int|string $primaryKey = null)
 */
interface PostRepositoryInterface extends RepositoryInterface, SearchableRepositoryInterface
{
    public function findAllFeatured(): EloquentCollection;

    public function searchVisible(array $queryParams, int $perPage): Paginator;

    public function findOnePublishedOrFail(int $postId): Post;

    /**
     * @return $this
     */
    public function withComments(): static;

    /**
     * @return $this
     */
    public function withCategories(): static;
}

Post 筛选器

PostFilter 展示了如何将自定义筛选器应用于 Post 模型,增强查询的灵活性。

<?php
declare(strict_types=1);

namespace App\Filters;

use App\Models\Post;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use Salehhashemi\Repository\BaseFilter;

class PostFilter extends BaseFilter
{
    public function applyFilter(array $queryParams): QueryBuilder
    {
        $this
            ->whereLike('title', $queryParams['title'] ?? '', self::WILD_BOTH)
            ->whereValue('status', $queryParams['status'] ?? '')
            ->compare('amount', '<=' $queryParams['max_amount'] ?? '')
            ->dateFrom('created_at', $queryParams['created_from'] ?? '')
            ->dateTo('created_at', $queryParams['created_to'] ?? '');

        if (! empty($queryParams['category_id'])) {
            $this->getQuery()->whereHas('categories', function ($query) use ($queryParams) {
                $query->where('categories.id', $queryParams['category_id']);
            });
        }

        return $this->getQuery();
    }
    
    protected function getModelClass(): string
    {
        return Post::class;
    }
}

特色 Post 条件

FeaturedPostCriteria 是定义筛选条件的示例,这里专注于特色帖子。

<?php

namespace App\Repositories\Criteria;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Salehhashemi\Repository\Contracts\CriteriaInterface;

class FeaturedPostCriteria implements CriteriaInterface
{
    /**
     * {@inheritDoc}
     */
    public function apply(Model $model, Builder $query): Builder
    {
        $query->where([
            'posts.is_featured' => 1,
        ]);

        return $query;
    }
}

将接口绑定到实现

将仓库接口与其具体实现绑定至关重要。这允许 Laravel 服务容器在需要的地方自动解析和注入适当的仓库实例。

  1. 创建服务提供者

    php artisan make:provider RepositoryServiceProvider
  2. 在服务提供者中绑定

    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\ServiceProvider;
    use App\Repositories\PostRepositoryInterface;
    use App\Repositories\PostRepository;
    
    class RepositoryServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->bind(
                PostRepositoryInterface::class,
                PostRepository::class
            );
        }
    }    
  3. 注册服务提供者:将您的 RepositoryServiceProvider 添加到 config/app.php 中的 providers 数组。

    'providers' => [
        // Other Service Providers
    
        App\Providers\RepositoryServiceProvider::class,
    ],

控制器中的仓库使用

此示例展示了如何在控制器中使用 PostRepository,并将其无缝集成到 Laravel 应用程序的工作流程中。

public function index(Request $request, PostRepositoryInterface $postRepository): AnonymousResourceCollection
{
    return PostCollectionResource::collection(
        $postRepository
            ->search(
                $request->query(),
                $request->integer('limit'),
            )
    );
}

可用方法

此包提供了一些方法

findOne

通过主键获取单个模型实例,或获取符合应用条件和关系的第一个模型。

$model = $repository->findOne($primaryKey);

findOneOrFail

类似于 findOne,但如果找不到结果,则抛出 ModelNotFoundException

$model = $repository->findOneOrFail($primaryKey);

findAll

检索符合应用条件、关系和指定选项的所有模型集合。

$models = $repository->findAll(['limit' => 10, 'offset' => 5]);

findList

获取按指定字段键控的模型值集合,对于下拉列表或类似显示很有用。

$list = $repository->findList('id', 'name');

paginate

基于应用条件、关系和指定页面大小返回模型的分页集合。

$paginatedModels = $repository->paginate(15);

addCriteria

将新的条件实例添加到仓库中进行查询筛选。

$repository->addCriteria(new YourCustomCriteria());

orderBy

根据字段和方向指定查询结果排序。

$repository->orderBy('created_at', 'DESC');

lockForUpdate

将对查询应用“锁定更新”子句以处理并发数据库访问。

$repository->lockForUpdate();

sharedLock

将对查询应用共享锁,防止修改所选行。

$repository->sharedLock();

配置

要发布配置文件,请运行以下命令

php artisan vendor:publish --provider="Salehhashemi\Repository\RepositoryServiceProvider" --tag="config"

发布后,请确保清除配置缓存以应用您的更改

php artisan config:clear

然后,您可以在 config/repository.php 中调整分页限制

异常

  • InvalidArgumentException 如果页面大小无效则抛出。
  • ModelNotFoundException 如果找不到模型则抛出。

Docker 设置

此项目使用 Docker 进行本地开发和测试。在继续之前,请确保您的系统已安装 Docker 和 Docker Compose。

构建 Docker 镜像

docker-compose build

启动服务

docker-compose up -d

要访问 PHP 容器,您可以使用

docker-compose exec php bash

测试

composer test

变更日志

有关最近更改的更多信息,请参阅CHANGELOG

贡献

有关详细信息,请参阅CONTRIBUTING

致谢

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅许可证文件