{"id":25387107,"url":"https://github.com/stratadox/hydrationmapping","last_synced_at":"2025-04-09T18:41:25.240Z","repository":{"id":57060375,"uuid":"110827239","full_name":"Stratadox/HydrationMapping","owner":"Stratadox","description":"Mappings for hydration purposes","archived":false,"fork":false,"pushed_at":"2020-11-23T18:48:30.000Z","size":413,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-14T10:17:50.043Z","etag":null,"topics":["converter","hydrate","hydration","hydrator","mapper","mapping","mappings","orm","php7"],"latest_commit_sha":null,"homepage":null,"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/Stratadox.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}},"created_at":"2017-11-15T11:53:38.000Z","updated_at":"2020-11-23T18:48:33.000Z","dependencies_parsed_at":"2022-08-24T07:30:44.580Z","dependency_job_id":null,"html_url":"https://github.com/Stratadox/HydrationMapping","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stratadox%2FHydrationMapping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stratadox%2FHydrationMapping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stratadox%2FHydrationMapping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stratadox%2FHydrationMapping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Stratadox","download_url":"https://codeload.github.com/Stratadox/HydrationMapping/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248089865,"owners_count":21045981,"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":["converter","hydrate","hydration","hydrator","mapper","mapping","mappings","orm","php7"],"created_at":"2025-02-15T11:26:24.457Z","updated_at":"2025-04-09T18:41:25.213Z","avatar_url":"https://github.com/Stratadox.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hydration Mapping\n\n[![Build Status](https://circleci.com/gh/Stratadox/HydrationMapping.svg?style=shield)](https://app.circleci.com/pipelines/github/Stratadox/HydrationMapping)\n[![codecov](https://codecov.io/gh/Stratadox/HydrationMapping/branch/master/graph/badge.svg)](https://codecov.io/gh/Stratadox/HydrationMapping)\n[![Infection Minimum](https://img.shields.io/badge/min_msi-70-green.svg)](https://app.circleci.com/pipelines/github/Stratadox/Hydrator)\n[![PhpStan Level](https://img.shields.io/badge/phpstan-8-brightgreen.svg)](https://travis-ci.org/Stratadox/HydrationMapping)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Stratadox/HydrationMapping/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Stratadox/HydrationMapping/?branch=master)\n[![Maintainability](https://api.codeclimate.com/v1/badges/cc2585ce303967dd4c7d/maintainability)](https://codeclimate.com/github/Stratadox/HydrationMapping/maintainability)\n[![Latest Stable Version](https://poser.pugx.org/stratadox/hydration-mapping/v/stable)](https://packagist.org/packages/stratadox/hydration-mapping)\n[![License](https://poser.pugx.org/stratadox/hydration-mapping/license)](https://packagist.org/packages/stratadox/hydration-mapping)\n\n[![Implements](https://img.shields.io/badge/inferfaces-github-blue.svg)](https://github.com/Stratadox/HydrationMappingContracts)\n[![Latest Stable Version](https://poser.pugx.org/stratadox/hydration-mapping-contracts/v/stable)](https://packagist.org/packages/stratadox/hydration-mapping-contracts)\n[![License](https://poser.pugx.org/stratadox/hydration-mapping-contracts/license)](https://packagist.org/packages/stratadox/hydration-mapping-contracts)\n\nMappings for hydration purposes.\n\nMaps array or array-like data structures to object properties, in order to \nassemble the objects that model a business domain.\n\n## Installation\n\nInstall using composer:\n\n`composer require stratadox/hydration-mapping`\n\n## Purpose\n\nThese mapping objects define the relationship between an object property and the\nsource of the data.\n\n## Typical Usage\n\nTypically, hydration mappings are given to [`Mapped`](https://github.com/Stratadox/Hydrator/blob/master/src/Mapping.php)[`Hydrator`](https://github.com/Stratadox/Hydrator) instances.\nTogether they form a strong team that solves a single purpose: mapping data to an object graph.\n\nFor example:\n```php\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\IntegerValue;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\StringValue;\nuse Stratadox\\Hydrator\\MappedHydrator;\nuse Stratadox\\Hydrator\\ObjectHydrator;\n\n$hydrator = MappedHydrator::using(\n    ObjectHydrator::default(),\n    StringValue::inProperty('title'),\n    IntegerValue::inProperty('rating'),\n    StringValue::inPropertyWithDifferentKey('isbn', 'id')\n);\n\n$book = new Book;\n$hydrator-\u003ewriteTo($book, [\n    'title'  =\u003e 'This is a book.',\n    'rating' =\u003e 3,\n    'isbn'   =\u003e '0000000001'\n]);\n```\n\nMore often, the mapped hydrator is given to a [`deserializer`](https://github.com/Stratadox/Deserializer):\n```php\nuse Stratadox\\Deserializer\\ObjectDeserializer;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\IntegerValue;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\StringValue;\nuse Stratadox\\Hydrator\\MappedHydrator;\nuse Stratadox\\Hydrator\\ObjectHydrator;\nuse Stratadox\\Instantiator\\ObjectInstantiator;\n\n$deserialize = ObjectDeserializer::using(\n    ObjectInstantiator::forThe(Book::class),\n    MappedHydrator::using(\n        ObjectHydrator::default(),\n        StringValue::inProperty('title'),\n        IntegerValue::inProperty('rating'),\n        StringValue::inPropertyWithDifferentKey('isbn', 'id')\n    )\n);\n\n$book = $deserialize-\u003efrom([\n   'title'  =\u003e 'This is a book.',\n   'rating' =\u003e 3,\n   'isbn'   =\u003e '0000000001'\n]);\n```\n\n## Mapping\n\nThree types of property mappings are available:\n- Scalar mappings\n- Relationship mappings\n- Extension points\n\n### Scalar Mapping\n\nScalar typed properties can be mapped using the `*Value` classes.\nThe following scalar mappings are available:\n- `BooleanValue`\n- `FloatValue`\n- `IntegerValue`\n- `StringValue`\n- `NullValue`\n\nScalar mappings are created through the named constructors:\n- `inProperty`\n    - Usage: `IntegerValue::inProperty('amount')` \n    - Use when the property name and data key are the same.\n- `inPropertyWithDifferentKey`\n    - Usage: `BooleanValue::inPropertyWithDifferentKey('isBlocked', 'is_blocked')`\n    - Use when the data key differs from the property name.\n\n#### Basic Validation\n\nWhen appropriate, these mappings validate the input before producing a value.\nFor instance, the `IntegerValue` mapping checks that:\n- The input value is formatted as an integer number\n- The value does not exceed the integer boundaries\n\nThis process can be skipped by using the `Casted*` mappings instead.\nThey provide a minor speed bonus at the cost of decreased integrity.\n`Casted*` mappings are available as:\n- `CastedFloat`\n- `CastedInteger`\n\nTo skip the entire typecasting process, the `OriginalValue` mapping can be used.\n\nInput to a `BooleanValue` must either be 0, 1 or already boolean typed.\nCustom true/false values can be provided as optional parameters:\n```php\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\BooleanValue;\n\n$myProperty = BooleanValue::withCustomTruths('foo', ['yes', 'y'], ['no', 'n']);\n```\n\n#### Nullable- and Mixed values\n\nEach of the above mappings can be made *nullable* by wrapping the mapping with\n`CanBeNull`.\n\nFor example, instead of `IntegerValue::inProperty('foo')`, the `foo` property \ncan be made *nullable* with: `CanBeNull::or(IntegerValue::inProperty('foo'))`.\n\nIn the same style, mixed value types can be configured. To map a value that \ncould be either an int or a float, as numeric PHP values are often found, \n`CanBeInteger` can be used: `CanBeInteger::or(FloatValue::inProperty('foo')))`.\nThis mapping will first check if the value can safely be transformed into an\ninteger, and fall back to a floating point value. Non-numeric values will result\nin an exception, denoting where and why the input data could not be mapped.\n\nThese mixed mapping can be combined (as is customary for [decorators](https://sourcemaking.com/design_patterns/decorator))\nto produce, for instance, mapping configurations that first attempt to map the \nvalue to a boolean, otherwise as an integer, if that cannot be done to cast it \nto a floating point, and if all else fails, make it a string:\n```php\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\CanBeBoolean;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\CanBeInteger;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\CanBeFloat;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\StringValue;\n\n$theProperty = CanBeBoolean::orCustom(\n    CanBeInteger::or(\n        CanBeFloat::or(\n            StringValue::inProperty('bar')\n        )\n    ), ['TRUE'], ['FALSE']\n);\n```\n\n### Relationship Mapping\n\nRelationships can be mapped with a monogamous `HasOne*` or polygamist `HasMany*` \nmapping.\n\nEach of these are connected to the input data in one of three ways:\n- As `*Embedded` values (for loading from tabular data)\n- As `*Nested` data structures (for loading from a json structure)\n- As `*Proxies` (for loading lazily)\n\nThis boils down to the following possibilities:\n- `HasManyNested`\n- `HasManyProxies`\n- `HasOneEmbedded`\n- `HasOneNested`\n- `HasOneProxy`\n\nRelationship mappings are created through the named constructors:\n- `inProperty`\n    - Usage:\n    `HasOneNested::inProperty('name', $deserializer)` \n    - Use when the property name and data key are the same.\n- `inPropertyWithDifferentKey`\n    - Usage: \n    `HasOneNested::inPropertyWithDifferentKey('friends', 'contacts', $deserializer)`\n    - Use when the data key differs from the property name.\n\nIn this context, the term `key` refers to the key of the associative array from\nwhich the object data is mapped, also known as `offset`, `index` or `position`.\n\n#### Nested vs Embedded\n\nFor `*Embedded` classes, there is no `inPropertyWithDifferentKey`. Instead of\nrelying on an embedded array in the key, they are given the original input array\nand compose their attributes from one or more of its values.\n\n##### Has One\n\n`HasOne*`-type relationships are each given an object that [`Deserializes`](https://github.com/Stratadox/DeserializerContracts/blob/master/src/Deserializes.php) \nthe related instance.\n\nA `HasOneNested` receives the value that was found in the original input for the \ngiven `key`. This value must be an array, presumably associative.\n\n`HasOneEmbedded` mappings take a different approach: they produce a new object\nfrom the data in the original input array. This approach is useful when mapping,\nfor example, [embedded values](https://martinfowler.com/eaaCatalog/embeddedValue.html).\n\n##### Has Many\n\nA `HasMany*` relation requires one object that `Deserializes` the collection, \nand one that `Deserializes` the items.\n\nThis approach allows for a lot of freedom in the way collections are mapped.\nThe available [deserializers](https://github.com/Stratadox/Deserializer) can map \nthe collection either as plain array or to a custom collection object.\n\nThese deserializers may in turn use mapped hydrator instances. The combination  \nis able to map entire structures of objects in all kinds and shapes.\n\n##### Proxies\n\n[`Proxies`](https://github.com/Stratadox/Proxy) are used to allow for lazy \nloading. Rather than deserializers, they take a factory to create objects that, \nin turn, load the \"real\" object in place of the proxy whenever called upon.\n\nLazy has-one relations can be mapped with the `HasOneProxy` mapping.\nLazy has-many relationships have the option to be normally lazy, or extra lazy.\nFor extra lazy relations, the `HasManyProxies` mapping is used. When the \nrelation is \"regular\" lazy, it is mapped as `HasOneProxy`, where \"one\" refers to\none collection.\n\nThe latter only works when the collection is contained in a collection object.\nIn cases where objects that are contained in an array should be lazy-loaded, a\n`HasManyProxies` mapping should be used, where each proxy is configured to load\nthe entire array when called upon. \n\nUsing this mechanism, both lazy and extra-lazy loading is supported through any \ntype of collection, whether it be an array or a collection object.\n\n#### Bidirectional\n\nBidirectional `one-to-many` and `one-to-one` relationships can be mapped using \nthe `HasBackReference` mapping.\n\nThis mapping acts as an observer to the hydrator for the owning side, assigning\nthe reference of the \"owner\" object to the given property.\n\n### Advanced validation\n\nAdvanced input validation can be applied with a `ConstrainedMapping`. \nA `ConstrainedMapping` will produce the value of the mapping if the \n[specification](https://github.com/Stratadox/Specification) is satisfied with \nit, or throw an exception otherwise.\n\nFor example, a check on whether a rating is between 1 and 5 might look like this:\n```php\nuse Stratadox\\Hydration\\Mapping\\Composite\\ConstrainedMapping;\nuse Stratadox\\Hydration\\Mapping\\Simple\\Type\\IntegerValue;\nuse Your\\Constraint\\IsNotLess;\nuse Your\\Constraint\\IsNotMore;\n\nConstrainedMapping::checkThatIt(\n    IsNotLess::than(1)-\u003eand(IsNotMore::than(5)),\n    IntegerValue::inProperty('rating')\n);\n```\nThe constraints themselves implement the (minimal) interface [`Satisfiable`](https://github.com/Stratadox/SpecificationInterfaces/blob/master/src/Satisfiable.php),\nwhich mandates only the method `isSatisfiedBy($input)`.\n\nThe recommended way to implement custom constraints is by extending the abstract \n[`Specification`](https://github.com/Stratadox/Specification/blob/master/src/Specification.php) class:\n```php\nuse Stratadox\\Specification\\Specification;\n\nclass IsNotLess extends Specification\n{\n    private $minimum;\n\n    private function __construct(int $minimum)\n    {\n        $this-\u003eminimum = $minimum;\n    }\n\n    public static function than(int $minimum): self\n    {\n        return new self($minimum);\n    }\n\n    public function isSatisfiedBy($number): bool\n    {\n        return $number \u003e= $this-\u003eminimum;\n    }\n}\n```\nOr by using the [`Specifying`](https://github.com/Stratadox/Specification/blob/master/src/Specifying.php)\ntrait:\n```php\nuse Stratadox\\Specification\\Contract\\Specifies;\nuse Stratadox\\Specification\\Specifying;\n\nclass IsNotMore implements Specifies\n{\n    use Specifying;\n\n    private $maximum;\n\n    private function __construct(int $maximum)\n    {\n        $this-\u003emaximum = $maximum;\n    }\n\n    public static function than(int $maximum): self\n    {\n        return new self($maximum);\n    }\n\n    public function isSatisfiedBy($number): bool\n    {\n        return $number \u003c= $this-\u003emaximum;\n    }\n}\n```\n\n### Default values\n\nTo honour the PHP spirit, a class is available that loads a default value rather\nthan propagating the exception: `Defaults::to(-1, IntegerValue::inProperty('foo'))`\n\n### Extension\n\nThe `ClosureMapping` provides an easy extension point.\nIt takes in an anonymous function as constructor parameter.\nThis function is called with the input data to produce the mapped result.\n\nFor additional extension power, custom mapping can be produced by implementing \nthe `Mapping` interface.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstratadox%2Fhydrationmapping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstratadox%2Fhydrationmapping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstratadox%2Fhydrationmapping/lists"}