iqbalatma/laravel-service-repo

此包最新版本(3.4.2)没有可用的许可证信息。

基于 Laravel 扩展基类的服务仓库模式项目


README

Laravel Service Repository 是一个 Laravel 包,用于实现服务仓库模式。服务仓库模式帮助您分离关注点并将逻辑移动到服务类中。您可以从服务类中查询所有数据,而不是直接使用模型。这将帮助您避免冗余查询并使其更容易维护。

即将推出

  • 自定义过滤和排序的根配置
  • 发布服务占位符以进行自定义
  • 测试
  • 在仓库扩展中使用预定义方法的类型提示

如何安装

使用 Composer 安装 Laravel Service Repository 包

composer require iqbalatma/laravel-service-repo

概念

以下是服务仓库的概念,您可以在下面的图中看到。控制器将仅处理 HTTP 请求和 HTTP 响应。这将使您的控制器更瘦。所有来自请求的数据都将由验证类进行验证。所有验证规则都定义在这里。在所有数据验证完毕后,数据将被传递到服务中。在服务中,所有业务逻辑都工作。每个需要数据或从数据库中修改数据的业务逻辑都将通过仓库进行通信。这个仓库已经有了一些预定义的查询,但您可以自由地根据需要自定义预定义查询。

Screenshot 2023-09-28 at 00 55 06

服务

服务用于您的业务逻辑。您可以从控制器或另一个服务中调用服务。此包有基础服务,您可以扩展此服务。您还可以使用 artisan 命令生成服务。

php artisan make:service UserService

此命令将在 App/Services 目录中为您生成服务。

仓库

使用仓库通过模型查询您的数据库。因此,您不需要两次写相同的查询。您可以通过 artisan 命令在 App/Repositories 目录中生成仓库。

php artisan make:repository UserRepository

此命令将在 App/Repositories 目录中为您生成仓库。以下是一个 UserRepository 的示例。

<?php

namespace App\Repositories;
use Iqbalatma\LaravelServiceRepo\BaseRepository;
use Illuminate\Database\Eloquent\Builder;
use App\Models\User;

class UserRepository extends BaseRepository
{

     /**
     * use to set base query builder
     * @return Builder
     */
    public function getBaseQuery(): Builder
    {
        return User::query();
    }

    /**
     * use this to add custom query on filterColumn method
     * @return void
     */
    public function applyAdditionalFilterParams(): void
    {
    }
}

如何从服务调用仓库

您可以通过静态或非静态方法调用仓库上的方法。

注意

以下是通过静态方法调用仓库的示例

<?php

namespace App\Services\Management;

use Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;
use Illuminate\Pagination\LengthAwarePaginator;

class UserService extends BaseService
{

    public function getAllDataPaginated():LengthAwarePaginator
    {
        return UserRepository::getAllDataPaginated();
    }
}

注意

以下是通过非静态方法调用仓库的示例

<?php

namespace App\Services\Management;

use Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;
use Illuminate\Pagination\LengthAwarePaginator;

class UserService extends BaseService
{

    public function getAllDataPaginated():LengthAwarePaginator
    {
        return (new UserRepository())->getAllDataPaginated();
    }
}

预定义方法查询

您可以从服务中调用预定义方法。以下是预定义方法查询及其用例的列表。

<?php
function getAllDataPaginated(array $whereClause = [], array $columns = ["*"]);
function getAllData(array $whereClause = [], array $columns = ["*"]);
function getDataById(string|int|array $id, array $columns = ["*"]);
function getSingleData(array $whereClause = [], array $columns = ["*"]);
function addNewData(array $requestedData);
function updateDataById(string|int $id, array $requestedData, array $columns = ["*"], bool $isReturnObject = true);
function updateDataByWhereClause(array $whereClause, array $requestedData, array $columns = ["*"], bool $isReturnObject = false);
function deleteDataById(string|int $id);
function deleteDataByWhereClause(array $whereClause);

您还可以在预定义方法查询之前链式调用方法。

<?php
function with(array|string $relations);
function without(array|string $relations);
function withAvg(array|string $relation, string $column);
function withCount(mixed $relations);
function withMin(array|string $relation, string $column);
function withMax(array|string $relation, string $column)
function withSum(array|string $relation, string $column);
function has(Relation|string $relation, string $operator = '>=', int $count = 1, string $boolean = 'and', Closure|null $callback = null);
function whereHas(string $relation, Closure|null $callback = null, string $operator = '>=', int $count = 1);

and other method ....

注意

以下是使用链式方法进行额外查询时使用预定义方法的示例

<?php

namespace App\Services\Management;

use Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;
use Illuminate\Pagination\LengthAwarePaginator;

class UserService extends BaseService
{

    public function getAllDataPaginated():LengthAwarePaginator
    {
        return UserRepository::with('profile')->getAllDataPaginated();
    }
}

如何添加预定义方法

您也可以在仓库中创建您自己的方法。例如,您可能想要创建一个将在多个地方执行的查询,并希望避免冗余代码。

重要

使用 query 前缀创建预定义方法查询。这样,您就可以在不使用查询前缀的情况下静态调用它。例如:UserRepository::getAllActiveUser()

<?php

namespace App\Repositories;
use Iqbalatma\LaravelServiceRepo\BaseRepository;
use Illuminate\Database\Eloquent\Builder;
use App\Models\User;

class UserRepository extends BaseRepository
{

     /**
     * use to set base query builder
     * @return Builder
     */
    public function getBaseQuery(): Builder
    {
        return User::query();
    }

    /**
     * use this to add custom query on filterColumn method
     * @return void
     */
    public function applyAdditionalFilterParams(): void
    {
    }

    public function queryGetAllActiveUser(){
          return $this->builder->where('status', 'active')->get();
    }
}

如何从仓库调用模型作用域

有时您想创建模型局部作用域,但仍想使用仓库来调用它。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;


class Tag extends Model
{
    use HasFactory;

    protected $fillable = [
        "name"
    ];

    public function scopeActive(Builder $query){
        $query->where('status', '=', 'active');
    }    
}

您可以调用此作用域 TagRepository::active()->getAllDataPaginated();

如何使用 orderColumn 方法

排序列是预定义方法,它将使用 HTTP 请求中的查询参数来排序请求数据。在使用 orderColumn 之前,您需要定义允许排序的列。您可以在模型内部定义这些排序列。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;


class User extends Model
{
    use HasFactory;

    public array $orderableColumns = [
        "name" => "users.name",
        "address" => "users.address",
    ];
}

如你所见,属性 $orderableColumns 是一个键值对数组。该属性的键用于从 HTTP 请求中获取键。你可以自定义这个键为任何你想要的值。该属性的值映射到数据库表中的列名。

要使用这个排序,你只需要在调用存储库时调用 orderColumn 方法。

<?php

namespace App\Services\Management;

Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;

class UserService extends BaseService
{
    public function getAllDataPaginated(){
        return UserRepository::orderColumn()->getAllDataPaginated();
    }
}

在其他情况下,你还可以传递参数给 orderColumn 方法来覆盖可排序列。

<?php

namespace App\Services\Management;

Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;

class UserService extends BaseService
{
    public function getAllDataPaginated(){
        return UserRepository::orderColumn([
            "name" => "users.name",
            "address" => "users.address",
        ])->getAllDataPaginated();
    }
}

如何使用 filterColumn 方法

过滤列是预定义的方法,通过查询参数从 HTTP 请求中过滤数据。在使用 filterColumn 方法之前,你需要定义允许过滤的列列表。

重要

所有的过滤查询都使用 '=' 操作符,但你也可以根据需要定义自定义操作符。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;


class User extends Model
{
    use HasFactory;

    public array $filterableColumns = [
        "name" => "users.name",
        "address" => "users.address",
    ];
}

要使用过滤列,你只需要调用并链式调用此方法。你也可以将其与像 orderColumn 这样的其他方法链式调用。

<?php

namespace App\Services\Management;

Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;

class UserService extends BaseService
{
    public function getAllDataPaginated(){
        return UserRepository::orderColumn()->filterColumn()->getAllDataPaginated();
    }
}

在其他情况下,你还可以传递参数给 filterColumn 方法来覆盖可过滤列。

<?php

namespace App\Services\Management;

Iqbalatma\LaravelServiceRepo\BaseService;
use App\Repositories\UserRepository;

class UserService extends BaseService
{
    public function getAllDataPaginated(){
        return UserRepository::orderColumn()->filterColumn([
            "name" => "users.name",
            "address" => "users.address",
        ])->getAllDataPaginated();
    }
}

注意

你也可以在关联上过滤列。但需要在模型上定义关联和可过滤列。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use App\Models\Profile;

class User extends Model
{
    use HasFactory;

    public array $filterableColumns = [
        "name" => "users.name",
        "address" => "users.address",
    ];

    public array $relationFilterableColumns = [
        "profile" => [
            "phone" => "profiles.pone"
        ]
    ];

    public function profile(){
        return $this->belongsTo(Profile::class);
    }   
}

当你想要过滤模型的关联时,你需要确保关联已经定义。然后,你需要创建 $relationFilterableColumns 属性。该属性的键是关联名称,值是可过滤列,就像 $filterableColumns 属性。

注意

你还可以通过传递数组形式的可过滤列到 filterColumn 的第二个参数来覆盖关联可过滤列。例如:filterColumn([], ["profile" => ["phone" => "profiles.pone"]])

如何自定义 filterColumn 上的操作符

你只需要定义一个包含列和操作符的数组。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;


class User extends Model
{
    use HasFactory;

    public array $filterableColumns = [
        "name" => [
            "column" => "users.name"
            "operator" => "like"
        ],
        "address" => "users.address",
    ];
}