OrderModel.php
<?php
namespace App\Dto\Request\Model;
use Symfony\Component\Validator\Constraints as Assert;
class OrderModel
{
#[Assert\Uuid(message: "Order id must be an unique identifier value.")]
#[Assert\Positive(message: "Order id must be a positive integer value.")]
public int $id;
/**
* @Assert\Positive(message="customerId must be a positive integer value.")
*/
public int $customerId;
public array $items;
/**
* @Assert\Type("string", message="Order total must be a string float value.")
* @Assert\Type("float", message="Order total must be a string float value.")
*/
public string $total;
}
OrderType.php
<?php
use App\Dto\Request\Model\OrderModel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', IntegerType::class)
->add('customerId', IntegerType::class)
->add('items', CollectionType::class, [
'entry_type' => ItemType::class
])
->add('total', TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => OrderModel::class
]);
}
}
OrderController.php:
#[Route('/order', name:'order_new', methods: 'POST')]
public function create(ManagerRegistry $doctrine, Request $request): JsonResponse|Response
{
$form = $this->createForm(\OrderType::class);
if ($request->isMethod('POST')) {
$form->submit($request->request->get($form->getName()));
if(!$form->isSubmitted() || !$form->isValid()){
return $this->handleView($this->view($form, Response::HTTP_BAD_REQUEST));
}
}
}
My Post Request:
{
"id": "dsdas",
"customerId": 1,
"items": [
{
"productId": 1,
"quantity": 1,
"unitPrice": "250.25",
"total": "250.25"
},
{
"productId": 1,
"quantity": 1,
"unitPrice": "250.25",
"total": "250.25"
}
],
"total": "500.50"
}
This request is passing validation and I'm trying to figure out. Any ideas would be appreciated.
CodePudding user response:
I think you are missing the form name in the submitted data as top-level key. You are trying to send this:
{
"id": "dsdas",
"customerId": 1,
"total": "500.50"
}
And your code ($request->request->get($form->getName())) expects this (if the form name is "order_type")
{
"order_type": {
"id": "dsdas",
"customerId": 1,
"total": "500.50"
}
}
One solution is to create a named form with ... no name :)
public function create(
Request $request,
FormFactoryInterface $formFactory
) {
// Create form with no name: setting the first parameter to '' means no name (ideal for API endpoints)
$form = $formFactory->createNamed('', OrderType::class);
$form->handleRequest($request);
if(
!$form->isSubmitted() ||
!$form->isValid()
) {
// ...
} else {
// ...
}
}
The second solution is to add the form name yourself
public function create(Request $request): JsonResponse|Response
{
$form = $this->createForm(\OrderType::class);
if ($request->isMethod('POST')) {
$form->submit([
// You can also add the key yourself right before submitting
$form->getName() => $request->request->all(),
]);
if(
!$form->isSubmitted() ||
!$form->isValid()
){
return $this->handleView($this->view($form, Response::HTTP_BAD_REQUEST));
}
}
}
A third option but not the best one is that you always send your data WITH the form key to the controller, but I would not go for this option if it an API endpoint. So only if it is a regular form submission and the submitted form fields are all generated with the original form name prefix.
As a last option, you can also catch both data formats coming in. Maybe something like this so you can actually send with or without the key:
$finalData = $request->request->get($form->getName()) ?: $request->request->all();
$form->submit([$form->getName() => $finalData]);