PHPackages                             azyouness/request-to-form-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. [API Development](/categories/api)
4. /
5. azyouness/request-to-form-bundle

ActiveSymfony-bundle[API Development](/categories/api)

azyouness/request-to-form-bundle
================================

Map HTTP request payloads to Symfony forms from controller attributes or a reusable mapper service.

0.1.5(1mo ago)88MITPHPPHP &gt;=8.2

Since May 27Pushed 1mo agoCompare

[ Source](https://github.com/azyouness/request-to-form-bundle)[ Packagist](https://packagist.org/packages/azyouness/request-to-form-bundle)[ Docs](https://github.com/azyouness/request-to-form-bundle)[ RSS](/packages/azyouness-request-to-form-bundle/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (39)Versions (7)Used By (0)

Request To Form Bundle
======================

[](#request-to-form-bundle)

Request To Form Bundle submits the current Symfony request to a Symfony Form directly from a controller argument.

Symfony already provides attributes such as `#[MapRequestPayload]` to map request data into typed objects like DTOs. This bundle provides a similar controller experience for applications that use Symfony Forms to submit and validate request data.

With `#[MapRequestToForm]`, the request payload is submitted to a form. If the form is valid, the controller receives the mapped form data or the submitted form itself. If the form is invalid, an exception is thrown before the controller is called.

```
use App\Entity\Post;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts', methods: ['POST'])]
public function create(
    #[MapRequestToForm]
    Post $post,
    EntityManagerInterface $entityManager,
): JsonResponse {
    $entityManager->persist($post);
    $entityManager->flush();

    return $this->json($post);
}
```

Here, `$post` is already the submitted and validated form data. The controller does not need to decode the request, create the form, submit it, check validity, or extract the data manually.

Requirements
------------

[](#requirements)

- PHP `>=8.2`
- Symfony `^7.4 || ^8.0`

Installation
------------

[](#installation)

```
composer require azyouness/request-to-form-bundle
```

Register the bundle manually if Symfony Flex did not do it automatically:

```
// config/bundles.php
return [
    // ...
    AzYouness\RequestToFormBundle\RequestToFormBundle::class => ['all' => true],
];
```

Basic Usage
-----------

[](#basic-usage)

Add `#[MapRequestToForm]` to a controller argument.

```
use App\Entity\Post;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts', methods: ['POST'])]
public function create(
    #[MapRequestToForm]
    Post $post,
): JsonResponse {
    // $post is the submitted and validated form data.

    return $this->json($post);
}
```

The form type can be inferred when exactly one registered form type uses the controller argument class as its `data_class`.

If the form type cannot be inferred or multiple form types use the same `data_class`, pass it explicitly:

```
use App\Form\PostType;

public function create(
    #[MapRequestToForm(PostType::class)]
    Post $post,
): JsonResponse {
    // ...
}
```

Automatic inference works when the form type exposes a `data_class` that can be inspected. If your form type needs runtime options, pass `formType` explicitly and provide `formOptions`, or use the `RequestToFormMapper` service.

If the form can return `null` data, the controller argument should be nullable:

```
public function create(
    #[MapRequestToForm]
    ?Post $post,
): JsonResponse {
    // ...
}
```

Existing Data
-------------

[](#existing-data)

When another Symfony resolver has already resolved the controller argument, the bundle submits the request into that object.

This is useful when submitting the request into existing data:

```
use App\Entity\Post;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts/{id}', methods: ['PUT'])]
public function update(
    #[MapRequestToForm]
    Post $post,
): JsonResponse {
    // $post is first resolved from the {id} route parameter by Symfony's EntityValueResolver,
    // then submitted to the form with the current request data.

    return $this->json($post);
}
```

`MapRequestToForm` can also be combined with other argument attributes, such as Doctrine's `MapEntity`:

```
use App\Entity\Post;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts/{slug}', methods: ['PUT'])]
public function update(
    #[MapRequestToForm]
    #[MapEntity(mapping: ['slug' => 'slug'])]
    Post $post,
): JsonResponse {
    // $post is resolved by MapEntity, then submitted through the form.

    return $this->json($post);
}
```

For nested create endpoints, disable entity resolution on the new entity argument:

```
use App\Entity\Comment;
use App\Entity\Post;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts/{id}/comments', methods: ['POST'])]
public function createComment(
    Post $post,
    #[MapEntity(disabled: true)]
    #[MapRequestToForm]
    Comment $comment,
): JsonResponse {
    // $post is resolved from the {id} route parameter.
    // $comment is created from the request body through the form.

    return $this->json($comment);
}
```

Without `#[MapEntity(disabled: true)]`, Symfony may also try to resolve `Comment $comment` from the same `{id}` route parameter before the form is submitted.

For `PATCH` and `GET` query requests, missing fields are kept by default. For other methods, missing fields are cleared by default. You can override this behavior with `clearMissing`.

```
public function update(
    #[MapRequestToForm(clearMissing: false)]
    Post $post,
): JsonResponse {
    // ...
}
```

Receiving The Form
------------------

[](#receiving-the-form)

If the controller argument type is `FormInterface`, the controller receives the submitted form.

```
use App\Form\PostType;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;

public function create(
    #[MapRequestToForm(PostType::class)]
    FormInterface $form,
): JsonResponse {
    $post = $form->getData();

    return $this->json($post);
}
```

This is useful when the controller needs access to the form object, not only its data.

You can also use another controller argument as the form data:

```
use App\Entity\Post;
use App\Form\PostType;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;

public function update(
    Post $post,
    #[MapRequestToForm(formType: PostType::class, dataArgument: 'post')]
    FormInterface $form,
): JsonResponse {
    // The form is submitted with $post as its initial data,
    // so $post is updated with the current request data.

    return $this->json($form->getData());
}
```

Root Scalar Forms
-----------------

[](#root-scalar-forms)

Root scalar form types are supported when the controller argument type matches the submitted form data.

```
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\JsonResponse;

public function rename(
    #[MapRequestToForm(TextType::class)]
    string $title,
): JsonResponse {
    // $title is the submitted string.

    return $this->json(['title' => $title]);
}
```

Supported Request Formats
-------------------------

[](#supported-request-formats)

By default, `json`, `form`, and `query` request formats are accepted:

```
#[MapRequestToForm(acceptFormat: ['json', 'form', 'query'])]
```

The supported formats are:

- `json`: submits the decoded JSON request body.
- `form`: submits request parameters and uploaded files, including `multipart/form-data`.
- `query`: submits query parameters from `GET` requests.

You can also limit an action to one format:

```
#[MapRequestToForm(acceptFormat: 'json')]
```

Example using the `query` format:

```
use App\Form\PostQueryType;
use AzYouness\RequestToFormBundle\Attribute\MapRequestToForm;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/posts', methods: ['GET'])]
public function index(
    #[MapRequestToForm(PostQueryType::class)]
    array $query,
): JsonResponse {
    return $this->json($query);
}
```

Options
-------

[](#options)

```
#[MapRequestToForm(
    formType: PostType::class,
    dataArgument: 'post',
    formOptions: ['validation_groups' => ['Default', 'publish']],
    clearMissing: false,
    acceptFormat: ['json', 'form', 'query'],
    validationFailedStatusCode: 422,
)]
```

The same options are available on the mapper service where they make sense.

OptionAvailable OnDescription`formType`attribute, mapperSymfony form type class. Required when it cannot be inferred.`dataArgument`attributeName of another controller argument to use as the form data.`data`mapperInitial form data. Use it to submit into an existing object.`formOptions`attribute, mapperOptions passed to `FormFactoryInterface::create()`.`clearMissing`attribute, mapperValue passed to `FormInterface::submit()`. Defaults to `false` for `PATCH` and `GET` query requests, `true` otherwise.`acceptFormat`attribute, mapperAccepted request formats: `json`, `form`, `query`, or a list of them.`throwOnInvalid`mapperSet to `false` to receive an invalid form instead of throwing.`validationFailedStatusCode`attribute, mapperHTTP status code used when validation fails. Default is `422`.Mapper Service
--------------

[](#mapper-service)

Use `RequestToFormMapper` directly when you need more control than the attribute gives you.

A common use case is preparing the form data before submitting the current request:

```
use App\Entity\Post;
use AzYouness\RequestToFormBundle\RequestToFormMapper;
use Symfony\Component\HttpFoundation\JsonResponse;

public function create(RequestToFormMapper $mapper): JsonResponse
{
    $post = new Post();
    $post->setAuthor($this->getUser());

    $mapper->handleCurrentRequest($post);

    // $post is now the submitted and validated form data.

    return $this->json($post);
}
```

You can also pass the request explicitly:

```
use App\Form\PostType;
use AzYouness\RequestToFormBundle\RequestToFormMapper;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

public function create(Request $request, RequestToFormMapper $mapper): JsonResponse
{
    $form = $mapper->handle(
        request: $request,
        formType: PostType::class,
    );

    return $this->json($form->getData());
}
```

The mapper throws when the form is invalid by default, like the attribute.

Disable this behavior when you want to handle the invalid form yourself:

```
$form = $mapper->handle(
    request: $request,
    formType: PostType::class,
    data: $post,
    throwOnInvalid: false,
);

if (!$form->isValid()) {
    // ...
}
```

Validation Failures
-------------------

[](#validation-failures)

When validation fails, the bundle throws an HTTP exception with status code `422` by default.

The previous exception is `FormValidationFailedException`, which gives access to the invalid form.

```
use AzYouness\RequestToFormBundle\Exception\FormValidationFailedException;

$previous = $exception->getPrevious();

if ($previous instanceof FormValidationFailedException) {
    $form = $previous->getForm();
}
```

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance92

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~2 days

Total

6

Last Release

46d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/63562514?v=4)[Youness](/maintainers/azyouness)[@azyouness](https://github.com/azyouness)

---

Top Contributors

[![azyouness](https://avatars.githubusercontent.com/u/63562514?v=4)](https://github.com/azyouness "azyouness (12 commits)")

---

Tags

requestapisymfonybundleformcontrollerattribute

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/azyouness-request-to-form-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/azyouness-request-to-form-bundle/health.svg)](https://phpackages.com/packages/azyouness-request-to-form-bundle)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M400](/packages/easycorp-easyadmin-bundle)[sylius/sylius

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

8.5k5.9M754](/packages/sylius-sylius)[chameleon-system/chameleon-base

The Chameleon System core.

1028.7k5](/packages/chameleon-system-chameleon-base)[prestashop/prestashop

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

9.2k18.1k](/packages/prestashop-prestashop)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M600](/packages/shopware-core)[oro/platform

Business Application Platform (BAP)

645143.5k116](/packages/oro-platform)

PHPackages © 2026

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