I’m struggling with a ‘strange’ behavior. When I use setMaxResult() + Rand() on my query_builder. I got randomly the message that my value is not valid.
SymfonyComponentValidatorConstraintViolation {#1320 ▼ -message: “Cette valeur n’est pas valide.” -messageTemplate: “This value is not valid.” -parameters: [▶] -plural: null -root: SymfonyComponentFormForm {#911 ▶} -propertyPath: “children[press]” -invalidValue: “8” -constraint: SymfonyComponentFormExtensionValidatorConstraintsForm {#987 …} -code: “1dafa156-89e1-4736-b832-419c2e501fca” -cause: SymfonyComponentFormExceptionTransformationFailedException {#916 …} }
If I remove setMaxResult(10) it works fine, if I remove Rand() It works too but not both
Could you please help me… I don’t get it and I don’t know what I can do
Here is my code:
GridType:
namespace AppForm; use AppEntityPress; use AppModelGridModel; use DoctrineORMEntityManagerInterface; use SymfonyBridgeDoctrineFormTypeEntityType; use SymfonyComponentFormAbstractType; use SymfonyComponentFormFormBuilderInterface; use SymfonyComponentOptionsResolverOptionsResolver; class GridType extends AbstractType { /** * @var EntityManagerInterface */ private $entityManager; public function __construct( EntityManagerInterface $entityManager ) { $this->entityManager = $entityManager; } public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('press', EntityType::class, [ 'class' => Press::class, 'query_builder' => $this->entityManager->getRepository(Press::class)->getIncluded($options['grid']), 'choice_label' => 'number', 'placeholder' => 'Sélectionner une presse', ]); } public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'grid' => null, 'data_class' => GridModel::class, 'csrf_protection' => false, ]); } }
PressRepository:
<?php namespace AppRepository; use AppConstantGlobalConstant; use AppEntityGrid; use AppEntityPress; use DoctrineBundleDoctrineBundleRepositoryServiceEntityRepository; use DoctrineORMQueryBuilder; use DoctrinePersistenceManagerRegistry; /** * @method Press|null find($id, $lockMode = null, $lockVersion = null) * @method Press|null findOneBy(array $criteria, array $orderBy = null) * @method Press[] findAll() * @method Press[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class PressRepository extends ServiceEntityRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Press::class); } /** * @param Grid|null $grid * @return QueryBuilder|null */ public function getIncluded(Grid $grid = null): ?QueryBuilder { $result = $this->createQueryBuilder('i') ->andWhere('i.status = :status') ->andWhere('i.include = :include') ->setParameters([ 'status' => GlobalConstant::STATUS_VALID, 'include' => true, ]); if ($grid) { $result->andWhere('i NOT IN (:grid)') ->setParameter( 'grid', $grid->getPress() ); } return $result->orderBy('i.number', 'ASC') ->setMaxResults(5) ->orderBy('RAND()'); }
Advertisement
Answer
The way Forms work, the query is executed every time the type is instanced, that means that the result of the initial query when loading the empty form is different than the one when submitting it (because of the random ordering). Since the submitted value is not anymore in the valid 'choices'
, you see that TransformationFailedException
.
One way to solve this would be to use ChoiceType
instead of EntityType
, and persist the query result in the session, for example.
public function __construct( PressRepository $repository, // This is deprecated in 5.3 // see https://symfony.com/blog/new-in-symfony-5-3-session-service-deprecation SessionInterface $session ) { $this->repository = $repository; $this->session = $session; } public function buildForm(FormBuilderInterface $builder, array $options): void { if (!$session->has('press_form_choices')) { $session->set( 'press_form_choices', $this->repository->getIncluded($options['grid']) ); } $choices = $session->get('press_form_choices'); $builder ->add('press', ChoiceType::class, [ 'choices' => $choices, 'choice_label' => 'number', 'choice_value' => 'id', 'placeholder' => 'Sélectionner une presse', ]); }
Upon sucessfull submission, clean up your session in your controller: $session->remove('press_form_choices');
.