All Projects → Rican7 → Incoming

Rican7 / Incoming

Licence: mit
Transform loose and complex input into consistent, strongly-typed data structures

Projects that are alternatives of or similar to Incoming

Osom
An Awesome [/osom/] Object Data Modeling (Database Agnostic).
Stars: ✭ 68 (-70.56%)
Mutual labels:  transformations
Tracks
Programming with shapes
Stars: ✭ 151 (-34.63%)
Mutual labels:  transformations
Kazaam
Arbitrary transformations of JSON in Golang
Stars: ✭ 184 (-20.35%)
Mutual labels:  transformations
Serverless Sharp
Serverless image optimizer for S3, Lambda, and Cloudfront
Stars: ✭ 102 (-55.84%)
Mutual labels:  transformations
Pytransform3d
3D transformations for Python
Stars: ✭ 133 (-42.42%)
Mutual labels:  transformations
Elm Geometry
2D/3D geometry package for Elm
Stars: ✭ 162 (-29.87%)
Mutual labels:  transformations
Optimus
🤖 Id obfuscation based on Knuth's multiplicative hashing method for PHP.
Stars: ✭ 1,084 (+369.26%)
Mutual labels:  transformations
Trimesh2
C++ library and set of utilities for input, output, and basic manipulation of 3D triangle meshes
Stars: ✭ 206 (-10.82%)
Mutual labels:  transformations
Bull
BULL - Bean Utils Light Library
Stars: ✭ 150 (-35.06%)
Mutual labels:  transformations
Transform
A polyglot web converter.
Stars: ✭ 2,842 (+1130.3%)
Mutual labels:  transformations
Hm
Idiomatic Ruby hash transformations
Stars: ✭ 121 (-47.62%)
Mutual labels:  transformations
Filestack React
Official React component for Filestack - API and content management system that makes it easy to add powerful file uploading and transformation capabilities to any web or mobile application.
Stars: ✭ 131 (-43.29%)
Mutual labels:  transformations
Transmogrifai
TransmogrifAI (pronounced trăns-mŏgˈrə-fī) is an AutoML library for building modular, reusable, strongly typed machine learning workflows on Apache Spark with minimal hand-tuning
Stars: ✭ 2,084 (+802.16%)
Mutual labels:  transformations
Ffimageloading
Image loading, caching & transforming library for Xamarin and Windows
Stars: ✭ 1,288 (+457.58%)
Mutual labels:  transformations
Robopy
Robopy is a python port for Robotics Toolbox in Matlab created by Peter Corke
Stars: ✭ 186 (-19.48%)
Mutual labels:  transformations
Stetl
Stetl, Streaming ETL, is a lightweight geospatial processing and ETL framework written in Python.
Stars: ✭ 64 (-72.29%)
Mutual labels:  transformations
Remixautoml
R package for automation of machine learning, forecasting, feature engineering, model evaluation, model interpretation, data generation, and recommenders.
Stars: ✭ 159 (-31.17%)
Mutual labels:  transformations
Slow Cheetah
Tooling for XML and JSON file transforms on build from Visual Studio and MSBuild
Stars: ✭ 227 (-1.73%)
Mutual labels:  transformations
Tt
a Pythonic toolkit for working with Boolean expressions
Stars: ✭ 206 (-10.82%)
Mutual labels:  transformations
Scramjet
Simple yet powerful live data computation framework
Stars: ✭ 171 (-25.97%)
Mutual labels:  transformations

Incoming

Build Status Code Coverage Quality Score Latest Stable Version

Incoming is a PHP library designed to simplify and abstract the transformation of loose, complex input data into consistent, strongly-typed data structures.

Born out of inspiration from using Fractal, Incoming can be seen as a spiritual inversion. When working with data models of any kind (database, remote service, etc), it can be a huge pain to take raw input data and turn it into anything usable. Even worse is when something changes and you have to duplicate code or try and keep backwards compatibility. Incoming is here to make all this easier while enabling you to create more concern-separated, reusable, and testable code.

"Wait, what? Why not just use 'x' or 'y'?" Don't worry, I've got you covered.

Features

  • Input filtering and transforming
  • Built-in powerful, immutable data-structures for handling complex input
  • Allows for automatic hydrator-for-model resolution via factory abstraction
  • Makes strong use of interfaces for well structured, easily-testable code
  • Completely configurable via composable units

Still curious? Check out the examples.

Installation

  1. Get Composer
  2. Add "incoming/incoming" to your dependencies: composer require incoming/incoming
  3. Include the Composer autoloader <?php require 'vendor/autoload.php';

Examples

The easiest example to relate to in the PHP world? "Form" or HTTP request data:

class UserHydrator implements Incoming\Hydrator\Hydrator
{
    public function hydrate($input, $model)
    {
        $model->setName($input['name']);
        $model->setGender($input['gender']);
        $model->setFavoriteColor($input['favorite_color']);

        return $model;
    }
}

// Create our incoming processor
$incoming = new Incoming\Processor();

// Process our raw form/request input into a User model
$user = $incoming->processForModel(
    $_POST,            // Our HTTP form-data array
    new User(),        // Our model to hydrate
    new UserHydrator() // The hydrator above
);

// Validate and save the user
// ...

Sure, that's a pretty contrived example. But what kind of power can we gain when we compose some pieces together?

class BlogPostHydrator implements Incoming\Hydrator\ContextualHydrator
{
    const USER_CONTEXT_KEY = 'user';

    public function hydrate($input, $model, Map $context = null)
    {
        $model->setBody($input['body']);
        $model->setCategories($input['categories']);
        $model->setTags($input['tags']);

        // Only allow admin users to publish posts
        if (null !== $context && $context->exists(self::USER_CONTEXT_KEY)) {
            $user = $context->get(self::USER_CONTEXT_KEY);

            $model->setAuthor($user->getName());

            if ($user->isAdmin()) {
                $model->setPublished($input['published']);
            }
        }

        return $model;
    }
}

// Create our incoming processor
$incoming = new Incoming\Processor();

// Create a context for the hydrator with active data
$context = Map::fromArray([
    BlogPostHydrator::USER_CONTEXT_KEY => $this->getCurrentUser() // A user context
]);

// Process our raw form/request input to update our BlogPost model
$post = $incoming->processForModel(
    $_POST,                      // Our HTTP form-data array
    BlogPost::find($_GET['id']), // Fetch our blog post to update and pass it in
    new BlogPostHydrator(),      // The hydrator above
    $context                     // Context data to enable more powerful conditional processing
);

// Validate and save the blog post
// ...

Let's try and filter our input first.

class SpecialCharacterFilterTransformer implements Incoming\Transformer\Transformer
{
    public function transform($input)
    {
        $transformed = [];

        foreach($input as $key => $string) {
            $transformed[$key] = filter_var($string, FILTER_SANITIZE_STRING);
        }

        return $transformed;
    }
}

class UserHydrator implements Incoming\Hydrator\Hydrator
{
    // Same as previous examples...
}

// Create our incoming processor
$incoming = new Incoming\Processor(
    new SpecialCharacterFilterTransformer()
);

// Process our raw form/request input into a User model
$user = $incoming->processForModel(
    $_POST,            // Our HTTP form-data array
    new User(),        // Our model to hydrate
    new UserHydrator() // The hydrator above
);

// Validate and save the user
// ...

Missing type hints? PHP's type-system's restrictions can be circumvented!:

class UserHydrator extends Incoming\Hydrator\AbstractDelegateHydrator
{
    // Boom! Type-hintable arguments!
    // (For more info, see the `AbstractDelegateHydrator` class doc-block)
    public function hydrateModel(Incoming\Structure\Map $input, User $model)
    {
        $model->setName($input['name']);
        // ...

        return $model;
    }
}

// Create our incoming processor
$incoming = new Incoming\Processor();

// Process our raw form/request input into a User model
$user = $incoming->processForModel(
    $_POST,            // Our HTTP form-data array
    new User(),        // Our model to hydrate
    new UserHydrator() // The hydrator above
);

// Validate and save the user
// ...

Immutable objects or objects with required data in the constructor? No problem!

class User
{
    private $first_name;
    private $last_name;

    public function __construct(string $first_name, string $last_name)
    {
        $this->first_name = $first_name;
        $this->last_name = $last_name;
    }
}

class UserBuilder extends Incoming\Hydrator\AbstractDelegateBuilder
{
    public function buildModel(Incoming\Structure\Map $input): User
    {
        return new User($input['first_name'], $input['last_name']);
    }
}

// Create our incoming processor
$incoming = new Incoming\Processor();

// Process our raw form/request input into a new User model
$user = $incoming->processForType(
    $_POST,            // Our HTTP form-data array
    User::class,       // Our type to build
    new UserBuilder()  // The builder above
);

// Validate and save the user
// ...

Wait, what? Why not just use "x" or "y"?

Still not sold on the idea even with the provided examples? You may be thinking...

  • "Why not just use MyModelName::fromArray($data)?"
  • "But my ORM already has a $model->fill($data) method..."
  • "I don't get it..."

Yea, sure, you could easily just build a model from a raw data array or just pass an array of attributes to a "fill" method and hope that everything goes well, but there's a few issues with doing that: What happens when you refactor a model or an underlying database table? Do you all of a sudden break backwards compatibility in an HTTP API's parameters just because your table might have changed? What about if you want to prevent certain parameters from being changed in a conditional manner? Do you just create a massive chunk of if statements in a factory method of the model itself?

The idea with using Incoming is to separate concerns, create reusable and composable units, and really enrich an application's ability to create complex entities while providing a convenient API that rids of some of PHP's "gotchas".

After all, "magical" solutions like mass attribute assignment have had their pitfalls before. ;)

License

Incoming is proud to be MIT licensed.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].