Symfony and Doctrine memory forms

When building a form using Symfony, building the form is terribly slow and memory spikes.

The form is created using some subforms and uses some relationships one-to-many

. As the form data gets larger (more entities in many ways), the form is slower and memory usage becomes more noticeable, although the amount of time and memory usage does not seem to be.

Example, when there are about 71 enities on the large side, memory is about 116 MB and takes 14 seconds to load.

I've already printed out the number of requests executed (75 to 4), although the memory spike is still happening at the time the form is created.

$form = $this->createForm(new TapsAndAppliancesType(), $taps);

Any tips and tricks to speed this up?

+3


source to share


1 answer


I am assuming you are using a type entity

in your form. They are quite heavy, as at first all objects are selected as objects and then reduced to some style id => label

.

So you can write your own type entityChoice

that works with id => label

-array (so nothing will be selected as an object in the frist place) and add a DataTransformer for that type:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

use MyNamespace\EntityToIdTransformer;

class EntityChoiceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addModelTransformer(new EntityToIdTransformer($options['repository']));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'empty_value' => false,
            'empty_data'  => null,
        ));

        $resolver->setRequired(array(
            'repository'
        ));
    }

    public function getParent()
    {
        return 'choice';
    }

    public function getName()
    {
        return 'entityChoice';
    }
}

      

And as a DataTransformer:

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class EntityToIdTransformer implements DataTransformerInterface
{
    private $entityRepository;

    public function __construct(EntityRepository $entityRepository)
    {
        $this->entityRepository = $entityRepository;
    }

    /**
     * @param object|array $entity
     * @return int|int[]
     *
     * @throws TransformationFailedException
     */
    public function transform($entity)
    {
        if ($entity === null) {
           return null;
        }
        elseif (is_array($entity) || $entity instanceof \Doctrine\ORM\PersistentCollection) {
            $ids = array();
            foreach ($entity as $subEntity) {
                $ids[] = $subEntity->getId();
            }

            return $ids;
        }
        elseif (is_object($entity)) {
            return $entity->getId();
        }

        throw new TransformationFailedException((is_object($entity)? get_class($entity) : '').'('.gettype($entity).') is not a valid class for EntityToIdTransformer');
    }

    /**
     * @param int|array $id
     * @return object|object[]
     *
     * @throws TransformationFailedException
     */
    public function reverseTransform($id)
    {
        if ($id === null) {
            return null;
        }
        elseif (is_numeric($id)) {
            $entity = $this->entityRepository->findOneBy(array('id' => $id));

            if ($entity === null) {
                throw new TransformationFailedException('A '.$this->entityRepository->getClassName().' with id #'.$id.' does not exist!');
            }

            return $entity;
        }
        elseif (is_array($id)) {

            if (empty($id)) {
                return array();
            }

            $entities = $this->entityRepository->findBy(array('id' => $id)); // its array('id' => array(...)), resulting in many entities!!

            if (count($id) != count($entities)) {
                throw new TransformationFailedException('Some '.$this->entityRepository->getClassName().' with ids #'.implode(', ', $id).' do not exist!');
            }

            return $entities;
        }

        throw new TransformationFailedException(gettype($id).' is not a valid type for EntityToIdTransformer');
    }
}

      



Finally, register the FormType as a new Type in service.yml

services:
    myNamespace.form.type.entityChoice:
        class: MyNamespace\EntityChoiceType
        tags:
            - { name: form.type, alias: entityChoice }

      

Then you can use it in your form like

$formBuilder->add('appliance', 'entityChoice', array(
    'label'       => 'My Label',
    'repository'  => $repository,
    'choices'     => $repository->getLabelsById(),
    'multiple'    => false,
    'required'    => false,
    'empty_value' => '(none)',
))

      

with $repository

as an instance of your desired repository and 'choices'

as an array withid => label

+4


source







All Articles