php-ddd icon indicating copy to clipboard operation
php-ddd copied to clipboard

Catching exceptions and converting them to Symfony form errors

Open webdevilopers opened this issue 9 years ago • 3 comments

If anywhere in your application e.g. in your Handler or DomainModel an Exception / DomainException is thrown you can catch and convert the message to use it in your Symfony form:

        $fooCommand = FooCommand::fromBar($bar);

        $form = $this->createForm(FooForm::class, $fooCommand);
        $form->handleRequest($this->getRequest());

        if ($form->isValid()) {
            $fooHandler = $this->get('acme.handler.foo');
            try {
                $fooHandler->handle($fooCommand);

                return $this->redirectToRoute('acme_foo_list');
            } catch (\Exception $e) {
                $form->addError(new FormError($e->getMessage()));
            }
        }

And then inside your TWIG template:

{{ form_errors(form) }}

webdevilopers avatar Apr 24 '16 14:04 webdevilopers

Normally we add value objects and asserts to our commands. So we thought about extending the try-catch-block to catch the exceptions:

  • https://gist.github.com/webdevilopers/e2263debd573c90b51ab18702c3db4c9

But now we are thinking of moving the value objects to the "Handler": https://github.com/webdevilopers/php-ddd/issues/14

In that case we no longer would have to extend and keep it the original way - see initial comment.

webdevilopers avatar Feb 03 '19 11:02 webdevilopers

We decided to make our Command DTOs (aka Symfony Form data classes) using primitives only again. The creation of value objects was moved to the Command Handler. Every value object throws a specific Domain Exception if they cannot be created or violate a business rule.

Now we can use a single try-catch block to catch the exceptions and transform them into a Symfony Form error.

This example features the @prooph service-bus. The actual Domain Exception can be accessed by getting the previous exception:

        $form->handleRequest($this->getRequest());

        if ($form->isValid()) {
            try {
                $this->get('prooph_service_bus.acme_command_bus')->dispatch($command);
            } catch (\Exception $busException) {
                switch (get_class($busException->getPrevious()))
                {
                    case InnerWidthIsOutOfRangeException::class:
                        $form->get('window')->addError(new FormError('...'));
                        break;
                }
            }
        }

Is there a better way to get and handle the domain exceptions @prolic @codeliner?

webdevilopers avatar Feb 09 '19 12:02 webdevilopers

The service bus will throw its own exceptions. If you don't like this behavior and want it to throw your custom exceptions directly instead, attach a listener to finalize event on the bus and replace the exception with the previous one.

prolic avatar Feb 09 '19 12:02 prolic