maer / entity
创建预定义实体对象
Requires
- php: >=7.1
Requires (Dev)
- phpunit/phpunit: ^7.5
- squizlabs/php_codesniffer: ^3.4
README
本版本包含破坏性更改,与1.x版本不兼容
与传递实体数组或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 Entity
或 Entity::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,现在是返回数组的函数而不是类属性
注意
如果您有任何问题、建议或问题,请告诉我!
编码愉快!