nuad / graph-objects
一个数组到对象映射的PHP库。在将传入的JSON或数据库结果映射时很有用
Requires
- php: >=5.6.0
Requires (Dev)
- phpunit/phpunit: ^9.5
This package is not auto-updated.
Last update: 2024-09-25 22:53:18 UTC
README
图对象是php5的注入器,使用类中定义的元数据将关联数组映射到类实例,当与数据库结果集或可以轻松格式化为关联数组的格式(如JSON)一起工作时非常有用。
该库没有额外要求(除了用于测试的PHPUnit)。它不要求自定义注解语法或使用文档注释来限制反射使用,以保持尽可能简单和轻量。
安装
使用Composer安装 nuad/graph-objects
$ composer require nuad/graph-objects
用法
所有项目文件都在Nuad/Graph命名空间下。所有图对象必须实现的接口是Graphable接口,所有功能都在GraphAdapter特质中。
库使用的两个主要方法是create()和graph()方法。第一个方法将仅输出一个干净的对象实例,第二个方法将提供将关联数组映射到对象的元数据。尽管GraphAdapter提供了一个create()方法的版本(使用反射创建实例),但可能需要一个具有默认值的对象实例。
假设有一个名为Person的DTO、entity等类,该类可能是一个User对象的基类。Person类的简单定义可能是
class Person
{
public $id;
public $name;
public $gender;
public function __construct($id, $name, $gender)
{
$this->id = $id;
$this->name = $name;
$this->gender = $gender;
}
}
现在要使用该库,上述类变为
use Nuad\Graph\Entity;
use Nuad\Graph\Graphable;
use Nuad\Graph\GraphAdapter;
use Nuad\Graph\Type;
class Person implements Graphable
{
use GraphAdapter;
public $id;
public $name;
public $gender;
public function __construct($id, $name, $gender)
{
$this->id = $id;
$this->name = $name;
$this->gender = $gender;
}
public static function create()
{
return new self(0,'','');
}
public function graph()
{
return Entity::graph(
['Person']
)
->properties(
[
'id' => Type::Integer(),
'name' => Type::String(),
'gender' => Type::String()
]
);
}
}
注意:可以忽略create方法,因为我们可以使用特质的create()方法,但这样可以在不使用反射的情况下创建实例。
现在要使用该类将$data关联数组映射,假设是从JSON格式中获取
//person.json
{
"id": 1,
"name": "Jewell Lester",
"gender": "female"
}
$data = json_decode(file_get_contents('person.json'),true);
$person = Person::map($data);
现在$person变量应该是一个包含从JSON文件中获取的数据的新Person实例。
语法
graph方法必须返回一个Entity对象。Entity的构造函数需要一个包含名称的数组,这些名称将标识类。Entity包含一个属性列表(使用properties()方法定义),它是一个关联数组,键是主类的属性名称,值是每个属性的类型。
类型
属性的类型分为三个主要类别
- 平面类型(整数、双精度、布尔值、平面数组)
- 对象(指另一个图对象。构造函数需要一个该对象的干净实例)
- 集合(指一组图对象。构造函数也需要该对象的干净实例)
继承和更复杂类型
使用上面的示例,我们将定义一个User类,它将扩展Person并包含一个Location对象,该对象将包含一个Point对象。
额外的Location和Point类
class Point implements Graphable
{
use GraphAdapter;
/**
* @var float
*/
public $lat;
/**
* @var float
*/
public $lng;
public function __construct($lat=0.0, $lng=0.0)
{
$this->lat = $lat;
$this->lng = $lng;
}
public function graph()
{
return Entity::graph(
['Point']
)
->properties(
[
'lat' => Type::Double()
->expected(array('latitude')),
'lng' => Type::Double()
->expected(array('longitude'))
]
);
}
}
class Location implements Graphable
{
use GraphAdapter;
/**
* @var Point
*/
public $point;
/**
* @var string
*/
public $city;
/**
* @var string
*/
public $country;
/**
* @var string
*/
public $address;
public function __construct(Point $point=null, $city='', $country='', $address='')
{
$this->point = $point;
$this->city = $city;
$this->country = $country;
$this->address = $address;
}
public function graph()
{
return Entity::graph(
['Location']
)
->properties(
[
'point' => Type::Object(Point::create()),
'city' => Type::String(),
'country' => Type::String(),
'address' => Type::String()
]
);
}
}
注意:Location包含一个名为point的对象,其类型为Point,并在graph()方法中如此定义。Point图定义了其两个属性预期的数组。在expected属性中,您可以放置可能遇到的数据中属性名称的变体。
现在来定义User子类
class User extends Person
{
use GraphAdapter;
/**
* @var int
*/
public $age;
/**
* @var string
*/
public $email;
/**
* @var boolean
*/
public $loggedIn;
/**
* @var Location
*/
public $location;
/**
* @var Person[]
*/
public $friends;
public function __construct($id,$name,$gender,$age, $email, $loggedIn, $location, $friends)
{
parent::__construct($id,$name,$gender);
$this->age = $age;
$this->email = $email;
$this->loggedIn = $loggedIn;
$this->location = $location;
$this->friends = $friends;
}
public function graph()
{
return parent::graph()->extend(
['User']
)
->properties(
[
'age' => Type::Integer(),
'email' => Type::String(),
'loggedIn' => Type::Boolean(),
'location' => Type::Object(Location::create()),
'friends' => Type::Collection(Person::create())
]
);
}
}
注意:GraphAdapter特质必须重新定义,并在graph()方法中像上面那样扩展父类的graph()方法。
特性
-
注入:
使用注入方法代替map方法将强制GraphAdapter特质使用对象的构造函数。此外,injectWithInstance方法接受一个图形对象实例作为参数,该参数将用于映射数据。
-
映射空:
特质中的mapEmpty方法只映射对象中为null的值。当在单独的步骤中创建对象属性时很有用。
-
预期 & 绑定:
类型的expected()方法接受一个索引数组,其中可能包含属性值的传入数据的位置。属性名称将优先于预期值。要覆盖此行为,请使用bind方法(也接受一个索引数组,该索引数组将优先于预期值和属性名称)。语法
'id' => Type::String() ->bindTo(['_id','ID']) ->expected(['identity','identifier','identification'])
-
回调:
实体通过finalize()方法接收一个finaliza回调,该回调在映射完成后立即提供对映射对象和原始数据的访问,可以在继续之前验证对象属性。
类型通过handler()方法接收一个处理器回调,该回调将提供对属性有效负载中数据的访问以及数据找到的索引。如果定义了返回值,则将使用处理器回调返回的值,如果处理器回调返回null,则适配器将正常映射属性。
return Entity::graph( ['Person'] ) ->properties( [ 'id' => Type::Integer(), 'name' => Type::String(), 'gender' => Type::String() ->handler(function($data,$name,$scenario) { return $data === 'fem' ? 'female' : 'male' }) ] )->finalize(function(Person $instance, $scenario, $data) { if($instance->id === null) { throw new Exception('invalid person data'); } });
注意:两个回调都提供对名为'scenario'的变量的访问。该变量可以通过inject/map方法传递,并在属性用于不同场景时很有用,例如不同的区域数据。