6

I have an entity Product with a ManyToMany relation to an entity Category

/**
 * @ORM\ManyToMany(targetEntity="App\Domain\Category", inversedBy="stalls")
 */
private $categories;

//...

/**
 * @return Collection|Category[]
 */
public function getCategories(): Collection
{
    return $this->categories;
}

In the ProductCrudController class I have the following configureFields method:

public function configureFields(string $pageName): iterable
{
    return [
        Field::new('name'),
        Field::new('description'),
        AssociationField::new('categories'),
    ];
}

When creating/editing a Product everything works as expected in the relation, but in the list of products instead of showing the related categories I see the number of categories the product has. How can I change this behaviour?

In the following image the first product has 1 category and the second one in the list has 2 different categories. I would like the name of the categories to be shown here.

enter image description here

As a side note: Category class has a __toString method returning the name of the category.

EDIT:

The behaviour I am looking for is the same as the Tags column in the following image:

enter image description here

hosseio
  • 1,142
  • 2
  • 12
  • 25

5 Answers5

14

You can make a template for that like so:

// somewhere here templates/admin/field/category.html.twig
{% for category in field.value %}
  {%- set url = ea_url()
    .setController('Path\\To\\Your\\CategoryCrudController')
    .setAction('detail')
    .setEntityId(category.id)
  -%}
  <a href="{{ url }}">
    {{ category.name }}{% if not loop.last %}, {% endif %}
  </a>
{% else %}  
  <span class="badge badge-secondary">None</span>
{% endfor %}

And just add it to the field

// in ProductCrudController
AssociationField::new('categories')->setTemplatePath('admin/field/category.html.twig'),
Flash
  • 1,101
  • 1
  • 9
  • 13
9

You can format the value using the method formatValue like this :

->formatValue(function ($value, $entity) {
                $str = $entity->getCategories()[0];
                for ($i = 1; $i < $entity->getCategories()->count(); $i++) {
                    $str = $str . ", " . $entity->getCategories()[$i];
                }
                return $str;
              })
br-dev
  • 232
  • 3
  • 10
  • 1
    I will try this `formatValue` func, thanks. For the purpose of your solution maybe you can use `implode` (https://www.php.net/manual/en/function.implode.php) to return the same result? – hosseio Sep 09 '20 at 07:54
  • Very good solution, but please not that this in its current form does not allow for code reuse if you need this for multiple entities. – Robin Bastiaan Mar 08 '21 at 17:11
  • Indeed, the implode variant is preferable, IMHO, as suggested by @hosseio, like: ``` php AssociationField::new('categories') ->onlyOnDetail() ->formatValue(function ($value, $entity) { return implode(', ', $entity->getCategories()->toArray()); }) ``` – Olivier Berger Aug 30 '22 at 16:14
4

I had the same issue on my detail page. So instead of a template, I change the field type depending on the pagename

if (Crud::PAGE_DETAIL === $pageName) {
   $field = ArrayField::new('field')->setLabel('label');
} else {
   $field = AssociationField::new('field')->setLabel('label');
}
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
Gino
  • 41
  • 1
1

I will do that way :

->formatValue(function ($value, $entity) {
    return implode(",",$entity->getCategories()->toArray());
})
  • Welcome to Stack Overflow! Your answers will have more long-term value if you include a brief explanation of why or how your code solves the asker’s question. Also, note that this question is over a year old and already has an accepted answer. That doesn’t mean you can’t still add an answer if you think your solution adds something the others don’t, but it would be more helpful to explain what that is. – zephryl Feb 22 '22 at 11:59
0

Building on top of the most upvoted answer, you could make the Twig snippet universal like this:

{% for member in field.value %}
    {%- if field.customOption('crudControllerFqcn') is not empty -%}
        {%- set url = ea_url()
            .setController(field.customOption('crudControllerFqcn'))
            .setAction('detail')
            .setEntityId(member.id)
        -%}
        <a href="{{ url }}">
            {{ member }}
        </a>
    {%- else -%}
        {{ member }}
    {%- endif -%}

    {%- if not loop.last %}, {% endif -%}
{% else %}
    <span class="badge badge-secondary">None</span>
{% endfor %}

This way you could set the link destination in your CRUD controller and use the template everywhere. When no CRUD controller is not set, you will still have the individual items enumerated, but not as links.

In my TeamCrudController, I'm doing this:

public function configureFields(string $pageName): \Generator
{
    yield TextField::new('name')
        ->setDisabled()
    ;

    $members = AssociationField::new('members')
        ->hideOnForm()

    ;

    if (Crud::PAGE_DETAIL === $pageName) { // I want to see the number on INDEX, but the list on DETAIL
        $members
            ->setCrudController(EmployeeCrudController::class)
            ->setTemplatePath('admin/field/expanded_association_field.html.twig')
        ;
    }

    yield $members;
}

Alternatively, if you would like to become the default behaviour, override the EasyAdminBundle template by placing the following content in templates/bundles/EasyAdminBundle/crud/field/association.html.twig:

{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% if 'toMany' == field.customOptions.get('associationType') %}
    {% if ea.crud.currentPage == 'detail' %}
        {% for member in field.value %}
            {%- if field.customOption('crudControllerFqcn') is not empty -%}
                {%- set url = ea_url()
                    .setController(field.customOption('crudControllerFqcn'))
                    .setAction('detail')
                    .setEntityId(member.id)
                -%}
                <a href="{{ url }}">
                    {{- member -}}
                </a>
            {%- else -%}
                {{ member }}
            {%- endif -%}

            {%- if not loop.last %}, {% endif -%}
        {% else %}
            <span class="badge badge-secondary">None</span>
        {% endfor %}

    {% else %}
        <span class="badge badge-secondary">{{ field.formattedValue }}</span>
    {% endif %}
{% else %}
    {% if field.customOptions.get('relatedUrl') is not null %}
        <a href="{{ field.customOptions.get('relatedUrl') }}">{{ field.formattedValue }}</a>
    {% else %}
        {{ field.formattedValue }}
    {% endif %}
{% endif %}

Jan Klan
  • 667
  • 6
  • 16