esslassi/metable

Laravel Eloquent 模型元数据

2.0.0 2024-05-24 23:30 UTC

This package is auto-updated.

Last update: 2024-09-25 14:22:23 UTC


README

Laravel Eloquent 模型元数据

Laravel Source License

安装

Composer

Laravel 可以安装在 laravel 9.x 或更高版本。

运行

composer require esslassi/metable

在 config/app.php 中注册包的服务提供者

'providers' => [
    ...
    Esslassi\Metable\MetableServiceProvider::class,
    ...
];

您应该使用以下命令发布迁移和 config/metable.php 配置文件:

php artisan vendor:publish --provider="Esslassi\Metable\MetableServiceProvider::class"

您可以在 config/metable.php 中自定义默认元数据表(默认:meta

return [

    /*
	 *
     * You can customize default table name by relacing 'meta' to your own
     *
     */

    'tables' => [

        // default table for all models

        'default' => 'meta'
        
    ]
];

在发布配置和迁移后,执行迁移命令以创建元数据表

php artisan migrate

配置

模型设置

将 Metable 特性添加到您需要可元化的模型中

use Esslassi\Metable\Metable;

class User extends Eloquent
{
    use Metable;
    ...
}

默认模型属性值

此外,您可以通过将一个名为 $defaultMetaValues 的数组分配给模型来指定默认值。这种配置有两个副作用

  1. 如果元数据属性不存在,将返回默认值而不是 null。
  2. 如果您将元数据属性设置为默认值,元数据表中的行将被删除,导致返回默认值,如规则 1 所述。

这对于大多数项目来说是期望的功能,但请注意,如果此功能不符合您的需求,您可能需要使用自己的自定义访问器和修改器重新实现默认功能。

此功能最适合记录规则异常的元数据条目。例如:用户不是管理员(默认值:不是管理员),因维护而关闭的节点(默认值:节点开启)等。这意味着表不需要为每个预期状态的数据存储数据,只需要存储处于异常状态的数据行,并允许在创建时具有默认状态,而无需添加写入代码。

   public $defaultMetaValues = [
      'is_admin' => false,
   ];

Metable 处理

设置内容元数据

设置现有属性或创建新数据的元值

  1. 流畅方式:您可以像使用常规 Eloquent 模型一样无缝地设置元值。
  2. Metable 检查该属性是否属于模型;如果不属于,它将访问元数据模型以追加或设置新的元值。
$user = new User;
$user->name = 'Esslassi Mohammed'; // model attribute
$user->country = 'Morocco'; // meta data attribute
$user->save(); // save model

您可以使用 setMeta 来设置或添加元值

$user = new User;
$user->name = 'Esslassi Mohammed';
$user->setMeta('country', 'Morocco');
$user->save();

设置多个元值

$user = new User;
$user->name = 'Esslassi Mohammed';
$user->setMeta([
    'country' => 'Morocco',
    'city' => 'Fez'
    ...
]);
$user->save();

使用模型列设置多个元值

$user = new User;
$user->setAttributes([
    'name' => 'Esslassi Mohammed',
    'country' => 'Morocco',
    'city' => 'Fez'
    ...
]);
$user->save();

使用标准 create 方法创建模型

如果模型属性上不存在属性,则将其添加为元值。

User::create([
    'name' => 'Esslassi Mohammed',
    'country' => 'Morocco',
    'city' => 'Fez'
    ...
]);

删除内容元数据

通常,如果您想删除元值,可以使用 unset 并保存模型

$user = User::find(1);
$user->name // model attribute
unset($user->country) // remove meta on save
$user->save();

使用 removeMeta

$user = User::find(1);
$user->removeMeta('country');
$user->save();

删除多个元值

$user = User::find(1);

$user->removeMeta(['country', 'city']);
OR
$user->removeMeta('country', 'city');

$user->save();

一次性删除所有元值

$user = User::find(1);
$user->deleteAllMeta();

注意:此功能将直接从数据库中删除所有元值,且不可回滚。

检查元数据

查看内容是否具有元数据

...
if (isset($user->country)) {
    ...
}
...

您可以通过 hasMeta 方法检查元数据是否存在

...
if ($user->hasMeta('country')){

}
...

您也可以检查模型是否具有多个元数据

//By providing an array
$user->hasMeta(['country', 'city']);
// By pipe spliter
$user->hasMeta('country|city');
// By comma spliter
$user->hasMeta('country,city');

注意:它仅在所有元数据都存在时返回 true。

如果您在第二个参数中提供 true,即使元数据标记为删除也会返回 true

$user = User::find(1);
$user->removeMeta('country');

$user->hasMeta('country', true); // Return true

$user->hasMeta('country'); // Return false
...

检索元数据

流畅方式,您可以像访问模型上的属性一样访问元数据。就像您在常规 eloquent 模型上所做的那样。

$user = User::find(1);
$user->name; // Return Eloquent value
$user->country; // Return meta value

您可以通过 getMeta 方法检索元值

$user = $user->getMeta('country');

或指定一个默认值,如果不存在

$user = $user->getMeta('country', 'Morocco');

注意:$defaultMetaValues 属性中设置的默认值优先于传递给此方法的任何默认值。

您还可以一次性检索多个元值,并作为集合接收它们。

// By comma
$user = $user->getMeta('country,city');
// By pipe
$user = $user->getMeta('country|city');
// By an array
$user = $user->getMeta(['country', 'city']);

禁用流畅访问

在某些情况下,您可能想禁用流畅访问或设置

protected $disableFluentMeta = true;

通过设置该属性,您将不再通过流畅方式设置或访问元值

$user = User::find(1);
$user->country = 'Morocco';// This will not set meta, this action will call setAttribute() of model class.
$user->country;// Here will not retrieve meta
unset($user->country);// No action will take
isset($user->country);// Will not check if meta exists

因此,设置元值的唯一方式是调用setMetaaddMeta方法

检索所有元值

要获取与某内容相关联的所有元值,请使用不带任何参数的getMeta

$user = User::find(1);
$metas = $user->getAllMeta();

元子句

元子句的位置

您可以使用元查询构建器的whereMeta方法向元查询添加where子句。对whereMeta方法的调用最基本需要三个参数:列名、操作符(可以是数据库支持的任何操作符)以及与列值进行比较的值。

例如,以下查询检索国家元键值等于'Morocco'的用户

$users = User::whereMeta('country', '=', 'Morocco')
    ->get();

为了方便,如果您想验证列是否等于给定值,可以将该值作为第二个参数传递给whereMeta方法。该包将假设您想使用=操作符

$users = User::whereMeta('country', 'Morocco')
    ->get();

或者,使用元子句

当对元查询构建器的whereMeta方法进行链式调用时,子句将使用AND操作符进行连接。但是,您可以使用orWhereMeta方法使用OR操作符将子句连接到元查询。orWhereMeta方法接受与whereMeta方法相同的参数

$users = User::whereMeta('country', 'Morocco')
    orWhereMeta('continent', 'Africa')
    ->get();

如果您需要将“或”条件分组在括号内,此包不支持此选项,请仅使用Laravel的where分组

$users = User::whereMeta('country', 'Morocco')
    ->orWhere(function (Builder $query) {
        $query->whereMeta('continent', 'Africa')
            ->whereMeta('competition', 'CAN');
    })
    ->get();

使用元子句非条件

可以使用whereMetaNotorWhereMetaNot方法来否定特定的查询约束。例如,以下查询排除了居住在摩洛哥的用户

$users = User::whereMetaNot('country', 'Morocco')
    ->get();

使用元子句非条件

whereMetaInorWhereMetaNotIn方法用于测试数组中是否存在元值

$users = User::whereMetaIn('country', ['Morocco', 'Algeria', 'Egypt', 'Tunisia'])
    ->get();

其他Where子句

whereMetaNull / orWhereMetaNull

whereMetaNull方法验证元值是否为null

$users = User::whereMetaNull('country')
    ->get();
whereMetaNotNull / orWhereMetaNotNull

whereMetaNotNull方法验证元值是否不为null

$users = User::whereMetaNotNull('country')
    ->get();
whereMetaHas / orWhereMetaHas

whereMetaHas方法验证元键是否存在

$users = User::whereMetaNotNull('country')
    ->get();
whereMetaDoesntHave / orWhereMetaDoesntHave

whereMetaDoesntHave方法验证元键是否存在

$users = User::whereMetaDoesntHave('country')
    ->get();
whereInMetaArray / whereNotInMetaArray(仅MySQL)

whereInMetaArray方法验证给定的值是否在元值中。以下示例检索元键值中包含'Morocco'的用户

$users = User::whereInMetaArray('countries', 'Morocco')
    ->get();

注意:此方法等同于Laravel的whereJsonContains,但由于值列的类型是长文本,我们无法与其一起使用,因此我们决定创建此方法来帮助我们解决这个问题。

定义关系

实际上,metable只有一个关系,因此本节的内容将非常简短。

一对一

想象一下,在我们的应用程序中有三个角色managerdriversupervisor,但只有一个角色必须拥有汽车是driver,并且不能将外键user_id设置到cars表中,因为一辆车可以由一个或多个驾驶员驾驶

users
    id - integer
    name - string
    role - string
 
cars
    id - integer
    model - string
    marque - string

因此,我们决定创建一个名为MetaOne的metable关系,其初始化方法为hasMetaOne

namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Esslassi\Metable\Relations\MetaOne;
 
class User extends Model
{
    /**
     * Get the car.
     */
    public function car(): MetaOne
    {
        return $this->hasMetaOne(Car::class, 'car_id');
    }
}

传递给hasMetaOne方法的第一个参数是我们希望访问的最终模型名称,而第二个参数是元键名称。

$user = User::find(1);
$car = $user->car;

键约定

基本上,metable会自动识别模型的本地键,但需要元键名称。因此不需要标识本地键。如果您想自定义关系的本地键,可以将它作为第三个参数传递给hasMetaOne方法。

class User extends Model
{
    /**
     * Get the car.
     */
    public function car(): MetaOne
    {
        return $this->hasMetaOne(
            Car::class,
            'car_id', // Meta key of the users meta...
            'id' // Local key on the users table...
        );
    }
}