{"id":25009348,"url":"https://github.com/tarantool-php/mapper","last_synced_at":"2025-04-13T02:09:10.473Z","repository":{"id":6268536,"uuid":"54117043","full_name":"tarantool-php/mapper","owner":"tarantool-php","description":"Map tarantool tuples to php objects.","archived":false,"fork":false,"pushed_at":"2025-04-03T15:58:20.000Z","size":605,"stargazers_count":69,"open_issues_count":2,"forks_count":9,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-13T02:08:35.157Z","etag":null,"topics":["mapper","php","schema","tarantool"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tarantool-php.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-17T12:44:27.000Z","updated_at":"2025-04-03T15:57:45.000Z","dependencies_parsed_at":"2024-05-03T15:33:04.996Z","dependency_job_id":"3e183f53-e8b4-4925-a55c-ebf91841887d","html_url":"https://github.com/tarantool-php/mapper","commit_stats":null,"previous_names":[],"tags_count":244,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool-php%2Fmapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool-php%2Fmapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool-php%2Fmapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool-php%2Fmapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tarantool-php","download_url":"https://codeload.github.com/tarantool-php/mapper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248654091,"owners_count":21140236,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mapper","php","schema","tarantool"],"created_at":"2025-02-05T04:39:25.009Z","updated_at":"2025-04-13T02:09:10.435Z","avatar_url":"https://github.com/tarantool-php.png","language":"PHP","readme":"# Tarantool Mapper\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Testing](https://github.com/tarantool-php/mapper/actions/workflows/tests.yml/badge.svg)](https://github.com/tarantool-php/mapper/actions/workflows/tests.yml)\n[![Latest Version](https://img.shields.io/github/release/tarantool-php/mapper.svg?style=flat-square)](https://github.com/tarantool-php/mapper/releases)\n[![Total Downloads](https://img.shields.io/packagist/dt/tarantool/mapper.svg?style=flat-square)](https://packagist.org/packages/tarantool/mapper)\n[![Telegram](https://img.shields.io/badge/Telegram-join%20chat-blue.svg)](https://t.me/tarantool_php)\n\n- [Getting Started](#getting-started)\n- [Schema Management](#schema-management)\n- [Working with the data](#working-with-the-data)\n- [Schema Cache](#schema-cache)\n- [Query Cache](#query-cache)\n- [Changes registration](#changes-registration)\n- [Multiple connections](#multiple-connections)\n- [Lua code delivery](#lua-code-delivery)\n- [Performance](#performance)\n\n## Getting started\nSupported versions are php 8+ and tarantool 2+.\\\nThe recommended way to install the library is through [Composer](http://getcomposer.org):\n```bash\ncomposer require tarantool/mapper\n```\n\nUsually, you manage dependencies in your service provider.\\\nTo get started you should create client instance and pass it to mapper constructor.\\\nIn this example we use PurePacker and StreamConnection.\\\nTo see other implementations please check [client documentation](https://github.com/tarantool-php/client#creating-a-client)\n\n```php\nuse Tarantool\\Client\\Client;\nuse Tarantool\\Mapper\\Mapper;\n\n$client = Client::fromDefaults();\n$mapper = new Mapper($client);\n\n// internaly mapper wraps client with special middleware\nassert($mapper-\u003eclient !== $client);\n```\n\n## Schema management\nTo get started you should describe your spaces, their format and indexes.\n```php\n$person = $mapper-\u003ecreateSpace('person', [\n    'engine' =\u003e 'memtx',\n    'if_not_exists' =\u003e true,\n]);\n\n// add properties - name, type and options\n$person-\u003eaddProperty('id', 'unsigned');\n$person-\u003eaddProperty('name', 'string');\n$person-\u003eaddProperty('birthday', 'unsigned');\n$person-\u003eaddProperty('gender', 'string', [\n    'default' =\u003e 'male'\n]);\n\n// indexes are created using fields array and optional index configuration\n$person-\u003eaddIndex(['name']);\n$person-\u003eaddIndex(['birthday'], ['unique' =\u003e true]);\n\n// index name is fields based, but you can specify any preffered one\n$person-\u003eaddIndex(['name', 'birthday'], [\n    'type' =\u003e 'hash',\n    'name' =\u003e 'name_with_birthday',\n]);\n\n/**\n * define format using properties\n */\nclass Tracker\n{\n    public int $id;\n    public int $reference;\n    public string $status;\n\n    public static function initSchema(\\Tarantool\\Mapper\\Space $space)\n    {\n        $space-\u003eaddIndex(['reference']);\n    }\n}\n\n$tracker = $mapper-\u003ecreateSpace('tracker');\n$tracker-\u003esetClass(Tracker::class);\n$tracker-\u003emigrate();\n\n/**\n * define format using constructor promotion\n */\nclass Policy\n{\n    public function __construct(\n        public int $id,\n        public string $nick,\n        public string $status,\n    ) {\n    }\n\n    public static function initialize(\\Tarantool\\Mapper\\Space $space)\n    {\n        $space-\u003eaddIndex(['nick'], ['unique' =\u003e true]);\n    }\n}\n\n$policy = $mapper-\u003ecreateSpace('policy', ['if_not_exists' =\u003e true]);\n$policy-\u003esetClass(Policy::class, 'initialize'); // use custom initialize method\n$policy-\u003emigrate();\n\n/**\n * mapper access using class name\n */\nclass Login\n{\n    public function __construct(\n        public int $id,\n        public string $username,\n        public string $password,\n    ) {\n    }\n\n    public static function initSchema(\\Tarantool\\Mapper\\Space $space)\n    {\n        $space-\u003eaddIndex(['username'], ['unique' =\u003e true]);\n    }\n}\n\n$nekufa = $mapper-\u003efindOrCreate(Login::class, ['username' = 'nekufa']);\n$mapper-\u003eupdate(Login::class, $nekufa, ['password' =\u003e password_hash(\"secret\")]);\n```\n\n## Working with the data\nNow you can store and retreive data from tarantool storage using mapper instance.\n```php\n// get space instance\n$persons = $mapper-\u003egetSpace('person');\n\n// create new entity\n$dmitry = $persons-\u003ecreate([\n    'id' =\u003e 1,\n    'name' =\u003e 'Dmitry'\n]);\n\n// create entities using mapper wrapper.\n// this way entity will be created and saved in the tarantool\n$vasily = $mapper-\u003ecreate('person', [\n    'id' =\u003e 2,\n    'name' =\u003e 'Vasily'\n]);\n\n// retreive entites by id using space\n$helloWorld = $mapper-\u003egetSpace('post')-\u003efindOne(['id' =\u003e 3]);\n\n// or using mapper wrapper\n$helloWorld = $mapper-\u003efindOne('post', ['id' =\u003e 3]);\n\n// pass client criteria object as well\n$criteria = Criteria::index('age')-\u003eandKey([18])-\u003eandGeIterator();\n$adults = $mapper-\u003efind('user', $criteria);\n\n// updates are easy\n$posts = $mapper-\u003egetSpace('post');\n$helloWorld = $posts-\u003eupdate($helloWorld, [\n    'title' =\u003e 'Hello world'\n]);\n\n// if you use instance classes, instance would be updated\n$policy = $mapper-\u003efindOrFail('policy', ['id' =\u003e 3]);\n$policy = $mapper-\u003eget('policy', 3); // getter shortcut\n$mapper-\u003eupdate('policy', $policy, [\n    'title' =\u003e 'updated title',\n]);\necho $policy-\u003etitle; // updated title\n\n// use client operations as well\nuse Tarantool\\Client\\Schema\\Operations;\n$mapper-\u003egetSpace('policy')-\u003eupdate($policy, Operations::add('counter', 1));\nvar_dump($policy-\u003ecounter); // actual value\n```\n\n## Schema Cache\nAny new mapper instance will fetch schema from the tarantool, this requests can takes a bit of database load.\\\nUse your favorite `psr/cache` implementation to persist schema on the application side.\\\nFor example, we use apcu adapter from `symfony/cache` package.\\\nIf new schema version is not persisted in cache, mapper will fetch it\n```php\nuse Symfony\\Component\\Cache\\Adapter\\ApcuAdapter;\n$cache = new ApcuAdapter();\n\n$mapper = new Mapper(Client::fromDefaults());\n$mapper-\u003ecache = $cache;\n$mapper-\u003egetSpace('_vspace'); // schema is fetched now\n\n$mapper = new Mapper(Client::fromDefaults());\n$mapper-\u003ecache = $cache;\n$mapper-\u003egetSpace('_vspace'); // no new requests are made\n```\n\n## Query Cache\nIf you don't want to repeat select queries you can inject cache interface implementation to space.\\\nUse your favorite psr/cache implementation to persist schema on the application side.\\\nFor example, we use array adapter from `symfony/cache` package.\n```php\nuse Symfony\\Component\\Cache\\Adapter\\ArrayAdapter;\n\n$mapper = new Mapper(Client::fromDefaults());\n$mapper-\u003egetSpace('_vspace')-\u003ecache = new ArrayAdapter(); // feel free to set default ttl\n\n$mapper-\u003efind('_vspace'); // query is executed\n$mapper-\u003efind('_vspace'); // results are fetched from cache\n$mapper-\u003efind('_vspace'); // results are fetched from cache\n```\n\n## Changes registration\nIn some cases you want to get all changes that were made during current session.\\\nBy default spy configuration is set to false, this improves performance a bit.\n```php\n$mapper-\u003espy = true;\n\n$nekufa = $mapper-\u003ecreate('user', ['login' =\u003e 'nekufa']);\n$firstPost = $mapper-\u003ecreate('post', [\n    'user_id' =\u003e $nekufa-\u003eid,\n    'title' =\u003e 'hello world',\n]);\n$mapper-\u003eupdate('post', $firstPost, ['title' =\u003e 'Final title']);\n\n// now there are two changes\n[$first, $second] = $mapper-\u003egetChanges();\necho $first-\u003etype; // insert\necho $first-\u003espace; // user\necho $first-\u003edata; // ['login' =\u003e 'nekufa']\n\n// all changes would be merged by space and key\n// this reduces changes duplicates\necho $second-\u003etype; // insert\necho $second-\u003espace; // post\necho $second-\u003edata; // ['user_id' =\u003e 1, 'title' =\u003e 'Final title']\n\n// of course you can flush all changes and start registration from scratch\n$mapper-\u003eflushChanges();\n```\n## Multiple connections\nIf you split your data across multiple tarantool instances you can use prefix based data api.\\\nApi is the same but you prefix space name with a connection prefix.\n```php\n$pool = new Pool(function (string $prefix) {\n    return new Mapper(Client::fromDsn('tcp://' . $prefix));\n});\n\n// connect to tarantool instance `volume` and find all timelines.\n$trackers = $pool-\u003efindOne('volume.timeline');\n\n$nekufa = $pool-\u003efindOrCreate('guard.login', ['username' =\u003e 'nekufa']);\n$pool-\u003eupdate('guard.login', $nekufa, ['locked_at' =\u003e time()]);\n\n// pool also wraps changes with the prefixes\necho $pool-\u003egetChanges()[0]-\u003espace; // guard.login\n\n// all expressions do the same behind the scenes\n$pool-\u003efind('flow.tracker', ['status' =\u003e 'active']);\n$pool-\u003egetMapper('flow')-\u003efind('tracker', ['status' =\u003e 'active']);\n$pool-\u003egetMapper('flow')-\u003egetSpace('tracker')-\u003efind(['status' =\u003e 'active']);\n```\n\n## Lua code delivery\nIproto usage is very powerful but sometimes is not enough.\\\nYou can easily execute lua code and pass local variables using associative array.\n\nIn addition, if you don't want to deliver it every request, use magic `call` method.\\\nWhen you use call method, mapper generates unique function name and creates it if it's not exist.\n```php\n// this method will always deliver and parse lua code on the tarantool side\n$mapper-\u003eevaluate('return a + b', ['a' =\u003e 2, 'b' =\u003e 7]); // 9\n\n// first call a function would be created with name evaluate_{BODYHASH}\n// there would be two requests - create function and call it\n$mapper-\u003ecall('return a + b', ['a' =\u003e 2, 'b' =\u003e 7]); // 9\n\n// second call will produce single request with function name and arguments\n$mapper-\u003ecall('return a + b', ['a' =\u003e 2, 'b' =\u003e 7]); // 9\n```\n\n## Migrations\nUse basic migration class to implement some logic before or after schema creation.\\\nPass migrations to mapper migrate method and that's all.\n```phpu\n\nuse Tarantool\\Mapper\\Migration;\nuse Tarantool\\Mapper\\Space;\n\nclass DropLegacySpaces extends Migration\n{\n    public function beforeSchema(Mapper $mapper)\n    {\n        $mapper-\u003ecall(\u003c\u003c\u003cLUA\n            if box.space.legacy then\n                box.space.legacy:drop()\n                box.space.legacy_detail:drop()\n            end\n        LUA);\n    }\n}\nclass InitializeData extends Migration\n{\n    public function afterSchema(Mapper $mapper)\n}\n$mapper = $container-\u003eget(Mapper::class);\n\n// also migrate accepts migration instance, or migration class arrays\n$mapper-\u003emigrate(DropLegacySpaces::class, InitializeData::class);\n```\n\n## Performance\nWe can calculate mapper overhead using getInstance method that is called per each instance.\\\nIf you don't use cache, there is single schema fetch on connection and each time schema is upgraded.\\\nPerfomance test was made on (AMD Ryzen 5 3600X), Ubuntu 23.10  using PHP 8.3.6\n\n| Instance type | Instances per second |\n| --- | --- |\n| constructor | 4 664 172 |\n| properties | 4 328 442 |\n| simple array | 11 983 040 |\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool-php%2Fmapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarantool-php%2Fmapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool-php%2Fmapper/lists"}