Clotaire
Clotaire

Reputation: 71

Symfony2 Entity form type with a specific query_buider

Context

In my case, I've some orders with "discount vouchers" (discount). A discount can be use on under different conditions. For instance, discounts have an expired date, can be used by a limited number of customers, can be dedicated to a user, ...

Each discount can be attached to several order.

In my backoffice, I want to add to order create form a field "Discount" with a list of discount available but only right discounts.

What I made

With this solution, I can return specific discount defined by my request in my entity repository. It's good for expired date condition for instance.

What I would like

I'd like to filter results in the checkbox list. In fact, I want limit usage of the discount to a dedicated user, limit to a list of products, or limit the number of usage... And these condition cannot be done by a simple sql request.

I try to create special Type. My idea is to have an array of entities Discount and load a choice list... After that, I create a dataTransformer but It doesn't work !

Thank's for your ideas !

Upvotes: 1

Views: 2367

Answers (2)

Clotaire
Clotaire

Reputation: 71

I found a solution and explain it if someone have the same issue as me.

  • Create a custom Type

My custom type is inspired by Symfony\Bridge\Doctrine\Form\Type\DoctrineType

class DiscountOrderType extends AbstractType
{
     // overide choiceList callback
     public function setDefaultOptions(OptionsResolverInterface $resolver)
     {
        $choiceListCache =& $this->choiceListCache;
        $type = $this;

        $choiceList = function (Options $options) use (&$choiceListCache, &$time, $container) {

          [[ Copy paste same as Doctrine type ]]
        // Create your own choiceList class (EntityChoiceList)
        if (!isset($choiceListCache[$hash])) {
            $choiceListCache[$hash] = new DiscountChoiceList(
                $options['em'],
                $options['class'],
                $options['property'],
                $options['loader'],
                $options['choices'],
                $options['group_by']
            );
            // If you want add container
            $choiceListCache[$hash]->setContainer($container);
        }

        return $choiceListCache[$hash];
    };

    $resolver->setDefaults(array(
        'choice_list'       => $choiceList,
    ));

}
  • Create a custom EntityChoiceList

My custom type is inspired by Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList

class EntityChoiceList extends ObjectChoiceList
{
protected function load()
{
    if ($this->entityLoader) {
        $entities = $this->entityLoader->getEntities();
    } else {
        $entities = $this->em->getRepository($this->class)->findAll();
    }

    // You have access to the entities in the choice list
    // Add your custom code here to manipulate the choice list
    // you can do some check not properly possible with sql request (http requests on each result, ...) before add it in choice list
    // you can add some custom cache rules, ...
    // if you use gedmon and want apply a "join" with translate table, you can add $query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'); before playing request...
    // Possibilities are infinite

    // FOR INSTANCE : you already want unset first entity of the result
    if (isset($entities[0])) {
        unset($entities[0]);
    }
    // END OF CUSTOM CODE
    try {
        // The second parameter $labels is ignored by ObjectChoiceList
        // The third parameter $preferredChoices is currently not supported
        parent::initialize($entities, array(), array());
    } catch (StringCastException $e) {
        throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e);
    }

    $this->loaded = true;
}

Of course you can try to extend symfony class for beautyfull code ;).

Thank's to @maxwell2022 for your help !

Upvotes: 0

maxwell2022
maxwell2022

Reputation: 2855

You could use the $options from public function buildForm(FormBuilderInterface $builder, array $options) to pass your user and product for instance. With those 2 informations you could refine your list of discount (in your query)

if you do so you need to add them in the setDefaultValue

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
        $resolver->setDefaults(array(
            'user_discount' => null,
            'product_discount' => null,
        ));
}

and in your controller:

$form = $this->formFactory->create(new YourFormType(), $entity, array(
    'user_discount' => $this->getUser(),
    'product_discount' => $product,
));

Upvotes: 1

Related Questions