webiny/entity

Webiny 实体组件

v1.6.1 2017-09-29 08:12 UTC

README

Entity 组件是 MongoDB 的 ODM 层。使用此组件创建的实体类将作为模块、表单、表格等的主要构建块。每个实体属性都是复杂的数据类型。没有简单的字符串或整数。甚至 boolean 也是一个复杂的数据类型,并有一个单独的类。这使得我们能够自动生成大量代码和界面元素。

安装组件

安装组件的最佳方式是使用 Composer。

composer require webiny/entity

有关该包的附加版本,请访问Packagist 页面

支持的属性

  • 布尔型
  • 字符
  • 整数
  • 浮点数
  • 日期
  • 日期时间
  • 数组
  • 对象
  • 动态
  • 多对一
  • 一对多
  • 多对多

one2manymany2many 属性扩展了 AbstractCollectionAttribute 类。这两个属性是最复杂的,因为它们的值由 EntityCollection 类表示,这是数据库返回的实际数据数组的包装器。这个包装器允许我们实现懒加载,并提供简单的接口来计算结果集中的数据数量(每页和总数)。

在数据库上操作的 AbstractEntity 类始终返回 AbstractEntity 类的实例。

实体结构

实体结构的示例代码

use Webiny\Component\Entity\AbstractEntity;

class Page extends AbstractEntity
{
    protected static $entityCollection = "Page";

    protected function _entityStructure() {

    	// Create attributes
		$this->attr('title')
				->char()
			->attr('author')
				->many2one()
					->setEntity('Author')
			->attr('status')
				->char()
					->setDefaultValue('draft')
			->attr('comments')
				->one2many('page')
					->setEntity('Comment')
					->setOnDelete('cascade')
		    ->attr('approvedComments')
                ->one2many('page')
                    ->setEntity('Comment')
                    ->setFilter(['status' => 'approved']);
			->attr('labels')
				->many2many('Page2Label')
					->setEntity('Label');
		    ->attr('createdOn')
                ->datetime()
                    ->setDefaultValue('now')
            ->attr('modifiedOn')
                ->date()
                    ->setDefaultValue('now')
                    ->setAutoUpdate(true)
            ->attr('metaData')
                ->arr()
                    ->setDefaultValue(['settings' => []]);

	}
}

属性 many2oneone2manymany2many 是懒加载的,这意味着在尝试访问之前,这些属性的值不会从数据库加载。

访问属性

要访问属性值,您可以使用以下两种方法

// Long syntax
$page = Page::findById("53712ed46803fa4e058b456b");
$title = $page->getAttribute('title')->getValue();

// Short syntax
$page = Page::findById("53712ed46803fa4e058b456b");
$title = $page->title;

注意:您需要调用 getValue() 来获取属性的实际值。如果您尝试回显或连接属性(不是值,而是实际的属性),则将触发 __toString 魔法方法,该方法将自动为您调用 getValue() 方法。

如果您正在尝试访问 many2one 属性的值

$page = Page::findById("53712ed46803fa4e058b456b");
$authorName = $page->author->firstName . ' '. $page->author->lastName;
// $authorName will be equal to 'John Doe'

设置值

// provides you with autocomplete on AbstractAttribute methods
$page->getAttribute('title')->setValue('New title');

// or (no autocomplete is the only downside of this)
$page->title->setValue('New title');

// or - This will trigger `__set` magic method and call $page->getAttribute('title')->setValue('New title');
$page->title = 'New title';

setOnce()

此方法允许您防止属性被更新。您使用此方法来仅允许在新的实体实例没有设置 ID(意味着它是一个新实例)时填充属性。在保存新的实体实例后,所有后续对 populate() 的调用都将跳过此属性。

一对多属性

此属性的值是 EntityCollection 的实例。您可以在 foreach 循环中使用它,通过数字索引访问值,还可以调用 count() 方法以找出数据集中的项目总数。

$page = Page::findById("53712ed46803fa4e058b456b");
echo $page->comments->count();

foreach($page->comments as $comment){
...
}

批量填充一对多属性

有多种方法可以从,例如,POST 请求中填充一对多属性。

  1. 将数据结构化为简单的实体 ID 数组
$data = [
    'title'    => 'My shiny title',
    'comments' => [
        '543c0fb76803fa76058b4569',
        '543c0fda6803fa76058b456f'
    ]
];
  1. 将数据结构化为包含实体数据的数组数组。如果数组包含 id,则将加载现有实例,并使用数组中指定的任何数据填充它(适用于更新现有实体)
$data = [
    'title'    => 'My shiny title',
    'comments' => [
        ['id' => '543c0fb76803fa76058b4569', 'someAttribute' => 'newValue'],
        ['id' => '543c0fda6803fa76058b456f']
    ]
];
  1. 将数据结构化为 arrayEntityCollectionAbstractEntity 实例。使用 find 方法
$entityCollection = Comment::find(['status' => 'approved']);

$data = [
    'title'    => 'My shiny title',
    'comments' => $entityCollection
];

或者,如果您手动构建数组...

$instance1 = Comment::findById($id1);
$instance2 = Comment::findById($id2);
$array = [$instance1, $instance2];

$data = [
    'title'    => 'My shiny title',
    'comments' => $array
];

参照完整性

one2many 属性提供了一种控制是否希望与父记录一起删除这些记录或防止删除包含任何子记录的父记录的方法。您可以通过使用 onDelete 方法来设置它,并选择 cascaderestrict 之一。如有必要,还将实现更多选项。

别名

此属性还提供了一种通过链接到同一实体但添加筛选值来创建数据别名的途径。这意味着当您访问属性值时,它将自动应用请求的筛选条件,并只返回匹配筛选条件的链接实体。

->attr('approvedComments')
    ->one2many('page')
        ->setEntity('Comment')
        ->setFilter(['status' => 'approved']);

与其他实体链接

比如说您发布了一条新评论,需要将其与 Page 实体链接,您可以通过两种方法来实现:

第一种方法是加载一个 Page 实例,并在 one2many 属性上使用 add() 方法。

// Load Page
$page = Page::findById("53712ed46803fa4e058b456b");

// Create new Comment
$comment = new Comment();
$comment->populate($commentData);

// Assign and save Page
$page->comments->add($comment);
$page->save();

第二种方法是设置 Page 实例为 Comment 属性(page 属性必须在 Comment 实体中作为 many2one 属性存在)。

// Load Page
$page = Page::findById("53712ed46803fa4e058b456b");

// Create new Comment
$comment = new Comment();
$comment->populate($commentData);

// Set Page instance and save
$comment->page->setValue($page);
$comment->save();

下次您加载 Page 评论时 - 新的 Comment 将包含在数据集中。

ArrayAttribute

此属性主要用于存储与实体相关联的额外数据,这些数据不会在表单或网格中显示给用户(一些设置等)。关于此属性的好处是它的“设置”和“获取”方法,允许您获取和设置嵌套键,如果键的某些部分不存在,则获取默认值。

// $page->settings is an instance of ArrayAttribute

// Set value by specifying whatever nesting level you want
// Even if some of the levels may not exist along the way - they will be created for you automatically
$page->settings->set('level1.level2.level3', 'My new value');

// Get value from key that may not exist
$page->settings->get('level1.level4', 'Default value');

// You can also append values like this
$page->settings[] = 'New value';

// Or using an 'append' method
$page->settings->append('New value');

// And you can also prepend values
$page->settings->prepend('New value');

Many2OneAttribute 的默认值

Many2OneAttribute 的默认值可以通过几种方式指定。

// Provide a default entity ID (can be fetched from logged in user, or retrieved from database or whatever)
$defaultId = '53712ed46803fa4e058b456b';
$this->attr('author')->many2one()->setEntity('Author')->setDefaultValue($defaultId);
// Provide a default entity instance
$defaultInstance = Author::find(['some' => 'condition']);
// or
$defaultInstance = Author::findById('53712ed46803fa4e058b456b');
$this->attr('author')->many2one()->setEntity('Author')->setDefaultValue($defaultInstance);
// Provide data which will be used to populate new entity instance
// (will create a new record with new ID each time a default value is used)
$defaultData = [
    'firstName' => 'Pavel', 
    'lastName' => 'Denisjuk'
];
$this->attr('author')->many2one()->setEntity('Author')->setDefaultValue($defaultData);

查找实体

有三种方法允许您查找实体:findfindByIdfindOne

find(array $conditions = [], array $order = [], $limit = 0, $page = 0) EntityCollection

  • $conditions - 是属性和它们值的键 => 值数组
  • $order - 是排序器的数组,定义为 ['-name', '+title', 'lastName'] ('-' 等于降序,'+' 或无前缀等于升序)
  • $limit - 要返回的实体数量
  • $page - 这将用于为您计算偏移量。注意:$page 值从 1 开始。例如:$limit=10, $page=2 将跳过前 10 条记录并返回下一 10 条。

此方法返回一个 EntityCollection 实例。

// Load Pages
$pages = Page::find(['active' => true], ['-title'], 5, 2);
$count = $pages->count();
foreach($pages as $page){
...
}

findById($id) AbstractEntity

通过给定的 $id 返回一个 AbstractEntity 实例。如果没有找到实体,则返回 null

// Load Page
$page = Page::findById("53712ed46803fa4e058b456b");

findOne(array $conditions = []) AbstractEntity

通过给定的 $conditions 返回一个 AbstractEntity 实例。如果没有找到实体,则返回 null

// Load Page
$page = Page::findOne(['title' => 'First blog post']);

EntityCollection 类

此类用于返回 find() 方法的结果。它实现了 IteratorAggregateArrayAccess 接口,因此其行为与普通数组完全相同,并且它还包含一些实用方法,以帮助您处理数据。

  • toArray($fields = '') - 返回结果集中所有实体的数组表示形式(更多详情见此处
  • add($item) - 将 $item 添加到结果集中(与 One2Many 和 Many2Many 属性一起使用,以将新项添加到属性值中)
  • count() - 返回结果集中的项目数量
  • totalCount() - 返回没有 $limit 和 $page 参数的项目总数
  • contains($item) - 检查给定的 $item 是否已在结果集中存在
  • delete() - 删除结果集中的所有项目(从数据库中删除它们)
  • removeItem($item) - 从结果集中移除项目(不会从数据库中移除。此方法与多对多属性一起使用,用于移除实体之间的链接)

将抽象实体转换为数组

您可以通过调用 toArray() 方法获取当前 AbstractEntity 实例的数组表示形式。默认情况下,只包括简单和单对多属性。如果您想控制要包含哪些属性,请传递一个包含属性名称的字符串。您还可以控制嵌套属性的属性。

$data = $page->toArray('title,author.name,comments.text,comments.id,labels');

这将产生类似以下结果

Array
(
    [author] => Array
        (
            [name] => John Doe
        )

    [title] => First blog post
    [comments] => Array
        (
            [0] => Array
                (
                    [text] => Best blog post ever!
                    [id] => 53dee8d26803fafe098b4569
                )

        )

    [labels] => Array
        (
            [0] => Array
                (
                    [label] => marketing
                    [id] => 53dee8d26803fafe098b456b
                )

            [1] => Array
                (
                    [label] => seo
                    [id] => 53dee8d26803fafe098b456e
                )

        )

)

如果您的实体具有许多属性,您可以使用 '*' 来指定 '所有默认属性',然后只添加您需要的特定属性。默认属性是指不是 One2ManyAttributeMany2ManyAttribute 的所有属性。如果您需要获取 One2ManyAttributeMany2ManyAttribute 属性值,您需要手动指定它们。

$data = $page->toArray('*,comments');

这将产生以下结果

Array
(
    [author] => Arrayf
        (
            [id] => 53dee8d26803fafe098c4769
            [name] => John Doe
        )

    [title] => First blog post
    [comments] => Array
        (
            [0] => Array
                (
                    [text] => Best blog post ever!
                    [id] => 53dee8d26803fafe098b4569
                )

        )
)

除了属性名称外,您还可以通过指定第二个参数中的深度来控制返回数据的深度。默认深度为 1,表示 self + 1(上述示例显示默认设置深度为 1 的输出)。

资源

要运行单元测试,您需要使用以下命令

$ cd path/to/Webiny/Component/Entity/
$ composer.phar install
$ phpunit

请确保您已在 Tests\MongoExampleConfig.yaml 中设置了您的 MongoDB 驱动程序设置。