greabock/populator

eloquent模型填充器

2.0.10 2022-06-10 10:17 UTC

This package is auto-updated.

Last update: 2024-09-19 09:41:49 UTC


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的实体。无论如何,填充器将填充该模型传递的emailname。在这种情况下,行为类似于User::findORNew($id)

🔝

HasOne

{
  "id": "123e4567-e89b-12d3-a456-426655440000",
  "name": "Greabock",
  "email": "greabock@gmail.com",
  "account": {
    "active": true,
  }
}

在这种情况下,填充器将按照与标识符示例相同的方式处理第一级实体(用户)。然后,它将尝试找到账户 - 如果找不到(在当前示例中账户没有id),则创建一个新的。如果找到了但具有不同的标识符,则替换为新的。旧账户将被删除。当然,相关字段(如user_idauthor_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 中添加多态关系的描述

🔝