egorov / mapper
Tarantool 的 PHP 对象映射器。
Requires
- php: ^7.0
- tarantool/client: ^0.4.1
Requires (Dev)
- nesbot/carbon: ^1.22
- phpdocumentor/reflection-docblock: ^4.2.0
- phpunit/phpunit: 6.5
- rybakit/msgpack: ^0.3.1
Suggests
- ext-msgpack: For using PeclPacker.
- phpdocumentor/reflection-docblock: Annotation Plugin
- rybakit/msgpack: For using PurePacker.
- dev-master
- 4.3.20
- 4.3.19
- 4.3.18
- 4.3.17
- 4.3.16
- 4.3.15
- 4.3.14
- 4.3.13
- 4.3.12
- 4.3.11
- 4.3.10
- 4.3.9
- 4.3.8
- 4.3.7
- 4.3.6
- 4.3.5
- 4.3.4
- 4.3.3
- 4.3.2
- 4.3.1
- 4.3.0
- 4.2.23
- 4.2.22
- 4.2.21
- 4.2.10
- 4.2.9
- 4.2.8
- 4.2.7
- 4.2.6
- 4.2.5
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.7
- 4.0.6
- 4.0.5
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 3.6.12
- 3.6.11
- 3.6.10
- 3.6.9
- 3.6.8
- 3.6.7
- 3.6.6
- 3.6.5
- 3.6.4
- 3.6.3
- 3.6.2
- 3.6.1
- 3.6.0
- 3.5.9
- 3.5.8
- 3.5.7
- 3.5.6
- 3.5.5
- 3.5.4
- 3.5.3
- 3.5.2
- 3.5.1
- 3.5.0
- 3.4.9
- 3.4.8
- 3.4.7
- 3.4.6
- 3.4.5
- 3.4.4
- 3.4.3
- 3.4.2
- 3.4.1
- 3.4.0
- 3.3.12
- 3.3.11
- 3.3.10
- 3.3.9
- 3.3.8
- 3.3.7
- 3.3.6
- 3.3.5
- 3.3.4
- 3.3.3
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.6
- 3.2.5
- 3.2.4
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.2
- 3.0.1
- 3.0.0
- 2.7.8
- 2.7.7
- 2.7.6
- 2.7.5
- 2.7.4
- 2.7.3
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.1
- 2.6.0
- 2.5.0
- 2.4.0
- 2.3.3
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.6
- 2.2.5
- 2.2.4
- 2.2.3
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.7
- 2.1.6
- 2.1.5
- 2.1.4
- 2.1.3
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.3
- 2.0.2
- 2.0.1-beta
- 2.0.0-alpha
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-v.1
This package is auto-updated.
Last update: 2020-01-21 12:13:58 UTC
README
安装
推荐通过 Composer 安装库。
$ composer require tarantool/mapper
实例化映射器
通常,您在服务提供者中管理依赖项。要开始,您应该实例化连接、打包器、客户端以及映射器本身。在这个例子中,我们使用了 PurePacker 和 StreamConnection。这意味着您不需要任何 pecl 扩展。要查看其他实现,请参阅 客户端文档
use Tarantool\Client\Client; use Tarantool\Client\Connection\StreamConnection; use Tarantool\Client\Packer\PurePacker; use Tarantool\Mapper\Mapper; $connection = new StreamConnection(); $client = new Client($connection, new PurePacker()); $mapper = new Mapper($client);
日志记录
默认情况下,客户端不会记录 Tarantool 请求,您可以使用支持日志记录的 mapper\client。
use Tarantool\Client\Connection\StreamConnection; use Tarantool\Client\Packer\PurePacker; use Tarantool\Mapper\Mapper; use Tarantool\Mapper\Client; $connection = new StreamConnection(); $client = new Client($connection, new PurePacker()); $mapper = new Mapper($client); $result = $client->ping(); $log = $client->getLog();
现有类型
您可以从当前的配置开始。请注意 - 所有实例都映射到键值对象。
$globalSpace = $mapper->find('_space', ['name' => '_space']); echo $globalSpace->id; // 280 $indexes = $mapper->find('_index', ['id' => $globalSpace->id]); var_dump($indexes); // indexes on _index space echo $indexes[0]->name; // primary index echo $indexes[0]->type; // tree $guest = $mapper->find('_user', ['name' => 'guest']); echo $guest->id; // 0 echo $guest->type; // user
描述实体
要开始,您应该使用元对象描述您的类型和字段。
$person = $mapper->getSchema()->createSpace('person'); // add properties $person->addProperty('id', 'unsigned'); $person->addProperty('name', 'string'); $person->addProperty('birthday', 'unsigned'); $person->addProperty('gender', 'string'); // add multiple properties $person->addProperties([ 'telegram' => 'string', 'vk' => 'string', 'facebook' => 'string', ]); // add indexes // first index is primary $person->createIndex([ 'type' => 'hash', // define type 'fields' => ['id'], ]); // create unique indexes using property or array of properties as parameter $person->createIndex('name'); // create not unique indexes $person->createIndex([ 'fields' => 'birthday', 'unique' => false ]); // if you wish - you can specify index name $person->createIndex([ 'fields' => ['name', 'birthday'], 'type' => 'hash', 'name' => 'name_with_birthday', ]);
使用迁移
use Tarantool\Mapper\Mapper; use Tarantool\Mapper\Migration; class InitTesterSchema implements Migration { public function migrate(Mapper $mapper) { $tester = $mapper->getSchema()->createSpace('tester', [ 'engine' => 'memtx', // or vinyl 'properties' => [ 'id' => 'unsigned', 'name' => 'string', ] ]); $tester->createIndex('id'); } } $mapper->getBootstrap()->register(InitTesterSchema::class); // or register instance $mapper->getBootstrap()->register(new InitTesterSchema()); $mapper->getBootstrap()->migrate();
使用流畅 API
use Tarantool\Mapper\Mapper; use Tarantool\Mapper\Migration; class InitTesterSchema implements Migration { public function migrate(Mapper $mapper) { $mapper->getSchema()->createSpace('person') ->addProperty('id', 'unsigned') ->addProperty('name', 'string') ->addIndex('id'); } }
处理数据
现在,您可以使用映射器实例存储和检索 Tarantool 存储中的数据。
// get repository instance $persons = $mapper->getRepository('person'); // create new entity $dmitry = $persons->create([ 'id' => 1, 'name' => 'Dmitry' ]); // save $mapper->save($dmitry); // you can create entities using mapper wrapper. // this way entity will be created and saved in the tarantool $vasily = $mapper->create('person', [ 'id' => 2, 'name' => 'Vasily' ]); // you can retreive entites by id from repository $helloWorld = $mapper->getRepository('post')->find(3); // or using mapper wrapper $helloWorld = $mapper->find('post', 3); // updates are easy $helloWorld->title = "Hello World!"; $mapper->save($helloWorld);
索引
$note = $mapper->getSchema()->createSpace('note'); $note->addProperty('slug', 'string'); $note->addProperty('title', 'string', $note->addProperty('status', 'string'); $note->addIndex('slug'); $note->addIndex([ 'fields' => 'status', 'unique' => false ]); // find using repository $mapper->getRepository('note')->find(['status' => 'active']); // find using shortcut $mapper->find('note', ['status' => 'active']); // find first $mapper->getRepository('note')->findOne(['slug' => 'my-secret-note']); // composite indexes can be used partial $person = $mapper->getSchema()->createSpace('person'); $person->addProperty('id', 'unsigned'); $person->addProperty('client', 'unsigned'); $person->addProperty('sector', 'unsigned'); $person->addProperty('name', 'unsigned'); $person->addIndex('id'); $person->addIndex([ 'fields' => ['client', 'sector'], 'unique' => false ]); // using index parts $mapper->find('person', ['client' => 2]); $mapper->find('person', ['client' => 2, 'sector' => 27]);
数组属性
您可以将数组作为属性存储,无需将其序列化为字符串。
$pattern = $mapper->getSchema()->createSpace('shift_pattern'); $pattern->addProperty('id', 'unsigned'); $pattern->addProperty('title', 'string'); $pattern->addProperty('pattern', '*'); $pattern->addIndex('id'); $mapper->create('shift_pattern', [ 'id' => 1, 'title' => '5 days week', 'pattern' => [ ['work' => true], ['work' => true], ['work' => true], ['work' => true], ['work' => true], ['work' => false], ['work' => false], ] ]); $mapper->get('shift_pattern', 1)->pattern[5]; // read element with index 5 from pattern array
序列插件
如果您想使用,可以启用基于序列空间的序列插件来生成下一个值。或者,您可以使用任何其他来源实现 ID 生成器,例如使用 raft 协议。
$mapper->getSchema()->createSpace('post', [ 'id' => 'unsigned', 'title' => 'string', 'body' => 'string', ]) ->addIndex('id'); $mapper->getPlugin(Tarantool\Mapper\Plugin\Sequence::class); $entity = $mapper->create('post', [ 'title' => 'Autoincrement implemented', 'body' => 'You can use Sequence plugin to track and fill your entity id' ]); echo $entity->id; // will be set when you create an instance
用户定义类插件
如果您想使用,可以指定用于存储库和实体实例的类。省略了实体和存储库类的实现,但您只需扩展基类即可。
$userClasses = $mapper->getPlugin(Tarantool\Mapper\Plugin\UserClasses::class); $userClasses->mapEntity('person', Application\Entity\Person::class); $userClasses->mapRepository('person', Application\Repository\Person::class); $nekufa = $mapper->create('person', [ 'email' => 'nekufa@gmail.com' ]); get_class($nekufa); // Application\Entity\Person; $mapper->getSchema()->getSpace('person')->getRepository(); // will be instance of Application\Repository\Person
注解插件
您可以使用 dobclock 描述您的实体。映射器将为您创建空间、格式和索引。
namespace Entity; use Tarantool\Mapper\Entity; class Person extends Entity { /** * @var integer */ public $id; /** * @var string */ public $name; } class Post extends Entity { /** * @var integer */ public $id; /** * @var string */ public $slug; /** * @var string */ public $title; /** * @var string */ public $body; /** * @var Person */ public $author; /** * @var integer * @required */ public $salary; }
如果您想对字段进行索引,扩展存储库并定义索引属性
namespace Repository; use Tarantool\Mapper\Repository; class Post extends Repository { public $engine = 'memtx'; // or vinyl public $indexes = [ // if your index is unique, you can set property collection ['id'], // extended definition unique index with one field [ 'fields' => ['slug'], 'unique' => true, ], // extended definition (similar to Space::addIndex params) // [ // 'fields' => ['year', 'month', 'day'], // 'unique' => true // ], ]; }
注册插件和所有类
$mapper->getPlugin(Tarantool\Mapper\Plugin\Sequence::class); // just not to fill id manually $mapper->getPlugin(Tarantool\Mapper\Plugin\Annotation::class) ->register(Entity\Person::class) ->register(Entity\Post::class) ->register(Repository\Person::class) ->migrate(); // sync database schema with code $nekufa = $mapper->create('person', ['name' => 'dmitry']); $post = $mapper->create('post', [ 'author' => $nekufa, 'slug' => 'hello-world', 'title' => 'Hello world', 'body' => 'Now you can use mapper better way' ]); // in addition you can simple get related entity $post->getAuthor() == $nekufa; // true // or related collection $nekufa->getPostCollection() == [$post]; // true
内部机制
Mapper使用IdentityMap和查询缓存
$dmitry = $mapper->getRepository('person')->findOne(['name' => 'Dmitry']); // person with id 1 echo $dmitry == $mapper->findOne('person', 1); // true // query result are cached until you create new entites $mapper->getRepository('person')->findOne(['name' => 'Dmitry']); // you can flush cache manually $mapper->getRepository('person')->flushCache();
性能
Mapper的额外开销取决于行数和操作类型。表中以每实体毫秒为单位显示额外开销。在某些情况下,由于浮点精度,无法计算开销。
操作 | 100 | 1000 | 10 000 | 100 000 |
---|---|---|---|---|
逐个创建实体 | 0.017 | 0.022 | 0.023 | 0.024 |
逐个选择实体 | - | 0.015 | 0.016 | 0.018 |
一次性选择所有实体 | - | - | 0.002 | 0.006 |
性能测试在(intel i5-6400),Windows 10的bash中使用php 7.0.18进行。例如,当单个选择会产生10,000个实体时,你会得到20毫秒的开销。