PHPackages                             alexanevsky/input-manager-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. alexanevsky/input-manager-bundle

ActiveSymfony-bundle[Validation &amp; Sanitization](/categories/validation)

alexanevsky/input-manager-bundle
================================

Provides functions which allows to map incoming data (for example, from JSON) into an object, modify it, validate it, and map it to a model or Doctrine entity

1.0.2(3y ago)151MITPHPPHP &gt;=8.1

Since Mar 24Pushed 3y ago1 watchersCompare

[ Source](https://github.com/alexanevsky/input-manager-bundle)[ Packagist](https://packagist.org/packages/alexanevsky/input-manager-bundle)[ RSS](/packages/alexanevsky-input-manager-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (8)Versions (4)Used By (0)

Input Manager
=============

[](#input-manager)

This library allows you to map incoming data (for example, from JSON) into an object, modify it, validate it, and map it to your model or Doctrine entity. This allows you to treat the data as an object and still prevent invalid property values for your model or Doctrine entity.

The library consists of three components:

- Deserializer
- Validator
- Mapper of input data to the model

Let's analyze each of them step by step.

Table of Contents
-----------------

[](#table-of-contents)

1. [First Step](#first-step)
2. [Deserializer](#deserializer)
    - [Basic Example](#basic-example)
    - [Type Conversion](#type-conversion)
    - [Objects Deserializing](#objects-deserializing)
    - [Nested Inputs Deserializing](#nested-inputs-deserializing)
    - [Input Collection Deserializing](#input-collection-deserializing)
    - [an Entity by Identifier Deserializing](#an-entity-by-identifier-deserializing)
    - [an Array of Entities by Identifier Deserializing](#an-array-of-entities-by-identifier-deserializing)
    - [Input Modifier](#input-modifier)
3. [Validator](#validator)
    - [Constraints Validator](#constraints-validator)
    - [Extended Validator](#extended-validator)
    - [Extended Validator Payload](#extended-validator-payload)
4. [Input to Object (Model) Mapper](#input-to-object-model-mapper)

First Step
----------

[](#first-step)

Add `InputManager` to the constructor of controller or service:

```
use Alexanevsky\InputManagerBundle\InputManager;

public function __construct(
    private InputManager $inputManager
) {}
```

Deserializer
------------

[](#deserializer)

### Basic Example

[](#basic-example)

Let's imagine that we have some model:

```
class User
{
    private string $firstName;

    private string $lastName;

    public function setFirstName(string $firstName): void
    {
        $this->firstName = $firstName;
    }

    public function setLastName(string $lastName): void
    {
        $this->lastName = $lastName;
    }
}
```

To map the data from the request to this model, we will use an intermediate object that implements `InputInterface` so that we can check and modify the incoming data if we need it:

```
use Alexanevsky\InputManagerBundle\Input\InputInterface;

class UserInput implements InputInterface
{
    public string $firstName;

    public string $lastName;
}
```

We can describe properties as public. We can also make them private and use setters and getters:

```
use Alexanevsky\InputManagerBundle\Input\InputInterface;

class UserInput implements InputInterface
{
    private string $firstName;

    private string $lastName;

    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function setFirstName(string $firstName): void
    {
        $this->firstName = $firstName;
    }

    public function getLastName(): string
    {
        return $this->lastName;
    }

    public function setLastName(string $lastName): void
    {
        $this->lastName = $lastName;
    }
}
```

You can use any approach you want. We will use public properties in this documentation.

*Note: If a public property has a getter (or setter), it will take precedence, i.e. the value of the getter will be taken (passed to the setter) rather than taken from the property (rather than assigned to the property). You can see more about how getters and setters are used in the library [alexanevsky/getter-setter-accessor](https://github.com/alexanevsky/getter-setter-accessor).*

So, the first step we need to do is deserialize our request to the Input object.

```
$json = '{"firstName": "John", "last_name": "Doe"}';
$input = new UserInput();
$this->deserializeInput($json, $input);
```

Or we can create an object during deserialization (either approach can be used):

```
$json = '{"firstName": "John", "last_name": "Doe"}';
$input = $this->deserializeInput($json, UserInput::class);
```

Please note that the deserializer can work with keys in both camel and snake cakes.

As a result, our `$input` will be like this:

```
echo $input->firstName; // John
echo $input->lastName; // Doe
```

### Type Conversion

[](#type-conversion)

The deserializer can convert simple data types.

Imagine that our Input expects the following data:

```
class UserInput implements InputInterface
{
    public bool $anyBoolValue;

    public bool $anyAnotherBoolValue;

    public int $anyIntValue;
}
```

And we will pass data of a slightly wrong type:

```
$json = '{"any_bool_value": 1, "any_another_bool_value": '', "anyIntValue": "123"}';
$input = $this->deserializeInput($json, UserInput::class);

echo $input->anyBoolValue; // true (bool)
echo $input->anyAnotherBoolValue; // false (bool)
echo $input->anyIntValue; // 123 (int)
```

As we can see, the deserializer coped with this task and converted the types to the required ones.

### Objects Deserializing

[](#objects-deserializing)

Imagine that we have a model that takes some object as its property.

```
class Address
{
    private string $city;

    private string $street;

    private string $building;

    // Setters and getters of properties...
}

class User
{
    private Address $address;

    // Setters and getters of properties...
}
```

In our Input class, we must use it:

```
class UserInput implements InputInterface
{
    private Address $address;
}
```

As a result, our Input will be successfully deserialized from this JSON:

```
$json = '{"address": {"city": "string", "street": "string", "building": "string" }}';
```

### Nested Inputs Deserializing

[](#nested-inputs-deserializing)

Your Input can accept a nested Input, which will also be deserialized and converted into the appropriate model object in the future.

Imagine that we have a model that accepts another model as its property.

```
class Category
{
    private string $name;

    // Getters and setters of properties...
}

class Article
{
    private string $title;

    private Category $category;

    // Getters and setters of properties...
}
```

These models will correspond to the following Input classes:

```
class CategoryInput implements InputInterface
{
    public string $name;
}

class ArticleInput implements InputInterface
{
    public string $title;

    public CategoryInput $category;
}
```

As a result, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"title": "Lorem Ipsum", "category": {"name": "Dolor"}}';
```

### Input Collection Deserializing

[](#input-collection-deserializing)

If our model has a property that does not contain one other model, but an array of models, we can create a collection input class that implements `InputCollectionInterface` (even easier - extends `AbstractInputCollection`), and define in it which Input we need to use:

```
use Alexanevsky\InputManagerBundle\Input\AbstractInputCollection;
use Alexanevsky\InputManagerBundle\Input\InputCollectionInterface;

class CategoryInput implements InputInterface
{
    public string $name;
}

class CategoryInputCollection extends AbstractInputCollection
{
    public function getClass(): string
    {
        return CategoryInput::class;
    }
}

class ArticleInput implements InputInterface
{
    public string $title;

    public CategoryInputCollection $categories;
}
```

As a result, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"title": "Lorem Ipsum", "categories": [{"name": "Dolor"}, {"name": "Sit"}]}';
```

### an Entity by Identifier Deserializing

[](#an-entity-by-identifier-deserializing)

Imagine that in the request data we only have the identifier of some entity, but we need to deserialize it into the entity itself:

Our entities look like this:

```
class Category
{
    private string $id;

    // Getters and setters of properties...
}

class Article
{
    private Category $category;

    // Getters and setters of properties...
}
```

To get the `Category` object by the passed `id`, add the `EntityFromId` attribute to our Input class, specifying which class we expect as the first parameter:

```
use Alexanevsky\InputManagerBundle\Input\Attribute\EntityFromId;

class ArticleInput implements InputInterface
{
    #[EntityFromId(Category::class)]
    public Category $category;
}
```

As a result, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"category_id": 1}';
```

We can also do without the suffix:

```
$json = '{"category": 1}';
```

If the identity property of our `Category` is different than `id`, we need to pass the name of the identity property as the second parameter to `EntityFromId`:

```
class Category
{
    private string $code;

    // Getters and setters of properties...
}

class Article
{
    private Category $category;

    // Getters and setters of properties...
}

class ArticleInput implements InputInterface
{
    #[EntityFromId(Category::class, 'code')]
    public Category $category;
}
```

As a result, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"category_code": "cat"}';
```

We can also do without the suffix:

```
$json = '{"category": "cat"}';
```

We can also specify with the third parameter `EntityFromId` what suffix we expect in the input data:

```
class ArticleInput implements InputInterface
{
    #[EntityFromId(Category::class, 'code', 'identifier')]
    public Category $category;
}
```

As a result, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"category_identifier": "cat"}';
```

We can also avoid the suffix, as in the two examples above.

If we don't want any suffix at all, we must pass false as the third parameter of `EntityFromId`:

```
class ArticleInput implements InputInterface
{
    #[EntityFromId(Category::class, 'code', false)]
    public Category $category;
}
```

### an Array of Entities by Identifier Deserializing

[](#an-array-of-entities-by-identifier-deserializing)

The `EntityFromId` attribute described above can also be used for models.

```
class Category
{
    private string $id;

    // Getters and setters of properties...
}

class Article
{
    private Collection $categories;

    // Getters and setters of properties...
}

class ArticleInput implements InputInterface
{
    #[EntityFromId(Category::class)]
    public array $categories;
}
```

A slight difference is that if the second parameter (suffix) is not specified, the deserializer will expect it with `s` at the end. That is, our `ArticleInput` will be successfully deserialized from this JSON:

```
$json = '{"categories_ids": [1, 2]}';
```

We can also do without the suffix:

```
$json = '{"categories": [1, 2]}';
```

### Input Modifier

[](#input-modifier)

We can implement our Input from `InputModifiableInterface` instead of `InputInterface`. This will allow us to add a `modify()` method that will be called immediately after deserialization. This will allow us to change some of our Input data.

```
use Alexanevsky\InputManagerBundle\Input\InputModifiableInterface;

class ArticleInput implements InputModifiableInterface
{
    public string $title;

    public \DateTime $createdAt;

    public function modify(): void
    {
        $this->title .= ' from ' . $createdAt->format('m/d/Y');
    }
}
```

So, given the following as request:

```
$json = '{"title": "Lorem Ipsum", "createdAt": "2023-01-01 12:00:00"}';
```

Our Input will be with the following data after derealization:

```
echo $input->title; // 'Lorem Ipsum from 01/01/2023'
```

Validator
---------

[](#validator)

### Constraints Validator

[](#constraints-validator)

The easiest way to validate is to use [Symfony Constraints](https://symfony.com/doc/current/validation.html#constraints).

Let's set the constraints attributes on our Input class:

```
use Symfony\Component\Validator\Constraints as Assert;

class UserInput implements InputInterface
{
    #[Assert\NotBlank]
    public string $name;

    #[Assert\NotBlank]
    #[Assert\Email]
    public string $email;
}
```

After we have deserialized our Input, let's validate it:

```
use Symfony\Component\Translation\TranslatableMessage;

/** @var TranslatableMessage[] $errors */
$errors = $this->inputManager->validate($input);
```

As a result, we will get an associative array of errors, in which the key is the name of the property in which the error occurred, and the value is `TranslatableMessage` with the error message.

### Extended Validator

[](#extended-validator)

We can create our own extended validator that will implements `InputValidatorInterface` (even easier - extends `AbstractInputValidator`). In it, we specify the `validate()` method, which will perform the necessary checks and return an associative array of errors, in which the key is the name of the property in which the error occurred, and the value is `TranslatableMessage` with the error message. If it returns an empty array, it means that the validation was successful.

```
use App\Component\InputManager\InputValidator\AbstractInputValidator;

class UserInput implements InputInterface
{
    public string $name;

    public string $email;
}

class UserInputValidator extends AbstractInputValidator
{
    public function validate(): array
    {
        $errors = [];

        if (!$this->getInput()->name) {
            $errors['name'] = new TranslatableMessage('Name is empty!');
        }

        if (!$this->getInput()->email) {
            $errors['email'] = new TranslatableMessage('Email is empty!');
        } elseif (filter_var($this->getInput()->email, FILTER_VALIDATE_EMAIL)) {
            $errors['email'] = new TranslatableMessage('Email is incorrect!');
        }

        return $errors = [];
    }
}
```

To use this validator, specify its class name as the second parameter to the validation method:

```
$errors = $this->inputManager->validate($input, UserInputValidator::class);
```

*Note that the extended validator will only be called if there were no errors defined by constraints.*

We can choose another way: in our extended validator, create validation methods for each property by prepend `validate` to the property name in the method name. We will have to return `TranslatableMessage` if there is an error, or null if there is no error.

```
class UserInputValidator extends AbstractInputValidator
{
    public function validateName(): ?TranslatableMessage
    {
        if (!$this->getInput()->name) {
            return new TranslatableMessage('Name is empty!');
        }

        return null;
    }

    public function validateEmail(): ?TranslatableMessage
    {
        if (!$this->getInput()->email) {
            return new TranslatableMessage('Email is empty!');
        } elseif (filter_var($this->getInput()->email, FILTER_VALIDATE_EMAIL)) {
            return new TranslatableMessage('Email is incorrect!');
        }

        return null;
    }
}
```

### Extended Validator Payload

[](#extended-validator-payload)

We can also pass some payload to our validator to use in validation. To do this, we will add a public property and add the `SetFromPayload` attribute to it, which will tell the validator that the value of the property should be received from the payload. We can also set the boolean `requred` parameter. If `required` is `true` and there is no data in the payload, we will have an exception. For example, we can pass to the validator the entity of our user for whom the current Input is being processed.

```
use Alexanevsky\InputManagerBundle\InputValidator\Attribute\SetFromPayload;

class UserInputValidator extends AbstractInputValidator
{
    #[SetFromPayload(true)]
    public User $user;

    public function __construct(
        private UserRepository $usersRepository
    ) {
    }

    public function validateEmail(): ?TranslatableMessage
    {
        $foundedUser = $this->usersRepository->findOneBy(['email' => $this->getInput()->email]);

        return !$foundedUser || $this->user === $foundedUser
            ? null
            : new TranslatableMessage('User with this email already exists!');
    }
}
```

To pass the payload to the extended validator, pass it as the third parameter to the validator method:

```
$errors = $this->inputManager->validate($input, UserInputValidator::class, ['user' => $user]);
```

Input to Object (Model) Mapper
------------------------------

[](#input-to-object-model-mapper)

Finally, having completed the deserialization and validation, we need to map the data from our Input to our Model. We will do this by simple method:

```
$this->$inputManager->mapInputToObject($input, $user);
```

And all our deserialized valid data from Input will be set to the user by public properties and setters.

Good luck!

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community4

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

3

Last Release

1139d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ed729cb5e814cf76d84dc99e64482b8628deac527a75ffd216eff74378c76413?d=identicon)[alexanevsky](/maintainers/alexanevsky)

### Embed Badge

![Health badge](/badges/alexanevsky-input-manager-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/alexanevsky-input-manager-bundle/health.svg)](https://phpackages.com/packages/alexanevsky-input-manager-bundle)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k16.7M310](/packages/easycorp-easyadmin-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
