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

Feature request: set optgroup property with entity field type #1735

Closed
webda2l opened this Issue Jul 19, 2011 · 9 comments

Comments

Projects
None yet
5 participants
@webda2l

webda2l commented Jul 19, 2011

Hello,

It seems that isn't actually possible to define an optgroup with entity field type.
It will be cool to have something which works with the indexBy parameter of the leftJoin function or a new property as 'property_group' which will be use to the creation of the choiceList

            ->add('detailEvt', 'entity', array(
                'class' => 'Astrimmo\\AppBundle\\Entity\\Detailevt',
                'property' => 'd.libelle',
   *             'property_group' => 't.libelle',
                'query_builder' => function(EntityRepository $er) {
                    return $er->createQueryBuilder('d')
   *                     ->leftJoin('d.type', 't', null, null, 't.libelle');
                },
                'label' => "Détail incident",
                'multiple' => true
            ))

What do you think? I think about doing the fix myself but maybe someone see a quick way.
Thks

@ericclemmons

This comment has been minimized.

Show comment
Hide comment
@ericclemmons

ericclemmons Jul 20, 2011

Contributor

I've been curious bout the right way of doing this for a while. Internally, we've extended to add a group_by attribute which is the property path of the group label.

For example, we have Users that each belong to a Group, so we use group_by: group.name, which translates to $user->getGroup()->getName(). Of course group alone will work as long as we have __toString() defined.

We've been hesitant to have this adjust the query builder, since using the query builder is optional.

Contributor

ericclemmons commented Jul 20, 2011

I've been curious bout the right way of doing this for a while. Internally, we've extended to add a group_by attribute which is the property path of the group label.

For example, we have Users that each belong to a Group, so we use group_by: group.name, which translates to $user->getGroup()->getName(). Of course group alone will work as long as we have __toString() defined.

We've been hesitant to have this adjust the query builder, since using the query builder is optional.

@webda2l

This comment has been minimized.

Show comment
Hide comment
@webda2l

webda2l Jul 20, 2011

Finally, for now, i use your code addition for support grouped entities, and i manage myself the list of choices with

    $details = $this->em->getRepository('AstrimmoAppBundle:Detailevt')->findAllWithType();
    foreach ($details as $detail) {
        $options[$detail->getType()->getLibelle()][$detail->getId()] = $detail;
    }

But your extension can be interesting if you want commit it. :)

webda2l commented Jul 20, 2011

Finally, for now, i use your code addition for support grouped entities, and i manage myself the list of choices with

    $details = $this->em->getRepository('AstrimmoAppBundle:Detailevt')->findAllWithType();
    foreach ($details as $detail) {
        $options[$detail->getType()->getLibelle()][$detail->getId()] = $detail;
    }

But your extension can be interesting if you want commit it. :)

@jasvrcek

This comment has been minimized.

Show comment
Hide comment
@jasvrcek

jasvrcek Oct 19, 2011

Could you guys elaborate a little more? Eric, would you post your code, or is there a better way to do this now? Thanks for any help.

Could you guys elaborate a little more? Eric, would you post your code, or is there a better way to do this now? Thanks for any help.

@jasvrcek

This comment has been minimized.

Show comment
Hide comment
@jasvrcek

jasvrcek Oct 19, 2011

Update:

After digging a little through the code I was able to create a list with optgroups using the 'choices' option on the entity field type. However, it required passing the entity manager to the formType in the constructor, not sure if that's how things are supposed to be done:

In MyType::buildForm()

->add('subcategory', 'entity', array(
'class' => 'App\Bundle\WebBundle\Entity\SubCategory',
'choices' => $this->em->getRepository('AppWebBundle:Category')->getSelectList()
))

Method in rerpository:

public function getSelectList()
{
$query = $this->getEntityManager()
->createQuery('
SELECT c, s FROM AppWebBundle:Category c
LEFT JOIN c.subcategories s
ORDER BY c.name, s.name'
);

    $list = array();
    foreach ($query->getResult() as $cat)
    {
        $list[$cat->getName()] = array();

        foreach ($cat->getSubcategories() as $sub)
        {
            $list[$cat->getName()][$sub->getId()] = $sub;
        }
    }
    return $list;
}

Update:

After digging a little through the code I was able to create a list with optgroups using the 'choices' option on the entity field type. However, it required passing the entity manager to the formType in the constructor, not sure if that's how things are supposed to be done:

In MyType::buildForm()

->add('subcategory', 'entity', array(
'class' => 'App\Bundle\WebBundle\Entity\SubCategory',
'choices' => $this->em->getRepository('AppWebBundle:Category')->getSelectList()
))

Method in rerpository:

public function getSelectList()
{
$query = $this->getEntityManager()
->createQuery('
SELECT c, s FROM AppWebBundle:Category c
LEFT JOIN c.subcategories s
ORDER BY c.name, s.name'
);

    $list = array();
    foreach ($query->getResult() as $cat)
    {
        $list[$cat->getName()] = array();

        foreach ($cat->getSubcategories() as $sub)
        {
            $list[$cat->getName()][$sub->getId()] = $sub;
        }
    }
    return $list;
}
@ericclemmons

This comment has been minimized.

Show comment
Hide comment
@ericclemmons

ericclemmons Oct 19, 2011

Contributor

I have a pretty clean solution I devised last night using a form extension.

I'll post it in a bit, then we can talk about merging it into the core.

-Eric Clemmons
Sent from my iPad Nano

On Oct 19, 2011, at 12:18 PM, jasvrcekreply@reply.github.com wrote:

Update:

After digging a little through the code I was able to create a list with optgroups using the 'choices' option on the entity field type:

In FormType::buildForm()

->add('subcategory', 'entity', array(
'class' => 'App\Bundle\WebBundle\Entity\SubCategory',
'choices' => $this->em->getRepository('AppWebBundle:Category')->getSelectList()
))

Method in rerpository:

public function getSelectList()
{
$query = $this->getEntityManager()
->createQuery('
SELECT c, s FROM AppWebBundle:Category c
LEFT JOIN c.subcategories s
ORDER BY c.name, s.name'
);

   $list = array();
   foreach ($query->getResult() as $cat)
   {
       $list[$cat->getName()] = array();

       foreach ($cat->getSubcategories() as $sub)
       {
           $list[$cat->getName()][$sub->getId()] = $sub;
       }
   }
   return $list;

}

Reply to this email directly or view it on GitHub:
#1735 (comment)

Contributor

ericclemmons commented Oct 19, 2011

I have a pretty clean solution I devised last night using a form extension.

I'll post it in a bit, then we can talk about merging it into the core.

-Eric Clemmons
Sent from my iPad Nano

On Oct 19, 2011, at 12:18 PM, jasvrcekreply@reply.github.com wrote:

Update:

After digging a little through the code I was able to create a list with optgroups using the 'choices' option on the entity field type:

In FormType::buildForm()

->add('subcategory', 'entity', array(
'class' => 'App\Bundle\WebBundle\Entity\SubCategory',
'choices' => $this->em->getRepository('AppWebBundle:Category')->getSelectList()
))

Method in rerpository:

public function getSelectList()
{
$query = $this->getEntityManager()
->createQuery('
SELECT c, s FROM AppWebBundle:Category c
LEFT JOIN c.subcategories s
ORDER BY c.name, s.name'
);

   $list = array();
   foreach ($query->getResult() as $cat)
   {
       $list[$cat->getName()] = array();

       foreach ($cat->getSubcategories() as $sub)
       {
           $list[$cat->getName()][$sub->getId()] = $sub;
       }
   }
   return $list;

}

Reply to this email directly or view it on GitHub:
#1735 (comment)

@webda2l

This comment has been minimized.

Show comment
Hide comment
@webda2l

webda2l Oct 19, 2011

@jasvrcek Yes this is what i have done but this is not the best solution
Maybe wait for those of @ericclemmons

webda2l commented Oct 19, 2011

@jasvrcek Yes this is what i have done but this is not the best solution
Maybe wait for those of @ericclemmons

@ericclemmons

This comment has been minimized.

Show comment
Hide comment
@ericclemmons

ericclemmons Oct 25, 2011

Contributor

Created a pull request with my latest version of group support. Comments welcome!

Contributor

ericclemmons commented Oct 25, 2011

Created a pull request with my latest version of group support. Comments welcome!

fabpot added a commit that referenced this issue Nov 1, 2011

merged branch ericclemmons/1735-entity_choice_list_group_by (PR #2464)
Commits
-------

6cb7acf CS - camelCase & curly braces
d9b7abb Added EntityChoiceList test for `group_by` and invalid, deep property paths
e6554d6 Removed Closure support from group_by (PropertyPath strings only)
037933a CS - (String) renamed to (string)
7ad0f05 Added group_by test for EntityType
882482a Added group_by tests for EntityChoiceList
040e988 `EntityChoiceList` now supports grouping of entities by property path or closure
b171a6a Added `group_by` to EntityType

Discussion
----------

[Doctrine] [Form] EntityType+EntityChoiceList supports grouping choices

Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: #1735

Per the discussion in #1735, `EntityType` does not immediately support grouping options, though I updated support for it in `EntityChoiceList` in fb9d951.

This PR accomplishes the following:

* Adds optional `group_by` property to `EntityType` that supports either a `PropertyPath` or a `\Closure` that is evaluated on the entity choices
* Support for groups is added via the constructor in `EntityChoiceList`
* Groups are created prior to `EntityChoiceList#loadEntities` via a new `groupEntities` function
* Added tests for `EntityChoiceList`
* Added test for `EntityType` `group_by` support

*There is an alternative version that only modifies `EntityType`, but that requires the addition of `EntityType#buildView(...)`, which is messy, IMO: ericclemmons/symfony@master...1735-entity_type_group_by*

---------------------------------------------------------------------------

by fabpot at 2011/10/25 01:48:23 -0700

ping @beberlei

---------------------------------------------------------------------------

by beberlei at 2011/10/25 03:06:05 -0700

I didnt run the tests, but generally this looks very good and is a good extension.

---------------------------------------------------------------------------

by beberlei at 2011/11/01 06:25:09 -0700

@fabpot i revewied this and it looks very good, tests all pass, i think this is a very nice addition.
@abeel

This comment has been minimized.

Show comment
Hide comment
@abeel

abeel Dec 20, 2011

Hi guys,
this issue isn't correct yet:
in EntityChoiseList you have this methode thath's not correct:

private function groupEntities($entities, $groupBy)
{
$grouped = array();

    foreach ($entities as $entity) {
        // Get group name from property path
        try {
            $path   = new PropertyPath($groupBy);
            $group  = (string) $path->getValue($entity);
        } catch (UnexpectedTypeException $e) {
            // PropertyPath cannot traverse entity
            $group = null;
        }

        if (empty($group)) {
           $grouped[] = $entity; =====> this line is not correct
        } else {
            $grouped[$group][] = $entity;
        }
    }

    return $grouped;
}

i explain if i have structure like that:

group1{group11,group12}
group2
group3{group31,group32}

with this code i have:

group1
group1
===group11
===group12
group2
group3
group3
===group31
===group32

we have the node twice because :

if (empty($group)) {
$grouped[] = $entity; =====> this line is not correct
} else {
$grouped[$group][] = $entity;
}

can you reopen please ?

abeel commented Dec 20, 2011

Hi guys,
this issue isn't correct yet:
in EntityChoiseList you have this methode thath's not correct:

private function groupEntities($entities, $groupBy)
{
$grouped = array();

    foreach ($entities as $entity) {
        // Get group name from property path
        try {
            $path   = new PropertyPath($groupBy);
            $group  = (string) $path->getValue($entity);
        } catch (UnexpectedTypeException $e) {
            // PropertyPath cannot traverse entity
            $group = null;
        }

        if (empty($group)) {
           $grouped[] = $entity; =====> this line is not correct
        } else {
            $grouped[$group][] = $entity;
        }
    }

    return $grouped;
}

i explain if i have structure like that:

group1{group11,group12}
group2
group3{group31,group32}

with this code i have:

group1
group1
===group11
===group12
group2
group3
group3
===group31
===group32

we have the node twice because :

if (empty($group)) {
$grouped[] = $entity; =====> this line is not correct
} else {
$grouped[$group][] = $entity;
}

can you reopen please ?

@Schyzophrenic

This comment has been minimized.

Show comment
Hide comment
@Schyzophrenic

Schyzophrenic Mar 19, 2012

I have to implement a similar behavior but I cannot migrate to Symfony 2.1.
I currently use Symfony 2.0.11.

I basically tried @jasvrcek implementation, which displays everything correctly.
The only difference is that I am dealing with a multiple select field within a collection. (If I remove this very field, everything works as expected).

When I submit the form, I have an error saying : The error occurs while binding the form ($form->bindRequest($request)).

request: Symfony\Component\Form\Exception\PropertyAccessDeniedException: Property "musicGenres" is not public in class Acme\BandBundle\Entity\Track". 
Maybe you should create the method "setMusicGenres()"? (uncaught exception) at ...\vendor\symfony\src\Symfony\Component\Form\Util\PropertyPath.php line 353

Here, I am dealing with a Track (the form within my collection), that is linked to several music genre. A ManyToMany relationship exists between these entities.

My field type is defined as follow:

->add('musicGenres', 'entity', array(
                                        'label'     => 'track.genres',
                                        'required'  => false,
                                        'class'     => 'AcmeBandBundle:MusicGenre',
                                        'choices'   => $this->em->getRepository('AcmeBandBundle:MusicGenre')->getAllGenresByCategory(),
                                        'property'  => 'name',
                                        'multiple'  => true,
                                        'attr'      => array('placeholder' => 'track.genres',
                                                        ),
                        ))

Am I missing something here ? Or is it something we cannot do with the current implementation?
Why am I asked for setGenres() whereas the console created addGenres instead ?

I have to implement a similar behavior but I cannot migrate to Symfony 2.1.
I currently use Symfony 2.0.11.

I basically tried @jasvrcek implementation, which displays everything correctly.
The only difference is that I am dealing with a multiple select field within a collection. (If I remove this very field, everything works as expected).

When I submit the form, I have an error saying : The error occurs while binding the form ($form->bindRequest($request)).

request: Symfony\Component\Form\Exception\PropertyAccessDeniedException: Property "musicGenres" is not public in class Acme\BandBundle\Entity\Track". 
Maybe you should create the method "setMusicGenres()"? (uncaught exception) at ...\vendor\symfony\src\Symfony\Component\Form\Util\PropertyPath.php line 353

Here, I am dealing with a Track (the form within my collection), that is linked to several music genre. A ManyToMany relationship exists between these entities.

My field type is defined as follow:

->add('musicGenres', 'entity', array(
                                        'label'     => 'track.genres',
                                        'required'  => false,
                                        'class'     => 'AcmeBandBundle:MusicGenre',
                                        'choices'   => $this->em->getRepository('AcmeBandBundle:MusicGenre')->getAllGenresByCategory(),
                                        'property'  => 'name',
                                        'multiple'  => true,
                                        'attr'      => array('placeholder' => 'track.genres',
                                                        ),
                        ))

Am I missing something here ? Or is it something we cannot do with the current implementation?
Why am I asked for setGenres() whereas the console created addGenres instead ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment