phpsymfonydoctrine-orm

How to handle in a generic way the session storage of filters avoiding the "Entity must be managed"


We've all been confronted to that problem : "Entity must be managed" when we are trying to setFilters() / getFilters()

So how to handle the session storage of filters in a generic way, in order to avoid merging and detaching or re-hydrating manually the entities ?

See the answer just below.


Solution

  • Well, some colleague (@benji07) at work have written this :

    /**
     * Set filters
     * @param string $name    Name of the key to store filters
     * @param array  $filters Filters
     */
    public function setFilters($name, array $filters = array())
    {
        foreach ($filters as $key => $value) {
            // Transform entities objects into a pair of class/id
            if (is_object($value)) {
                if ($value instanceof ArrayCollection) {
                    if (count($value)) {
                        $filters[$key] = array(
                            'class' => get_class($value->first()),
                            'ids' => array()
                        );
                        foreach ($value as $v) {
                            $identifier = $this->getDoctrine()->getEntityManager()->getUnitOfWork()->getEntityIdentifier($v);
                            $filters[$key]['ids'][] = $identifier['id'];
                        }
                    }
                }
                elseif (!$value instanceof \DateTime) {
                    $filters[$key] = array(
                        'class' => get_class($value),
                        'id'    => $this->getDoctrine()->getEntityManager()->getUnitOfWork()->getEntityIdentifier($value)
                    );
                }
            }
        }
    
        $this->getRequest()->getSession()->set(
            $name,
            $filters
        );
    }
    
    /**
     * Get Filters
     * @param string $name    Name of the key to get filters
     * @param array  $filters Filters
     *
     * @return array
     */
    public function getFilters($name, array $filters = array())
    {
        $filters = array_merge(
            $this->getRequest()->getSession()->get(
                $name,
                array()
            ),
            $filters
        );
    
        foreach ($filters as $key => $value) {
            // Get entities from pair of class/id
            if (is_array($value) && isset($value['class']) && isset($value['id'])) {
                $filters[$key] = $this->getDoctrine()->getEntityManager()->find($value['class'], $value['id']);
            } elseif (isset($value['ids'])) {
                $data = $this->getDoctrine()->getEntityManager()->getRepository($value['class'])->findBy(array('id' => $value['ids']));
                $filters[$key] = new ArrayCollection($data);
            }
        }
    
        return $filters;
    }
    

    It works for basic entities, and multivalued choices

    PS: Don't forget to add a use statement for the ArrayCollection

    Disclaimer, we don't know if it's a good practice, and we know at least one limitation : you have to be sure that the object you try to save in the session has an id (it's 99,9% the case)