railken/eloquent-mapper


README

Actions Status

一个Laravel包,利用关系的全部力量来创建自动连接并执行高级筛选。

例如,对于两个模型 OfficeEmployee,你可以将如下字符串转换为这样的SQL查询:

select offices.* 
from `offices` 
left join `employees` as `employees` on `employees`.`office_id` = `offices`.`id`
where (`employees`.`name` like ? or `employees`.`name` like ?)

功能

需求

PHP 8.1和laravel 8

安装

你可以通过以下命令使用Composer安装它:

composer require railken/eloquent-mapper

使用方法

为了使用这个库,你需要一个映射。

在你想创建的地方创建一个新类,如下面的示例所示

app/Map.php

namespace App;

use Railken\EloquentMapper\Map as BaseMap;

class Map extends BaseMap
{
    /**
     * Return an array of all models you want to map
     *
     * @return array
     */
    public function models(): array
    {
        /** return [
            \App\Models\User::class
        ]; **/
    }
}

方法 models 必须返回所有模型列表。你甚至可以添加位于你的vendor文件夹中的模型,无论你使用什么逻辑,你只需返回一个数组。

Railken\EloquentMapper\Map 还基于模型提供关系的映射和属性映射,如果你希望,你可以覆盖该功能并编写自己的。 查看源代码

这些方法仅在调用 artisan mapper:generate 命令(见下文)时调用,并将结果缓存到位于 bootstrap/cache/map.php 的文件中。

这意味着你可以执行任何逻辑来检索所有模型(例如扫描文件),因此请放心缓存。

重要:为了被检测到,所有关系都必须返回类型 Illuminate\Database\Eloquent\Relations\Relation,如下所示

namespace App;

use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Foo extends Model
{   
    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function bar(): BelongsTo
    {
        return $this->belongsTo(Bar::class);
    }
}

现在是将这个类注册到任何提供者中以覆盖默认类的时候了。

app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Map;
use Railken\EloquentMapper\Contracts\Map as MapContract;

class AppServiceProvider extends ServiceProvider
{
    /**
     * @inherit
     */
    public function register()
    {
        $this->app->bind(MapContract::class, Map::class);
    }
}

Artisan

只有一个命令,就是 artisan mapper:generate。这个命令将重新映射并重新缓存,所以请记住,每次你更改代码模型时都必须执行它。

如果你使用位于你的vendor文件夹中的模型,你可以在 composer.json 中添加以下内容,以便在库更新时重新加载。

{
   "scripts": {
        "post-autoload-dump": [
            "@php artisan mapper:generate"
        ]
    }
}

筛选

看看筛选是如何实际工作的?

use Railken\EloquentMapper\Scopes\FilterScope;
use Railken\EloquentMapper\With\WithCollection;
use Railken\EloquentMapper\With\WithItem;
use App\Models\Foo;

$foo = new Foo;
$query = $foo->newQuery();
$filter = "created_at >= 2019";

$scope = new FilterScope;
$scope->apply($query, $filter, new WithCollection([
    new WithItem('bar')
]));

就是这样! $query 现在已经被筛选,如果 Foo 有任何关系,你可以使用点表示法,筛选将自动执行连接。例如,如果 Foo 有一个名为 tags 的关系,并且你想检索所有标签名为 myCustomTagFoo,只需使用 tag.name = 'myCustomTag'

这里是 完整语法

第三个参数是预加载选项。你当然可以使用点表示法,并添加子查询。例如,以下示例表示一个包含名称 Mario 的所有作者列表,并返回所有具有名为 tag.namesci-fi 的书籍。

use Railken\EloquentMapper\Scopes\FilterScope;
use Railken\EloquentMapper\With\WithCollection;
use Railken\EloquentMapper\With\WithItem;
use Railken\EloquentMapper\Tests\Models\Author;

$author = new Author;
$query = $author->newQuery();
$filter = "name ct 'Mario'";
$scope = new FilterScope;

$scope->apply($query, $filter, new WithCollection([
    new WithItem('books', 'tag.name eq "sci-fi"')
]));

连接器

这是一个内部类,由FilterScope在执行过滤前连接必要的关联关系使用,但您也可以独立使用它。查看测试

示例 - 设置

让我们用一个真实例子来继续,首先是设置。我们将使用两个模型:OfficeEmployee

app/Models/Office.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Office extends Model
{   
    /** 
     * @var array
     */
    public $fillable = [
        'name',
        'description'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function employees(): HasMany
    {
        return $this->hasMany(Employee::class);
    }
}

app/Models/Employee.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Models\Office;

class Employee extends Model
{   
    /**
     * @var array
     */
    public $fillable = [
        'name',
        'description',
        'office_id'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function office(): BelongsTo
    {
        return $this->belongsTo(Office::class);
    }
}

app/Map.php

namespace App;

use Railken\EloquentMapper\Map as BaseMap;

class Map extends BaseMap
{
    /**
     * Return an array of all models you want to map
     *
     * @return array
     */
    public function models(): array
    {
        return [
            \App\Models\Employee::class,
            \App\Models\Office::class
        ];
    }
}

示例 - 使用

检索所有有名为Mario RossiGiacomo的员工的办公室

use App\Models\Office;
use Railken\EloquentMapper\Scopes\FilterScope;

$office = new Office;

$query = $office->newQuery();
$filter = "employees.name ct 'Mario Rossi' or employees.name ct 'Giacomo'"

$scope = new FilterScope();
$scope->apply($query, $filter);

echo $query->toSql();

结果

select offices.* 
from `offices` 
left join `employees` as `employees` on `employees`.`office_id` = `offices`.`id`
where (`employees`.`name` like ? or `employees`.`name` like ?)