maer/entity

创建预定义实体对象

2.0.5 2021-12-19 19:36 UTC

This package is auto-updated.

Last update: 2024-09-20 01:59:23 UTC


README

本版本包含破坏性更改,与1.x版本不兼容

Build Status

与传递实体数组或StdClass实例相比,其中你永远无法信任是否存在特定的键或属性,或者它们是否包含正确的数据类型,因此最好使用预定义的实体对象。

我在构建一个REST-api时创建了此类,其中数据来自不同的来源。例如,MySQL默认将所有值返回为字符串。

在其他情况下,数据源的一个参数可能已更改名称或已删除。那时,如果你不需要在所有代码中找到它并更改使用它的地方,那就很棒了。

安装

克隆此存储库或使用以下命令使用composer下载库:

$ composer require maer/entity

使用

定义一个实体

在定义实体时,您开始创建一个新的类,该类扩展了Maer\Entity\Entity

class Hero extends Maer\Entity\Entity {}

默认属性值和类型

创建一个空实体并不那么令人兴奋。我们应该定义一些属性和默认值

class Hero extends Maer\Entity\Entity
{
    protected $id        = 0;
    protected $name      = '';
    protected $awesome   = false;
    protected $someFloat = 0.0;
    protected $anything  = null;
}

当您定义默认值时,您需要确保将默认值设置为正确的数据类型。稍后当您设置/更新属性值时,新值将转换为与默认值相同的类型。支持自动转换的数据类型:整数字符串布尔浮点数/双精度浮点数。如果默认值为null,它可以设置为任何内容。

保护属性

如果您希望能够在代码中使用属性,但又不想在API响应中公开它,您可以通过“保护”它。一个例子是一个用户实体有一个密码散列。要保护(删除)JSON序列化或作为数组检索时的属性,可以使用protect()方法,如下所示

class User extends Maer\Entity\Entity
{
    protected $id           = 0;
    protected $name         = '';
    protected $passwordHash = '';

    protected function protect() : array
    {
        return [
            'passwordHash',
            // Keep adding property names to protect, if needed
        ];
    }
}

重新映射属性

有时源数组可能具有与定义的实体参数不同的键名。为了使您尽可能容易,您可以将参数名称映射到您的实体,您的实体将自动在实例化时重新映射它们。

class User extends Maer\Entity\Enity
{
    protected $username = '';

    protected function map() : array
    {
        // Assign the map as ['entityPropName' => 'sourcePropName']
        return [
            'username' => 'email',
        ];
    }
}

如果您现在发送一个带有email键的数组,该值将被映射为username

重新映射嵌套属性

如果您想在一个多维数组中映射一个值,您可以像上面一样做,但是使用点符号作为映射键。

class User extends Maer\Entity\Enity
{
    protected $username = '';

    protected function map() : array
    {
        return [
            'username' => 'user.username',
        ];
    }
}

这将映射['user' => ['username' => 'Chuck Norris']]username。您可以去多少层嵌套没有限制。

实例化一个实体

根据您的需求,您可以通过多种方式实例化一个实体。

创建默认实体

由于它是一个类,您可以像这样使用默认值创建一个新的实体

$hero = new Hero();

echo $hero->id;
// Returns: 0, just as we defined earlier.

您也可以通过传递一个数组给构造函数来为它设置新的值

$hero = new Hero([
    'id' => 1337,
]);

echo $hero->id;
// Returns: 1337, just as we defined earlier.

只需记住,这些值将被转换为与默认值相同的数据类型。

将数组转换为实体

在创建单个实体时,您可以使用上述构造函数方法,或者可以使用静态方法 Entity::make()

$hero = Hero::make([
    'id' => 1337,
]);

echo $hero->id;
// Returns: 1337

将多维数组转换为实体列表

静态方法 Entity::make() 更聪明一些,不仅可以为您提供单个实体。例如,如果您传递一个多维数组,它将返回一个包含实体的 Collection-实例

$dataset = [
    [
        'id'   => 1337,
        'name' => 'Chuck Norris',
    ],
    [
        'id'   => 12345,
        'name' => 'Some guy',
    ],
];

$heroes = Hero::make($dataset);

echo $heroes[0]->id;
// Returns: 1337

您还可以定义哪个属性应该用作数组键,使其成为关联数组。

$heroes = Hero::make($dataset, 'id');

echo $heroes[1337]->name;
// Returns: "Chuck Norris"

获取数组而不是集合

如果您希望 make() 方法返回数组而不是 Collection 实例,请将 true 作为第四个参数传递

$heroes = Hero::make($dataset, null, null, true);

在实例化时修改值

有时您会得到一个需要修改后才创建实体的值列表。在这个例子中,我们将展示如何在一个 URL 前面添加 http://(如果它缺失的话)

假设我们有一个实体和如下所示的数据集

class Website extends Maer\Entity\Entity
{
    protected $title = '';
    protected $url   = '';
}

$dataset = [
    'title'   => 'Google',
    'url'     => 'www.google.com',
];

$website = new Website($dataset);

echo $website->url;
// Returns: "www.google.com"

当然,我们可以在实例化实体之前添加 http://,但这需要我们每次实例化实体时都重复它。如果它是一系列网站,我们还必须手动遍历数据集。

实例化时的修改器

幸运的是,我们可以在创建实体时以闭包的形式发送一个修改器

$website = new Website($dataset, function (array &$params)  {
    if (isset($params['url']) && strpos($params['url'], 'http://') !== 0) {
        // We got a parameter called url that doesn't start with http://
        $params['url'] = 'http://' . $params['url'];
    }
});

如您所见,闭包将获得 $params 数组的引用,这意味着它不需要返回任何内容。

您也可以使用静态方法 Entity::make() 做同样的事情

$website = Website::make($dataset, null, function (array &$params) {
    // ... Modifie the values like the above example
});

全局修改器

使用闭包在您想要为一些特定实例添加修改器时效果很好。但是,如果您想要将修改器用于每个实例,请使用 modified() 方法。

class Website extends Maer\Entity\Entity
{
    protected $title = '';
    protected $url   = '';


    protected function modifier(array $params)
    {
        if (isset($params['url']) && strpos($params['url'], 'http://') !== 0) {
            // We got a parameter called url that doesn't start with http://
            $params['url'] = 'http://' . $params['url'];
        }
    }
}

$dataset = [
    'title'   => 'Google',
    'url'     => 'www.google.com',
];

$website = new Website($dataset);
// Or
$website = Website::make($dataset);

echo $website->url;
// Returns: "http://www.google.com"

注意:如果您有一个全局的 modifier() 方法,并且在实例化时仍然发送了一个修改器,则全局修改器将首先被调用,然后是特定实例的修改器。

辅助方法

检查属性是否存在

如果您尝试获取或设置一个不存在的属性,将抛出一个 \InvalidArgumentException,除非实体是通过 new EntityEntity::make() 方法创建的,或者当您使用 replace() 方法时。

要检查属性是否存在,请使用 has() 方法

if ($hero->has('name')) {
    // Yes, it exists and can be used
} else {
    // No, doesn't exist. Try to get/set it, an exception will be thrown
}

第一个参数是包含值的属性的名称,第二个参数是格式(默认为: "F j, Y")。

此方法也适用于包含 UNIX 时间戳的属性。

将实体转换为数组

如果您需要将实体转换为数组,请使用 asArray()

$array = $hero->asArray();
// Return: ['id' => 1337, 'name' => 'Chuck Norris', ...]

protect() 方法返回的所有属性都将被移除。

将实体转换为JSON

Entity 类实现了 JsonSerializable 接口,因此只需使用 json_encode()

$json = json_encode($hero);
// Return: "{"id": 1337, "name": "Chuck Norris", ...}"

protect() 方法返回的所有属性都将被移除。

替换实体数据

有时您想更新实体中的数据。如果只是少数几个值,那么使用 $entity->foo = 'bar'; 就可以了,但如果您有具有许多属性的大型实体并希望替换它们,则可以使用 replace() 方法

// New data
$data = [
    'name' => 'Batman',
    ...
];

$entity->replace($data);

// $entity->name now has the value "batman"

这将用数组中的数据替换现有的实体属性。

您也可以传递一个修改器

// New data
$data = [
    'name' => 'Batman',
    ...
];

$entity->replace($data, function (array $params) {
    // Modify the data
});

// $entity->name now has the value "batman"

重置实体

如果您出于任何原因需要将实体重置为其默认值,请使用 reset() 方法

$entity->reset();

重置属性

如果您只想将特定属性重置为其默认值,请使用 resetProperty() 方法

$entity->resetProperty('nameOfTheProperty');

创建您自己的辅助方法

您当然可以创建自己的助手函数

class Hero extends Maer\Entity\Entity
{
    protected $name = '';

    public function myNewHelper()
    {
        // Do stuff...
    }
}

并且像访问任何其他方法一样访问它: $hero->myNewHelper()

集合

当您使用 make() 方法同时创建多个实体时,您将返回一个 Maer\Entity\Collection 实例。

此类可以用作数组(实现 ArrayAccess 和 Countable 接口)。

此类本身具有一些辅助方法。

计数

获取集合的实体数量

echo $collection->count();
// same as count($collection)

获取第一个元素

要获取集合中的第一个元素,请调用 first() 方法

$firstElement = $collection->first();

// $firstElement now contains the first entity in the collection

获取最后一个元素

要获取集合中的最后一个元素,请调用 last() 方法

$firstElement = $collection->last();

// $firstElement now contains the last entity in the collection

获取属性值列表

如果您想从一个单一属性获取所有值(来自集合中的所有实体)

$names = $collection->list('username');

// Will give you something like:
// [
//     'firstUsername',
//     'secondUsername',
//     'thirdUsername',
//     ...
// ]

您也可以通过传递属性名称作为第二个参数来定义用作索引的属性

$names = $collection->list('username', 'id');

// Will give you something like:
// [
//     1337 => 'firstUsername',
//     1234 => 'secondUsername',
//     555  => thirdUsername',
//     ...
// ]

对实体进行排序

您可以使用 usort() 方法对集合中的实体进行排序

$collection->usort(function ($a, $b) {
    return $a->name <=> $b->name;
});

此方法与核心函数 usort() 的工作方式完全相同(因为它在后台使用它)。

删除实体

要从集合中删除实体,请使用 unset() 方法

$collection->unset('theEntityIndex');

附加组件

在版本 1 中,您在基实体类中有 date() 辅助方法。从版本 2 开始,所有辅助方法都是 trait,您需要自己将其添加到实体中。这是为了使实体类尽可能精简。

DateTimeTrait

Maer\Entity\Traits\DateTimeTrait trait 包含一些日期方法,使事情变得更容易。

日期

获取格式化的日期字符串属性

class Foo extends Maer\Entity\Entity
{
    protected $created = '2019-06-10 13:37:00';

    // Include the trait
    use Maer\Entity\Traits\DateTimeTrait;
}

$foo = new Foo();

// First argument is the property to use
echo $foo->date('created');
// Returns: June 10, 2019

// The second argument is the datetime format (standard PHP-date formats)
echo $foo->date('created', 'Y-m-d');
// Returns: 2019-06-10

// If you want to use a specific timezone, pass it as a third argument
echo $foo->date('created', 'Y-m-d', 'Europe/Stockholm');

dateTime

如果您想获取 DateTime 实例,请使用

// First argument is the propety to use
$date = $foo->dateTime('created');
// Returns an instance of DateTime

// If you want to use a specific timezone, pass it as a second argument
$date = $foo->dateTime('created', 'Europe/Stockholm');

timestamp

如果您想获取日期作为时间戳,请使用

// First argument is the propety to use
$timestamp = $foo->timestamp('created');
// Returns: 1560166620

// If you want to use a specific timezone, pass it as a second argument
$timestamp = $foo->timestamp('created');

TextTrait

Maer\Entity\Traits\TextTrait trait 包含一些文本方法,使事情变得更容易。

摘录

如果您只想显示文本的摘录,可以使用 excerpt() 方法

class Foo extends Maer\Entity\Entity
{
    protected $content = 'Here be some lorem ipsum text';

    // Include the trait
    use Maer\Entity\Traits\TextTrait;
}

$foo = new Foo();

$maxLength = 20;
$suffix    = '...';

echo $foo->excerpt('content', $maxLength, $suffix);
// Returns: Here be some...

返回字符串的长度可以小于最大长度,但永远不会更长。该方法确保不会截断单词,并且后缀适合最大长度。

默认的最大长度为 300,后缀为 "..."。

版本2中的更改

版本 2 完全重建,以使用更少的内存并稍微更快。在创建 10,000 个实体时测试,它仅稍微快一点,但使用的内存约为 2-3 倍更少。

主要更改的快速概述

  • 属性现在定义为受保护的类属性,而不是 $_params = []
  • Entity::make() 现在默认返回 Maer\Entity\Collection 实例而不是数组
  • 辅助方法,如 $entity->date(),已移动到 traits 并不再包含在基实体中
  • 设置,如 map 和 protect,现在是返回数组的函数而不是类属性

注意

如果您有任何问题、建议或问题,请告诉我!

编码愉快!