2014-10-07 2 views
3

При создании формы с использованием Symfony сборка формы ужасно медленная и память всплескивается.Управление памятью Формы Symfony и Doctrine

Форма формируется с использованием некоторых подформ и использует некоторые отношения one-to-many. Когда данные формы становятся больше (больше объектов во многих сторонах), форма медленнее, а использование памяти становится все больше, чем кажется, хотя количество времени и памяти не похоже.

Пример, когда примерно 71 членство во многих частях памяти занимает около 116 МБ и занимает 14 секунд для загрузки.

я уже выводил число запросов, сделанных (от 75 до 4), хотя шип памяти все еще случается в тот момент, форма создается

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

Любые советы и приемы, чтобы ускорить этот вверх?

+0

не использовать тип 'entity' но только' выбор 'с' id => label' и напишите [DataTransformers] (http://symfony.com/doc/current/cookbook/form/data_transformers.html). Затем вы работаете только с равными значениями и только в конце, когда что-то было выбрано, преобразуйте его в желаемый объект – SBH

ответ

4

Я предполагаю, что вы используете тип entity в вашей форме. Они довольно тяжелые, так как сначала все объекты выбираются как объекты, а затем уменьшаются до некоторого id => label.

Таким образом, вы могли бы написать свой собственный entityChoice тип, который работает с id => label -array (так что ничего не забирается в качестве объекта в Фрист месте) и добавить DataTransformer к этому типу:

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'; 
    } 
} 

И как 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'); 
    } 
} 

И, наконец, зарегистрировать FormType как новый тип в service.yml

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

Вы можете использовать его в форме

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

с $repository как экземпляр нужного хранилища и 'choices' как массив с id => label

+0

. Я разрешил себе повторное использование и (попытаться) улучшить этот ответ в этом [Gist] (https: //gist.github .com/lologhi/bb900f061f8205841f6a) – LaurentG