Symfony2 表单验证耗尽内存
Symfony2 form validation exhausted memory
我有一个复杂的表单,其中包含许多实体选择字段。当我不使用验证时,add/edit 没问题。但是当我在 /config/validation.yml 中启用表单验证时,我不能再 add/edit 了。
我检查了 php 个日志文件并发现了一些错误:
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 32 bytes) in ..\example.com\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php on line 694
PHP Stack trace:
PHP 1. {main}() ..\example.com\web\app_dev.php:0
PHP 2. Symfony\Component\HttpKernel\Kernel->handle() ..\example.com\web\app_dev.php:29
PHP 3. Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle() ..\example.com\var\bootstrap.php.cache:2377
PHP 4. Symfony\Component\HttpKernel\HttpKernel->handle() ..\example.com\var\bootstrap.php.cache:3133
PHP 5. Symfony\Component\HttpKernel\HttpKernel->handleRaw() ..\example.com\var\bootstrap.php.cache:2984
PHP 6. call_user_func_array:{..\example.com\var\bootstrap.php.cache:3022}() ..\example.com\var\bootstrap.php.cache:3022
PHP 7. DefaultBundle\Controller\PropertyHouseController->createAction() ..\example.com\var\bootstrap.php.cache:3022
PHP 8. Symfony\Component\Form\Form->createView() ..\example.com\src\DefaultBundle\Controller\PropertyHouseController.php:86
PHP 9. Symfony\Component\Form\Form->createView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php:1061
PHP 10. Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php:1058
PHP 11. Symfony\Component\Form\ResolvedFormType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy.php:111
PHP 12. Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\ResolvedFormType.php:159
PHP 13. Symfony\Component\Form\ResolvedFormType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy.php:111
PHP 14. Symfony\Component\Form\Extension\Core\Type\ChoiceType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\ResolvedFormType.php:162
PHP 15. Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList->getPreferredViews() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Core\Type\ChoiceType.php:101
PHP 16. Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList->load() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList.php:174
PHP 17. Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader->getEntities() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList.php:430
PHP 18. Doctrine\ORM\AbstractQuery->execute() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader.php:71
PHP 19. Doctrine\ORM\Query->_doExecute() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\AbstractQuery.php:794
PHP 20. Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\Query.php:286
PHP 21. Doctrine\DBAL\Connection->executeQuery() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\Query\Exec\SingleSelectExecutor.php:50
PHP 22. PDOStatement->execute() ..\example.com\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php:694
如何解决这个错误?感谢您的帮助。
我的表格很复杂
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('beds', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
])
->add('baths', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
])
->add('storey', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
'required' => false,
])
->add('title')
->add('body')
->add('password', 'repeated', [
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
])
->add('province', 'entity', [
'class' => 'AppBundle:Province',
'property' => 'name',
'placeholder' => ''
])
->add('activity', 'entity', [
'class' => 'AppBundle:Activity',
'property' => 'name',
'placeholder' => ''
])
->add('unit', 'entity', [
'class' => 'AppBundle:Unit',
'property' => 'name',
'placeholder' => ''
])
->add('direction', 'entity', [
'class' => 'AppBundle:Direction',
'property' => 'name',
'placeholder' => '',
'required' => false
])
->add('legal', 'entity', [
'class' => 'AppBundle:Legal',
'property' => 'name',
'placeholder' => '',
'required' => false
])
->add('images', 'collection', [
'type' => new PropertyImageType(),
'allow_add' => true,
'allow_delete' => true
])
->add('amenities', 'entity', [
'class' => 'AppBundle:Amenity',
'property' => 'name',
'expanded' => true,
'multiple' => true,
'query_builder' => function (EntityRepository $repository) {
return $repository->createQueryBuilder('a')
->from('AppBundle:Amenity', 'amenity')
->where('a.type = 1');
}
]);
然后我使用 EventSubscriber 动态添加一些字段
$builder->addEventSubscriber(new AddDistrictFieldSubscriber())
->addEventSubscriber(new AddStreetFieldSubscriber())
->addEventSubscriber(new AddWardFieldSubscriber())
->addEventSubscriber(new AddProjectFieldSubscriber());
class AddDistrictFieldSubscriber implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit',
];
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$province = $data->getProvince();
$districts = $province === null ? [] : $province->getDistricts();
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'choices' => $districts,
]);
}
public function preSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$province = array_key_exists('province', $data) ? $data['province'] : null;
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('d')
->from('AppBundle:District', 'district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);
}
}
class AddStreetFieldSubscriber implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit',
];
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$district = $data->getDistrict();
$streets = $district === null ? [] : $district->getStreets();
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'choices' => $streets,
'required' => false,
]);
}
public function preSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$district = array_key_exists('district', $data) ? $data['district'] : null;
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($district) {
return $repository->createQueryBuilder('s')
->from('AppBundle:Street', 'street')
->where('street.district = :district')
->setParameter('district', $district);
}
]);
}
}
数据库表:
- 省份:id - 名称(70 条记录)
- 分区:id - province_id - 前缀 - 名称(700 条记录)
- 街道:id - district_id - 前缀 - 名称(17000 条记录)
- 病房:id - district_id - 前缀 - 姓名(11000 条记录)
...还有一些表
调试表单中的每个字段后,我终于在 preSubmit() 的查询生成器中发现了问题
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('d')
->from('AppBundle:District', 'district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($district) {
return $repository->createQueryBuilder('s')
->from('AppBundle:Street', 'street')
->where('street.district = :district')
->setParameter('district', $district);
}
]);
我使用不同的别名,所以学说会在每个 table --> 耗尽内存中获取所有记录。
因此,只需在查询构建器中使用相同的别名或删除 from() ;)
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);
我有一个复杂的表单,其中包含许多实体选择字段。当我不使用验证时,add/edit 没问题。但是当我在 /config/validation.yml 中启用表单验证时,我不能再 add/edit 了。 我检查了 php 个日志文件并发现了一些错误:
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 32 bytes) in ..\example.com\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php on line 694
PHP Stack trace:
PHP 1. {main}() ..\example.com\web\app_dev.php:0
PHP 2. Symfony\Component\HttpKernel\Kernel->handle() ..\example.com\web\app_dev.php:29
PHP 3. Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle() ..\example.com\var\bootstrap.php.cache:2377
PHP 4. Symfony\Component\HttpKernel\HttpKernel->handle() ..\example.com\var\bootstrap.php.cache:3133
PHP 5. Symfony\Component\HttpKernel\HttpKernel->handleRaw() ..\example.com\var\bootstrap.php.cache:2984
PHP 6. call_user_func_array:{..\example.com\var\bootstrap.php.cache:3022}() ..\example.com\var\bootstrap.php.cache:3022
PHP 7. DefaultBundle\Controller\PropertyHouseController->createAction() ..\example.com\var\bootstrap.php.cache:3022
PHP 8. Symfony\Component\Form\Form->createView() ..\example.com\src\DefaultBundle\Controller\PropertyHouseController.php:86
PHP 9. Symfony\Component\Form\Form->createView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php:1061
PHP 10. Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php:1058
PHP 11. Symfony\Component\Form\ResolvedFormType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy.php:111
PHP 12. Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\ResolvedFormType.php:159
PHP 13. Symfony\Component\Form\ResolvedFormType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeDataCollectorProxy.php:111
PHP 14. Symfony\Component\Form\Extension\Core\Type\ChoiceType->buildView() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\ResolvedFormType.php:162
PHP 15. Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList->getPreferredViews() ..\example.com\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Core\Type\ChoiceType.php:101
PHP 16. Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList->load() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList.php:174
PHP 17. Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader->getEntities() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList.php:430
PHP 18. Doctrine\ORM\AbstractQuery->execute() ..\example.com\vendor\symfony\symfony\src\Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader.php:71
PHP 19. Doctrine\ORM\Query->_doExecute() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\AbstractQuery.php:794
PHP 20. Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\Query.php:286
PHP 21. Doctrine\DBAL\Connection->executeQuery() ..\example.com\vendor\doctrine\orm\lib\Doctrine\ORM\Query\Exec\SingleSelectExecutor.php:50
PHP 22. PDOStatement->execute() ..\example.com\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php:694
如何解决这个错误?感谢您的帮助。
我的表格很复杂
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('beds', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
])
->add('baths', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
])
->add('storey', 'choice', [
'choices' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9+',
],
'placeholder' => '',
'required' => false,
])
->add('title')
->add('body')
->add('password', 'repeated', [
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
])
->add('province', 'entity', [
'class' => 'AppBundle:Province',
'property' => 'name',
'placeholder' => ''
])
->add('activity', 'entity', [
'class' => 'AppBundle:Activity',
'property' => 'name',
'placeholder' => ''
])
->add('unit', 'entity', [
'class' => 'AppBundle:Unit',
'property' => 'name',
'placeholder' => ''
])
->add('direction', 'entity', [
'class' => 'AppBundle:Direction',
'property' => 'name',
'placeholder' => '',
'required' => false
])
->add('legal', 'entity', [
'class' => 'AppBundle:Legal',
'property' => 'name',
'placeholder' => '',
'required' => false
])
->add('images', 'collection', [
'type' => new PropertyImageType(),
'allow_add' => true,
'allow_delete' => true
])
->add('amenities', 'entity', [
'class' => 'AppBundle:Amenity',
'property' => 'name',
'expanded' => true,
'multiple' => true,
'query_builder' => function (EntityRepository $repository) {
return $repository->createQueryBuilder('a')
->from('AppBundle:Amenity', 'amenity')
->where('a.type = 1');
}
]);
然后我使用 EventSubscriber 动态添加一些字段
$builder->addEventSubscriber(new AddDistrictFieldSubscriber())
->addEventSubscriber(new AddStreetFieldSubscriber())
->addEventSubscriber(new AddWardFieldSubscriber())
->addEventSubscriber(new AddProjectFieldSubscriber());
class AddDistrictFieldSubscriber implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit',
];
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$province = $data->getProvince();
$districts = $province === null ? [] : $province->getDistricts();
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'choices' => $districts,
]);
}
public function preSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$province = array_key_exists('province', $data) ? $data['province'] : null;
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('d')
->from('AppBundle:District', 'district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);
}
}
class AddStreetFieldSubscriber implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit',
];
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$district = $data->getDistrict();
$streets = $district === null ? [] : $district->getStreets();
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'choices' => $streets,
'required' => false,
]);
}
public function preSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$district = array_key_exists('district', $data) ? $data['district'] : null;
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($district) {
return $repository->createQueryBuilder('s')
->from('AppBundle:Street', 'street')
->where('street.district = :district')
->setParameter('district', $district);
}
]);
}
}
数据库表: - 省份:id - 名称(70 条记录) - 分区:id - province_id - 前缀 - 名称(700 条记录) - 街道:id - district_id - 前缀 - 名称(17000 条记录) - 病房:id - district_id - 前缀 - 姓名(11000 条记录) ...还有一些表
调试表单中的每个字段后,我终于在 preSubmit() 的查询生成器中发现了问题
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('d')
->from('AppBundle:District', 'district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);
$form->add('street', 'entity', [
'class' => 'AppBundle:Street',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($district) {
return $repository->createQueryBuilder('s')
->from('AppBundle:Street', 'street')
->where('street.district = :district')
->setParameter('district', $district);
}
]);
我使用不同的别名,所以学说会在每个 table --> 耗尽内存中获取所有记录。 因此,只需在查询构建器中使用相同的别名或删除 from() ;)
$form->add('district', 'entity', [
'class' => 'AppBundle:District',
'property' => 'name',
'placeholder' => '',
'query_builder' => function (EntityRepository $repository) use ($province) {
return $repository->createQueryBuilder('district')
->where('district.province = :province')
->setParameter('province', $province);
}
]);