Подтвердить что ты не робот

Практическое руководство. Оптимизация производительности форм Symfony?

У меня есть форма, которая является узким местом моего запроса ajax.

    $order = $this->getDoctrine()
        ->getRepository('AcmeMyBundle:Order')
        ->find($id);
    $order = $order ? $order : new Order();

    $form = $this->createForm(new OrderType(), $order);

    $formView = $form->createView();

    return $this->render(
        'AcmeMyBundle:Ajax:order_edit.html.twig',
        array(
            'form' => $formView,
        )
    );

Для более чистого кода я удалил операторы stopwatch.

Мой OrderType имеет следующие поля:

    $builder
        ->add('status') // enum (string)
        ->add('paid_status') // enum (string)
        ->add('purchases_price') // int
        ->add('discount_price') // int
        ->add('delivery_price') // int
        ->add('delivery_real_price', null, array('required' => false)) // int
        ->add('buyer_name') // string
        ->add('buyer_phone') // string
        ->add('buyer_email') // string
        ->add('buyer_address') // string
        ->add('comment') // string
        ->add('manager_comment') // string
        ->add('delivery_type') // enum (string)
        ->add('delivery_track_id') // string
        ->add('payment_method') // enum (string)
        ->add('payment_id') // string
        ->add('reward') // int
        ->add('reward_status') // enum (string)
        ->add('container') // string
        ->add('partner') // Entity: User
        ->add('website', 'website') // Entity: Website
        ->add('products', 'collection', array( // Entity: Purchase
            'type' => 'purchase',
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
            'property_path' => 'purchases',
            'error_bubbling' => false,
        ));

Тип покупки:

    $builder
        ->add('amount')
        ->add('price')
        ->add('code', 'variant', array(
            'property_path' => 'variantEntity',
            'data_class' => '\Acme\MyBundle\Entity\Simpla\Variant'
        ))
    ;

Также Тип покупки имеет прослушиватель, который здесь не является существенным. Он представлен в профайле Symfony ниже variant_retrieve, purchase_form_creating. Вы можете видеть, что это занимает около 200 мс.

Здесь я поставил результат профилировщиков: SymfonyProfilerBlackFire ProfilerBlackFire Profiler

Как вы можете видеть: $this->createForm(...) занимает 1011 мс, $form->createView(); занимает 2876 мс, а рендеринг формы в ветке тоже очень медленный: 4335мс. Как указано профилировщиком blackfire, все сделки заключаются в ObjectHydrator::gatherRowData() и UnitOfWork::createEntity().

Метод createEntity() называется 2223 раза, потому что есть некоторое поле, которое отображается с объектом Variant и имеет тип формы Entity. Но, как видно из вышеприведенного кода, для варианта нет типов Entity. Мой VariantType - это простой расширенный тип формы text, который имеет modelTransformer. Чтобы не испортить все, вы можете увидеть код для аналогичного класса типа docs.

Я нашел с XDebug, что buildView для VariantType был вызван в Purchase buildView с типом формы text. Но после этого откуда-то buildView для VariantType был вызван снова, и в этом случае он имеет тип формы Entity. Как это возможно? Я попытался определить пустой массив в choices и preferred_choices для каждого типа моей формы, но ничего не изменил. Что мне нужно сделать, чтобы предотвратить загрузку EntityChoiceList для моей формы?

4b9b3361

Ответ 1

Описанное поведение выглядит как работа угадывателя. У меня такое ощущение, что нужно показать какой-то дополнительный код (слушатели, VariantType, WebsiteType, PartnerType).

Предположим, что некоторый класс имеет ассоциацию variant to variant и FormType для этого класса имеет код ->add('variant') без явного указания типа (как я вижу, существует много мест, где тип не указан), Затем в игру входит DoctrineOrmTypeGuesser.

https://github.com/symfony/symfony/blob/2.7/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php#L46

Этот код присваивает этому типу entity type (!). Вызывается EntityRepository::findAll() и все варианты из БД гидратируются.

Как для других способов оптимизации формы:

  • Попробуйте указать тип во всех возможных случаях, чтобы предотвратить угадывание типа;
  • Используйте SELECT с JOINs, чтобы получить заказ, когда новые суб-запросы к БД отправляются для установки базовых данных для каждого отношения карт формы;
  • Сохранять ключи для элементов коллекции в представлении как удаление одного элемента без сохранения ключей приведет к ненужным обновлениям.

Ответ 2

У меня также была такая же проблема с типом сущности, мне нужно было перечислять города, там было как болото, а затем 4000, что я в основном делал, чтобы вводить варианты в форму. В контроллере вы запрашиваете Варианты из базы данных, в вызове репозитория, убираете их как массив и вы выбираете только идентификатор, имя или заголовок, а затем вы переходите в форму в качестве значения параметра. При этом часть базы данных будет намного быстрее.