Naming your parameters

08. August 2020 Doctrine, Symfony 0

Last week I stumbled upon a new behavior, for me at least, in the way Symfony uses the route parameters to populate your method variables. In my situation I had a few parameters in my method, one of which was an entity reference, defaulting to null. Accessing the method whilst expecting that one entity to be null, resulted in a populated entity. I could not wrap my head around it. Here is what happened.

<?php

/**
     * @Route("/check_availibility/{order}/{type}/{appointment}")
     * @param Request                       $request
     * @param SalesOrder                    $order
     * @param string                        $type
     * @param GetPlanningAvailabilityAction $action
     * @param Appointment|null              $appointment
     *
     * @return Response
     */
    public function check_availability(Request $request, SalesOrder $order, string $type, GetPlanningAvailabilityAction $action, ?Appointment $appointment = null): Response
    {
        if (!$appointment) {
            $appointment = new Appointment();
            $appointment->setType($type)
                ->setOrder($order);
        }

        // Perform some actions
    }

In the process of visiting the url /check_availability/1234/Visit you would expect the variable $appointment to be null, therefor creating a new instance of the class Appointment. I however got the wrong end of that deal and this needs a little explaining of the Symfony internals.

At some point in the request/response life cycle, Symfony knows which Controller to instantiate and the method to call. The method needs to be called with the parameters which are either a service or a variable linked to the route parameters. The services are loaded via Symfony’s service injection, in this case the Request and GetPlanningAvailibilityAction classes. The simple type stringis loaded directly from the route variables. The 2 entities are looked up via the DoctrineParamConverter. It performs a lookup by the passed ID value. The repository is selected through the type-hint of the variable. So the SalesOrder entity repository is called to find ID 1234.

Now this is where stuff got interesting. Because there was no value for $appointment in the route, DoctrineParamConverter could not look up the entity by it’s ID. However, the converter has a fallback which has some Reflection going on. The reflection object which was created now had knowledge of all the properties the Appointment class has to offer. One of the properties is $type. And by now you can probably guess what happened. The converter was looking up an instance of Appointment through the property $type and this got a hit. Unintended and unexpected.

When I found this out, all I had to do was rename the parameter $type in the method and Route in the controller. This resulted in the method finally working as expected.

I hope this might help someone in the future. It cost me more time than I care to admit discovering this.


Leave a Reply

Your email address will not be published. Required fields are marked *