greabock / populator
eloquent模型填充器
Requires
- illuminate/database: ^8.0|^9.0
- illuminate/support: ^8.0|^9.0
- ramsey/uuid: ^4.1.1
README
为Eloquent提供混合数据同步的数据证明
内容
想法
在通过API处理数据时,我们经常得到具有嵌套关系的实体,但在发送数据时,需要手动处理这些嵌套关系。这个同步器允许您无需考虑这些,从而极大地加速了开发。
特点
- 允许“原样”处理Eloquent模型的输入和输出 - 无需额外的操作
- 工作单元 - 或者将执行所有更改,或者(在出现错误的情况下)根本不执行更改
- 标识映射 - 确保相同类型和相同标识符的实体是唯一的
- uuid - 允许创建有效的实体并通过标识符相互关联,而无需访问数据库
限制
- 目前仅支持uuid
安装
composer require greabock/populator
使用
使用此工具非常简单
后端示例
<?php namespace App\Http\Controllers; use App\Post; use Exception; use Greabock\Populator\Populator; use Illuminate\Http\Request; class PostController { /** * @param $id * @param Request $request * @param Populator $populator * @return Post * @throws Exception */ public function put(Request $request, Populator $populator): Post { $post = Post::findOrNew($request->get('id')); $populator->populate($post, $request->all()); // здесь мы можем сделать что-то до того, как изменения отправятся в базу. $populator->flush(); return $post; } }
前端示例
(不要这样做 - 这只是一个示例)
import uuid from 'uuid/v4' class Post { constructor(data) { if(!data.id) { data.id = uuid() } Object.assign(this, data) } addTag (tag) { this.tags.push(tag) } addImage (image) { this.images.push(image) } } class Tag { constructor(data) { if(!data.id) { data.id = uuid() } Object.assign(this, data) } } let post, tags; // function loadTags () { fetch('tags') .then(response => response.json()) .then(tagsData => tags = data.map(tagdata => new Tag(tagdata))) } function loadPost (id) { fetch(`posts/${id}`) .then(response => response.json()) .then(data => post = new Post(data)) } function savePost(post) { fetch(`posts/${post.id}`, {method: 'PUT', body: JSON.stringify(post)}) .then(response => response.json()) .then(data => alert(`Post ${data.title} saved!`)) } loadTags() loadPost(1) // После того, как всё загружено: post.addTag(tags[0]) post.title = 'Hello World!' savePost(post)
输入特点
平面实体
让我们举一个简单的例子
{ "name": "Greabock", "email": "greabock@gmail.com", }
由于传入的数据中缺少字段id
(或模型中指定的其他字段),填充器将创建一个新的实体。然后,使用标准的fill
方法用传入的数据填充它。在这种情况下,将立即为模型生成id
。
带有标识符的示例
{ "id" : "123e4567-e89b-12d3-a456-426655440000", "name": "Greabock", "email": "greabock@gmail.com", }
在这个例子中,传递了id
- 因此填充器将尝试在数据库中找到这样的实体。但是,如果它无法在数据库中找到这样的记录,则它将创建一个新的具有传递的id
的实体。无论如何,填充器将填充该模型传递的email
和name
。在这种情况下,行为类似于User::findORNew($id)
。
HasOne
{ "id": "123e4567-e89b-12d3-a456-426655440000", "name": "Greabock", "email": "greabock@gmail.com", "account": { "active": true, } }
在这种情况下,填充器将按照与标识符示例相同的方式处理第一级实体(用户)。然后,它将尝试找到账户 - 如果找不到(在当前示例中账户没有id
),则创建一个新的。如果找到了但具有不同的标识符,则替换为新的。旧账户将被删除。当然,相关字段(如user_id
或author_id
- 取决于如何在User::account()
关系中指定)将记录用户的标识符。
HasMany
{ "id": "123e4567-e89b-12d3-a456-426655440000", "name": "Greabock", "email": "greabock@gmail.com", "posts": [ { "id": "1286d5bb-c566-4f3e-abe0-4a5d56095f01", "title": "foo", "text": "bar" }, { "id": "d91c9e65-3ce3-4bea-a478-ee33c24a4628", "title": "baz", "text": "quux" }, { "title": "baz", "text": "quux" } ] }
在“多对一”关系的示例中,填充器将像在HasOne
示例中一样处理每个帖子记录。此外,所有未在传入的帖子数组中呈现的记录都将被删除。
BelongsTo
{ "id" : "123e4567-e89b-12d3-a456-426655440000", "name": "Greabock", "email": "greabock@gmail.com", "organization": { "id": "1286d5bb-c566-4f3e-abe0-4a5d56095f01", "name": "Acme" }, }
虽然这个例子看起来像HasOne
,但实际上它是不同的。如果填充器在数据库中找到了这样的组织,则用户将通过关系字段与其关联。另一方面,如果没有找到,则用户将获得该字段的null
。所有其他相关记录的字段(组织)都将被忽略 - 因为User
不是Organization
关系的“聚合根”,因此不能通过用户对象管理组织字段,也不能创建新的组织。
BelongsToMany
{ "id" : "123e4567-e89b-12d3-a456-426655440000", "name": "Greabock", "email": "greabock@gmail.com", "roles": [ { "id": "dcb41b0c-8bc1-490c-b714-71a935be5e2c", "pivot": { "sort": 0 } } ] }
这个例子类似于HasMany
(在意义上是,所有未在传入的帖子数组中呈现的记录都将被删除)和BelongsTo
(所有字段,除了$primaryKey
字段,都将被忽略,如上所述在belongsTo
部分中所述)。请注意,也可以处理连接点。
所有这些内容都是递归的,适用于任何程度的嵌套。
输出特点
还应注意,所有传递的关系都会在输出时添加到实体中。例如
$user = $populator->populate(User::class, [ 'id' => '123e4567-e89b-12d3-a456-426655440000', 'name' => 'Greabock', 'email' => 'greabock@gmail.com', 'roles' => [ [ 'id' => 'dcb41b0c-8bc1-490c-b714-71a935be5e2c', 'pivot' => ['sort' => 0], ], ], ]); $user->relationLoaded('roles'); // true // хотя flush еще не сделан, все отношения уже прописаны, и нет необходимости загружать их дополнтительно. // Обращение к $user->roles - не вызовет повтороно запроса к бд. $populator->flush(); // Только после этого сущность со всеми ее связями попадёт в базу данных.
待办事项
- 添加未通过 hydrator 的实体持久化的功能
- 在 README 中添加多态关系的描述