{"id":17067751,"url":"https://github.com/rican7/incoming","last_synced_at":"2025-10-09T12:32:52.939Z","repository":{"id":27474033,"uuid":"30953406","full_name":"Rican7/incoming","owner":"Rican7","description":"Transform loose and complex input into consistent, strongly-typed data structures","archived":false,"fork":false,"pushed_at":"2018-04-17T16:10:51.000Z","size":152,"stargazers_count":219,"open_issues_count":0,"forks_count":7,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-29T08:07:51.024Z","etag":null,"topics":["deserialize","hydrate","inflate","marshall","php","transformations","unmarshall"],"latest_commit_sha":null,"homepage":"https://github.com/Rican7/incoming","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/Rican7.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-18T05:27:04.000Z","updated_at":"2024-07-22T08:12:20.000Z","dependencies_parsed_at":"2022-08-21T10:10:49.758Z","dependency_job_id":null,"html_url":"https://github.com/Rican7/incoming","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rican7%2Fincoming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rican7%2Fincoming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rican7%2Fincoming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rican7%2Fincoming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rican7","download_url":"https://codeload.github.com/Rican7/incoming/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312078,"owners_count":20918344,"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":["deserialize","hydrate","inflate","marshall","php","transformations","unmarshall"],"created_at":"2024-10-14T11:11:33.357Z","updated_at":"2025-10-09T12:32:47.892Z","avatar_url":"https://github.com/Rican7.png","language":"PHP","readme":"# Incoming\n\n[![Build Status](https://img.shields.io/travis/Rican7/incoming.svg?style=flat)](https://travis-ci.org/Rican7/incoming)\n[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Rican7/incoming.svg?style=flat)](https://scrutinizer-ci.com/g/Rican7/incoming/)\n[![Quality Score](https://img.shields.io/scrutinizer/g/Rican7/incoming.svg?style=flat)](https://scrutinizer-ci.com/g/Rican7/incoming/)\n[![Latest Stable Version](https://img.shields.io/github/release/Rican7/incoming.svg?style=flat)](https://github.com/Rican7/incoming/releases)\n\nIncoming is a PHP library designed to simplify and abstract the transformation of loose, complex input data into\nconsistent, strongly-typed data structures.\n\nBorn out of inspiration from using [Fractal][fractal-lib-website], **Incoming** can be seen as a spiritual inversion.\nWhen working with data models of any kind (database, remote service, etc), it can be a huge pain to take raw input data\nand turn it into anything usable. Even worse is when something changes and you have to duplicate code or try and keep\nbackwards compatibility. Incoming is here to make all this easier while enabling you to create more concern-separated,\nreusable, and testable code.\n\n\"Wait, what? Why not just use 'x' or 'y'?\" [Don't worry, I've got you covered.](#wait-what-why-not-just-use-x-or-y)\n\n\n## Features\n\n - Input filtering and transforming\n - Built-in powerful, immutable data-structures for handling complex input\n - Allows for automatic hydrator-for-model resolution via factory abstraction\n - Makes strong use of interfaces for well structured, easily-testable code\n - Completely configurable via composable units\n\nStill curious? Check out the [examples](#examples).\n\n\n## Installation\n\n1. [Get Composer][composer-website]\n2. Add **\"incoming/incoming\"** to your dependencies: `composer require incoming/incoming`\n3. Include the Composer autoloader `\u003c?php require 'vendor/autoload.php';`\n\n\n## Examples\n\nThe easiest example to relate to in the PHP world? \"Form\" or HTTP request data:\n\n```php\nclass UserHydrator implements Incoming\\Hydrator\\Hydrator\n{\n    public function hydrate($input, $model)\n    {\n        $model-\u003esetName($input['name']);\n        $model-\u003esetGender($input['gender']);\n        $model-\u003esetFavoriteColor($input['favorite_color']);\n\n        return $model;\n    }\n}\n\n// Create our incoming processor\n$incoming = new Incoming\\Processor();\n\n// Process our raw form/request input into a User model\n$user = $incoming-\u003eprocessForModel(\n    $_POST,            // Our HTTP form-data array\n    new User(),        // Our model to hydrate\n    new UserHydrator() // The hydrator above\n);\n\n// Validate and save the user\n// ...\n```\n\nSure, that's a pretty contrived example. But what kind of power can we gain when we compose some pieces together?\n\n```php\nclass BlogPostHydrator implements Incoming\\Hydrator\\ContextualHydrator\n{\n    const USER_CONTEXT_KEY = 'user';\n\n    public function hydrate($input, $model, Map $context = null)\n    {\n        $model-\u003esetBody($input['body']);\n        $model-\u003esetCategories($input['categories']);\n        $model-\u003esetTags($input['tags']);\n\n        // Only allow admin users to publish posts\n        if (null !== $context \u0026\u0026 $context-\u003eexists(self::USER_CONTEXT_KEY)) {\n            $user = $context-\u003eget(self::USER_CONTEXT_KEY);\n\n            $model-\u003esetAuthor($user-\u003egetName());\n\n            if ($user-\u003eisAdmin()) {\n                $model-\u003esetPublished($input['published']);\n            }\n        }\n\n        return $model;\n    }\n}\n\n// Create our incoming processor\n$incoming = new Incoming\\Processor();\n\n// Create a context for the hydrator with active data\n$context = Map::fromArray([\n    BlogPostHydrator::USER_CONTEXT_KEY =\u003e $this-\u003egetCurrentUser() // A user context\n]);\n\n// Process our raw form/request input to update our BlogPost model\n$post = $incoming-\u003eprocessForModel(\n    $_POST,                      // Our HTTP form-data array\n    BlogPost::find($_GET['id']), // Fetch our blog post to update and pass it in\n    new BlogPostHydrator(),      // The hydrator above\n    $context                     // Context data to enable more powerful conditional processing\n);\n\n// Validate and save the blog post\n// ...\n```\n\nLet's try and filter our input first.\n\n```php\nclass SpecialCharacterFilterTransformer implements Incoming\\Transformer\\Transformer\n{\n    public function transform($input)\n    {\n        $transformed = [];\n\n        foreach($input as $key =\u003e $string) {\n            $transformed[$key] = filter_var($string, FILTER_SANITIZE_STRING);\n        }\n\n        return $transformed;\n    }\n}\n\nclass UserHydrator implements Incoming\\Hydrator\\Hydrator\n{\n    // Same as previous examples...\n}\n\n// Create our incoming processor\n$incoming = new Incoming\\Processor(\n    new SpecialCharacterFilterTransformer()\n);\n\n// Process our raw form/request input into a User model\n$user = $incoming-\u003eprocessForModel(\n    $_POST,            // Our HTTP form-data array\n    new User(),        // Our model to hydrate\n    new UserHydrator() // The hydrator above\n);\n\n// Validate and save the user\n// ...\n```\n\nMissing type hints? PHP's type-system's restrictions can be circumvented!:\n\n```php\nclass UserHydrator extends Incoming\\Hydrator\\AbstractDelegateHydrator\n{\n    // Boom! Type-hintable arguments!\n    // (For more info, see the `AbstractDelegateHydrator` class doc-block)\n    public function hydrateModel(Incoming\\Structure\\Map $input, User $model)\n    {\n        $model-\u003esetName($input['name']);\n        // ...\n\n        return $model;\n    }\n}\n\n// Create our incoming processor\n$incoming = new Incoming\\Processor();\n\n// Process our raw form/request input into a User model\n$user = $incoming-\u003eprocessForModel(\n    $_POST,            // Our HTTP form-data array\n    new User(),        // Our model to hydrate\n    new UserHydrator() // The hydrator above\n);\n\n// Validate and save the user\n// ...\n```\n\nImmutable objects or objects with required data in the constructor? No problem!\n\n```php\nclass User\n{\n    private $first_name;\n    private $last_name;\n\n    public function __construct(string $first_name, string $last_name)\n    {\n        $this-\u003efirst_name = $first_name;\n        $this-\u003elast_name = $last_name;\n    }\n}\n\nclass UserBuilder extends Incoming\\Hydrator\\AbstractDelegateBuilder\n{\n    public function buildModel(Incoming\\Structure\\Map $input): User\n    {\n        return new User($input['first_name'], $input['last_name']);\n    }\n}\n\n// Create our incoming processor\n$incoming = new Incoming\\Processor();\n\n// Process our raw form/request input into a new User model\n$user = $incoming-\u003eprocessForType(\n    $_POST,            // Our HTTP form-data array\n    User::class,       // Our type to build\n    new UserBuilder()  // The builder above\n);\n\n// Validate and save the user\n// ...\n```\n\n\n## Wait, what? Why not just use \"x\" or \"y\"?\n\nStill not sold on the idea even with the provided [examples](#examples)? You may be thinking...\n\n- \"Why not just use `MyModelName::fromArray($data)`?\"\n- \"But my ORM already has a `$model-\u003efill($data)` method...\"\n- \"I don't get it...\"\n\nYea, sure, you could easily just build a model from a raw data array or just pass an array of attributes to a \"fill\"\nmethod and hope that everything goes well, but there's a few issues with doing that: What happens when you refactor a\nmodel or an underlying database table? Do you all of a sudden break backwards compatibility in an HTTP API's parameters\njust because your table might have changed? What about if you want to prevent certain parameters from being changed in a\nconditional manner? Do you just create a massive chunk of `if` statements in a factory method of the model itself?\n\nThe idea with using **Incoming** is to separate concerns, create reusable and composable units, and really enrich an\napplication's ability to create complex entities while providing a convenient API that rids of some of PHP's \"gotchas\".\n\nAfter all, \"magical\" solutions like mass attribute assignment have [had their pitfalls before][rails-gh-5228]. ;)\n\n\n# License\n\n**Incoming** is proud to be [MIT licensed][license-file].\n\n\n\n[fractal-lib-website]: http://fractal.thephpleague.com/\n[composer-website]: https://getcomposer.org/\n[rails-gh-5228]: https://github.com/rails/rails/issues/5228\n[license-file]: LICENSE\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frican7%2Fincoming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frican7%2Fincoming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frican7%2Fincoming/lists"}