Skip to content

Instantly share code, notes, and snippets.

@johanderuijter
Last active September 22, 2020 20:33
Show Gist options
  • Save johanderuijter/a82fdf12693841041859aa38a3cf7d28 to your computer and use it in GitHub Desktop.
Save johanderuijter/a82fdf12693841041859aa38a3cf7d28 to your computer and use it in GitHub Desktop.
Symfony - Request to Command
<?php declare(strict_types=1);
namespace App;
use Webmozart\Assert\Assert;
final class YourCommand
{
/** @var string */
private string $foo;
/** @var array<string> */
private array $bar;
public function __construct(string $foo, array $bar)
{
// Assert away, make the command as strict (or loose for that matter) as you want
Assert::minLength($foo, 3);
Assert::maxLength($foo, 50);
$this->foo = $foo;
Assert::allString($bar);
$this->bar = $bar;
}
}
<?php declare(strict_types=1);
namespace App;
use Symfony\Component\Form\DataMapperInterface;
use Throwable;
use function iterator_to_array;
final class YourCommandMapper implements DataMapperInterface
{
public function mapDataToForms($data, $forms)
{
// Uni-directional is probably fine, otherwise, fill form here with stuff
}
public function mapFormsToData($forms, &$data)
{
try {
$forms = iterator_to_array($forms);
$data = new YourCommand(
$forms['foo']->getData(),
$forms['bar']->getData(),
);
} catch (Throwable $exception) {
// Nothing to see here... We just need to catch it so symfony continues and eventually starts validating the form
}
}
}
<?php declare(strict_types=1);
namespace App;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
final class YourCommandType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('foo', TextType::class, [
'constraints' => [
// All the validator constraints you need.
new NotBlank(),
new Length([
'min' => 3,
'max' => 50,
]),
]
])
// Whatever else you want
->add('bar', ChoiceType::class, [
'choices' => [
// ...
],
'multiple' => true,
])
;
$builder->setDataMapper(new YourCommandMapper());
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => YourCommand::class,
'empty_data' => null,
'constraints' => [
// This is kind of a last resort, in case you missed a constraint on one of the fields
// Dont rely on this, as the feedback to the user isn't all that usefull
new NotNull([
'message' => 'Something went wrong, please check your request and try again.',
]),
],
]);
}
}
<?php declare(strict_types=1);
namespace App;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
final class YourController
{
public function yourAction(Request $request): Response
{
$form = $this->createForm(YourCommandType::class);
$form->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
/** @var YourCommand $command */
$command = $form->getData();
// Do things
return new Response(null, Response::HTTP_NO_CONTENT);
}
// Handle form errors
// $errors = $form->getErrors(true);
return new Response(null, Response::HTTP_BAD_REQUEST);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment