{"id":23037141,"url":"https://github.com/it-bens/object-transformer","last_synced_at":"2025-04-02T23:18:01.910Z","repository":{"id":46698737,"uuid":"408582537","full_name":"it-bens/object-transformer","owner":"it-bens","description":null,"archived":false,"fork":false,"pushed_at":"2023-12-15T14:49:10.000Z","size":37,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T03:46:19.293Z","etag":null,"topics":[],"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/it-bens.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":"2021-09-20T19:57:54.000Z","updated_at":"2022-11-02T19:50:19.000Z","dependencies_parsed_at":"2023-01-22T21:31:21.184Z","dependency_job_id":null,"html_url":"https://github.com/it-bens/object-transformer","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-bens%2Fobject-transformer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-bens%2Fobject-transformer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-bens%2Fobject-transformer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-bens%2Fobject-transformer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/it-bens","download_url":"https://codeload.github.com/it-bens/object-transformer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246905869,"owners_count":20852820,"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":[],"created_at":"2024-12-15T17:29:25.504Z","updated_at":"2025-04-02T23:18:01.892Z","avatar_url":"https://github.com/it-bens.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Object Transformer\n\n![Maintenance Status](https://img.shields.io/badge/Maintained%3F-yes-green.svg)\n[![Build Status](https://app.travis-ci.com/it-bens/object-transformer.svg?branch=master)](https://app.travis-ci.com/it-bens/object-transformer)\n[![Coverage Status](https://coveralls.io/repos/github/it-bens/object-transformer/badge.svg?branch=master)](https://coveralls.io/github/it-bens/object-transformer?branch=master)\n\n## How to install the package?\nThe package can be installed via Composer:\n```bash\ncomposer require it-bens/object-transformer\n```\nIt requires at least PHP 8, but no other extensions or packages.\n\n## How to use the Object Transformer?\nFirst, at least one implementation of the `TransformerInterface` has to be created.\n```php\nuse ITB\\ObjectTransformer\\TransformerInterface;\n\nclass OptimusPrime implements TransformerInterface \n{\n    public static function supportedTransformations(): array\n    {\n        return [['input' =\u003e MissionCity::class, 'output' =\u003e Ruins::class]];\n    }\n    \n    public function transform(TransformationEnvelope $envelope, string $outputClassName): object\n    {\n        // This method performs the actual transformation and returns the resulting object.\n    }    \n}\n\nclass Megatron implements TransformerInterface \n{\n    public static function supportedTransformations(): array\n    {\n        return [['input' =\u003e SamWitwicky::class, 'output' =\u003e Corpse::class]];\n    }\n    \n    public function transform(TransformationEnvelope $envelope, string $outputClassName): object {...}\n}\n```\nThe input- and output class names are used to register the supported transformations in the `TransformationMediator`.\n\nThe `TransformationMediator` requires an iterable of `TransfromerInterface`-implementing objects (at least one).\n```php\nuse ITB\\ObjectTransformer\\TransformationMediator;\n\n$mediator = new TransformationMediator(new \\ArrayObject([new OptimusPrime(), new Megatron()]));\n```\n\nBecause the `TransformerInterface` objects are processed at the first call of the `transform` method of the `TransformationMediator`,\nthe `TranformationMediator` (or the `TransformationMediatorInterface`) can be passed as constructor argument to the transformer \n(otherwise, this could lead to endless circle calls).\n\nAfter everything is prepared, the `transform` method can be used.\n```php\n$object1 = new Object1('The hell am I doing here?');\n$object2 = $mediator-\u003etransform($object1, Object2::class);\n\n// Explicit envelope usage\n$object2 = $mediator-\u003etransform(TransformationEnvelope::wrap($object1), Object2::class);\n```\nThe `transform` method of the `TransformationMediator` can handle any object. If the passed object isn't a `TransformationEnvelope`,\nit will be wrapped with one. So if you don't want to use any stamps (see below), just pass the ordinary object.\nBut be aware, that you will always receive a `TransformationEnvelope` in your implementations of `TransformerInterface?`.\n\n## What do you mean with \"stamp\"?\nThe envelope and stamp system is inspired, but not identical to the system used by the Symfony messenger component.\nEvery object that is passed to the `transform` method of the `TransformationMediator` is wrapped with an `TransformationEnvelope`\n(if it's not already one).\n\nLike a real envelope, the `TransformationEnvelope` can carry stamps. Stamps have two tasks:\n1. pass data to the `transform` method of the `TransformerInterface` implementation\n2. provide data that can be used during processing in the `TransformationMediator`\n\nAll stamps have to implement the `TransformationStampInterface` and provide a priority.\n\nThe main difference to the Symfony messenger component is, that the envelope can only hold one stamp per type.\nIf two or more stamps of the same type are passed to the envelope, a later stamp will overwrite an earlier one, \nif it's priority is higher.\n\n### Looping data through the mediator\nTo loop data through the mediator to the transformer, any custom stamp can be passed to the envelope. \nThey won't be touched during processing and are accessible via the envelope:\n```php\npublic function transform(TransformationEnvelope $envelope, string $outputClassName): object\n{\n    $customStamp = $envelope-\u003egetStamp(CustomStampClass::class); // returns null if the envelope contains no such stamp\n}   \n```\n\n### Data processed by the mediator\nAll implementations of the `TransformationStampInterface` provided by this package are used inside the mediator.\nAfter there usage they are removed from the envelope and are not accessible in the `TransformerInface` implementation.\n\n#### InputClassStamp\nBecause of its internal data flow, the passed input object has to be of the exact same class\nthat was defined as input in the `supportedTransformations` method of the factory.\n\nThis could lead to problems with packages like Doctrine. Doctrine creates proxy classes for managed entities,\nthat can be used just like the Entity itself. However, the `TransformationMediator` would not find a matching transformer\nand throw an exception, because the exact class of the proxy object is not registered for transformation.\n\nThat's where the `InputClassStamp` comes into play. Let's define some objects first.\n```php\nclass Object1\n{\n    public $someString;\n    public function __construct($someString) { $this-\u003esomeString = $someString; }\n}\n\nclass Object2\n{\n    public $letterCount;\n    public function __construct($letterCount) { $this-\u003eletterCount = $letterCount; }\n}\n\nclass Object3 extends Object1\n{\n}\n```\nThe following lines would lead to an `UnsupportedInputOutputTypes` exception.\n```php\n$object3 = new Object3('The hell am I doing here?');\n$result = $mediator-\u003etranform($object3, Object2::class);\n```\nWith the `InputClassStamp` it's working.\n```php\nuse ITB\\ObjectTransformer\\TransformationMediator;\n\n$envelope = new \\ITB\\ObjectTransformer\\TransformationEnvelope(\n    new Object3('The hell am I doing here?'),\n    [new InputClassStamp(Object1::class)]\n);\n$result = $mediator-\u003etranform($envelope, Object2::class);\n```\n\n## Why does this package exist?\nA common pattern I stumbled across in my projects is to map data between different objects types like DTOs and Entities.\n\nThe mapping is often very simple: the value of the DTO (Data Transfer Object) property \nis the same as the property of the Entity (and vice versa).\nHowever, things get more complicated if value objects are used (which I strongly recommend).\nNew objects has to be created and the few lines of code get more and more complex.\n\nIn my projects, this often lead to the creation of factory classes, that handle the object transformation \nand contain as little business logic as possible. But, as a class should only serve a single purpose, \nthere can be a lot of such factories. When engineering more complex entities, the factories sometimes depend on each other.\nThat makes the DI (Dependency Injection) or Singleton usage quite a mess.\n\nThat's where the object transformer comes into play: the mediator is sufficient to do all the transformations needed.\n\n## How does the Object Transformer work?\nA factory or any other class that should be used by the `TransformationMediator` \nhas to implement the `TransformerInterface`.\n\nAll classes that implement the interface (and are passed to the mediator) provide an array of supported transformations\nvia the static `supportedTransformations` method. The array contains an array for every supported transformation.\nEvery one of these inner arrays requires an `input` and an `ouput` key, which both represent existing classes.\n\nWhen the `transform` method of the `TransformationMediator` is first called, it will populate it's internal transformer registry.\nFor performance reasons every supported transformation is registered with it's input and output class.\nThis way, the associations can be used to find a responsible transformer.\n\n## Contributing\nI am really happy that the software developer community loves Open Source, like I do! ♥\n\nThat's why I appreciate every issue that is opened (preferably constructive) \nand every pull request that provides other or even better code to this package.\n\nYou are all breathtaking!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fit-bens%2Fobject-transformer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fit-bens%2Fobject-transformer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fit-bens%2Fobject-transformer/lists"}