esslassi / metable
Laravel Eloquent 模型元数据
Requires
- php: >=7.3
- ext-json: *
- illuminate/database: ^8.0|^9.0|^10.0|^11.0
- illuminate/events: ^8.0|^9.0|^10.0|^11.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0
Requires (Dev)
- phpunit/phpunit: ^9.5|^10.5
README
Laravel Eloquent 模型元数据
安装
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
的数组分配给模型来指定默认值。这种配置有两个副作用
- 如果元数据属性不存在,将返回默认值而不是 null。
- 如果您将元数据属性设置为默认值,元数据表中的行将被删除,导致返回默认值,如规则 1 所述。
这对于大多数项目来说是期望的功能,但请注意,如果此功能不符合您的需求,您可能需要使用自己的自定义访问器和修改器重新实现默认功能。
此功能最适合记录规则异常的元数据条目。例如:用户不是管理员(默认值:不是管理员),因维护而关闭的节点(默认值:节点开启)等。这意味着表不需要为每个预期状态的数据存储数据,只需要存储处于异常状态的数据行,并允许在创建时具有默认状态,而无需添加写入代码。
public $defaultMetaValues = [
'is_admin' => false,
];
Metable 处理
设置内容元数据
设置现有属性或创建新数据的元值
- 流畅方式:您可以像使用常规 Eloquent 模型一样无缝地设置元值。
- 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
因此,设置元值的唯一方式是调用setMeta
或addMeta
方法
检索所有元值
要获取与某内容相关联的所有元值,请使用不带任何参数的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();
使用元子句非条件
可以使用whereMetaNot
和orWhereMetaNot
方法来否定特定的查询约束。例如,以下查询排除了居住在摩洛哥的用户
$users = User::whereMetaNot('country', 'Morocco') ->get();
使用元子句非条件
whereMetaIn
和orWhereMetaNotIn
方法用于测试数组中是否存在元值
$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只有一个关系,因此本节的内容将非常简短。
一对一
想象一下,在我们的应用程序中有三个角色manager
、driver
和supervisor
,但只有一个角色必须拥有汽车是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... ); } }