diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index f94e10e9e7..6f4433f4dc 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -29,6 +29,11 @@ abstract class DoctrineType extends AbstractType */ protected $registry; + /** + * @var array + */ + private $choiceListCache = array(); + public function __construct(ManagerRegistry $registry) { $this->registry = $registry; @@ -46,6 +51,7 @@ abstract class DoctrineType extends AbstractType public function setDefaultOptions(OptionsResolverInterface $resolver) { + $choiceListCache =& $this->choiceListCache; $registry = $this->registry; $type = $this; @@ -59,17 +65,34 @@ abstract class DoctrineType extends AbstractType return null; }; - $choiceList = function (Options $options) use ($registry) { + $choiceList = function (Options $options) use ($registry, &$choiceListCache, &$time) { $manager = $registry->getManager($options['em']); - return new EntityChoiceList( - $manager, + $choiceHashes = is_array($options['choices']) + ? array_map('spl_object_hash', $options['choices']) + : $options['choices']; + + $hash = md5(json_encode(array( + spl_object_hash($manager), $options['class'], $options['property'], $options['loader'], - $options['choices'], + $choiceHashes, $options['group_by'] - ); + ))); + + if (!isset($choiceListCache[$hash])) { + $choiceListCache[$hash] = new EntityChoiceList( + $manager, + $options['class'], + $options['property'], + $options['loader'], + $options['choices'], + $options['group_by'] + ); + } + + return $choiceListCache[$hash]; }; $resolver->setDefaults(array( diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php new file mode 100644 index 0000000000..94102159d6 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form\Type; + +use Symfony\Component\Form\Tests\FormPerformanceTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; +use Doctrine\ORM\Tools\SchemaTool; +use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; + +/** + * @author Bernhard Schussek + */ +class EntityTypePerformanceTest extends FormPerformanceTestCase +{ + const ENTITY_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + protected function getExtensions() + { + $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $manager->expects($this->any()) + ->method('getManager') + ->will($this->returnValue($this->em)); + + return array( + new CoreExtension(), + new DoctrineOrmExtension($manager) + ); + } + + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { + $this->markTestSkipped('Doctrine DBAL is not available.'); + } + + if (!class_exists('Doctrine\Common\Version')) { + $this->markTestSkipped('Doctrine Common is not available.'); + } + + if (!class_exists('Doctrine\ORM\EntityManager')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + + $this->em = DoctrineOrmTestCase::createTestEntityManager(); + + parent::setUp(); + + $schemaTool = new SchemaTool($this->em); + $classes = array( + $this->em->getClassMetadata(self::ENTITY_CLASS), + ); + + try { + $schemaTool->dropSchema($classes); + } catch (\Exception $e) { + } + + try { + $schemaTool->createSchema($classes); + } catch (\Exception $e) { + } + + $ids = range(1, 300); + + foreach ($ids as $id) { + $name = 65 + chr($id % 57); + $this->em->persist(new SingleIdentEntity($id, $name)); + } + + $this->em->flush(); + } + + /** + * This test case is realistic in collection forms where each + * row contains the same entity field. + */ + public function testCollapsedEntityField() + { + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 20; ++$i) { + $form = $this->factory->create('entity', null, array( + 'class' => self::ENTITY_CLASS, + )); + + // force loading of the choice list + $form->createView(); + } + } +}