Практическое руководство. Оптимизация производительности форм 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 мс.
Здесь я поставил результат профилировщиков:
![SymfonyProfiler]()
![BlackFire Profiler]()
![BlackFire 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
для моей формы?
Ответы
Ответ 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, что я в основном делал, чтобы вводить варианты в форму. В контроллере вы запрашиваете Варианты из базы данных, в вызове репозитория, убираете их как массив и вы выбираете только идентификатор, имя или заголовок, а затем вы переходите в форму в качестве значения параметра. При этом часть базы данных будет намного быстрее.