Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[DoctrineBridge][Form] Fix BC break in DoctrineType #14576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 42 additions & 28 deletions src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader;
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface;
use Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader;
Expand All @@ -25,7 +24,6 @@
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
use Symfony\Component\Form\Exception\RuntimeException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand Down Expand Up @@ -92,6 +90,24 @@ public static function createChoiceName($choice, $key, $value)
return (string) $value;
}

/**
* Gets important parts from QueryBuilder that will allow to cache its results.
* For instance in ORM two query builders with an equal SQL string and
* equal parameters are considered to be equal.
*
* @param object $queryBuilder
*
* @return array|false Array with important QueryBuilder parts or false if
* they can't be determined
*
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
public function getQueryBuilderPartsForCachingHash($queryBuilder)
{
return false;
}

public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null)
{
$this->registry = $registry;
Expand All @@ -117,29 +133,28 @@ public function configureOptions(OptionsResolver $resolver)
$type = $this;

$choiceLoader = function (Options $options) use ($choiceListFactory, &$choiceLoaders, $type) {
// This closure and the "query_builder" options should be pushed to
// EntityType in Symfony 3.0 as they are specific to the ORM

// Unless the choices are given explicitly, load them on demand
if (null === $options['choices']) {
// We consider two query builders with an equal SQL string and
// equal parameters to be equal
$qbParts = $options['query_builder']
? array(
$options['query_builder']->getQuery()->getSQL(),
$options['query_builder']->getParameters()->toArray(),
)
: null;

$hash = CachingFactoryDecorator::generateHash(array(
$options['em'],
$options['class'],
$qbParts,
$options['loader'],
));

if (isset($choiceLoaders[$hash])) {
return $choiceLoaders[$hash];
$hash = null;
$qbParts = null;

// If there is no QueryBuilder we can safely cache DoctrineChoiceLoader,
// also if concrete Type can return important QueryBuilder parts to generate
// hash key we go for it as well
if (!$options['query_builder'] || false !== ($qbParts = $type->getQueryBuilderPartsForCachingHash($options['query_builder']))) {

$hash = CachingFactoryDecorator::generateHash(array(
$options['em'],
$options['class'],
$qbParts,
$options['loader'],
));

if (isset($choiceLoaders[$hash])) {
return $choiceLoaders[$hash];
}
}

if ($options['loader']) {
Expand All @@ -151,15 +166,19 @@ public function configureOptions(OptionsResolver $resolver)
$entityLoader = $type->getLoader($options['em'], $queryBuilder, $options['class']);
}

$choiceLoaders[$hash] = new DoctrineChoiceLoader(
$doctrineChoiceLoader = new DoctrineChoiceLoader(
$choiceListFactory,
$options['em'],
$options['class'],
$options['id_reader'],
$entityLoader
);

return $choiceLoaders[$hash];
if ($hash !== null) {
$choiceLoaders[$hash] = $doctrineChoiceLoader;
}

return $doctrineChoiceLoader;
}
};

Expand Down Expand Up @@ -240,10 +259,6 @@ public function configureOptions(OptionsResolver $resolver)
$queryBuilderNormalizer = function (Options $options, $queryBuilder) {
if (is_callable($queryBuilder)) {
$queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class']));

if (!$queryBuilder instanceof QueryBuilder) {
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
}
}

return $queryBuilder;
Expand Down Expand Up @@ -305,7 +320,6 @@ public function configureOptions(OptionsResolver $resolver)

$resolver->setAllowedTypes('em', array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager'));
$resolver->setAllowedTypes('loader', array('null', 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'));
$resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder'));
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,34 @@
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

class EntityType extends DoctrineType
{
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);

// Invoke the query builder closure so that we can cache choice lists
// for equal query builders
$queryBuilderNormalizer = function (Options $options, $queryBuilder) {
if (is_callable($queryBuilder)) {
$queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class']));

if (!$queryBuilder instanceof QueryBuilder) {
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
}
}

return $queryBuilder;
};

$resolver->setNormalizer('query_builder', $queryBuilderNormalizer);
$resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder'));
}

/**
* Return the default loader object.
*
Expand All @@ -35,4 +60,23 @@ public function getName()
{
return 'entity';
}

/**
* We consider two query builders with an equal SQL string and
* equal parameters to be equal.
*
* @param QueryBuilder $queryBuilder
*
* @return array
*
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
public function getQueryBuilderPartsForCachingHash($queryBuilder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected would work isn't it ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It yells that it has to be public as that's how parent class defines it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing the parent is also possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't it break in PHP 5.3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please try moving both of them to protected. It should work, and anyway tests will tell if I'm wrong on 5.3 or any other version

{
return array(
$queryBuilder->getQuery()->getSQL(),
$queryBuilder->getParameters()->toArray(),
);
}
}