Skip to content

Custom entity name representation for autocompleteΒ #3753

@hhamon

Description

@hhamon

Hi everyone!

Allow one to customize the string representation of an entity for the autocomplete field:

The current implementation doesn't allow one to change the way an entity is represented in an autocomplete field. The EntityPaginator::getResultsAsJson method doesn't leverage any extension point.

class EntityPaginator
{
    public function getResultsAsJson(): string
    {
        foreach ($this->getResults() ?? [] as $entityInstance) {
            $entityDto = $this->entityFactory->createForEntityInstance($entityInstance);

            $jsonResult['results'][] = [
                'entityId' => $entityDto->getPrimaryKeyValueAsString(),
                'entityAsString' => $entityDto->toString(), // <------------ unable to change this hardcoded statement
            ];
        }

        $jsonResult['has_next_page'] = $this->hasNextPage();

        return json_encode($jsonResult);
    }
}

Of course, we can use the __toString() method but in my case I need a different representation that is not the one I use elsewhere with my already implemented __toString method.

I'm using the autocomple feature to allow the user find and select a US city among thousands cities in the database. As you know, you can have cities with the same name in different US states. My current Locality::__toString method already returns the city name because this is what I just need everywhere else I want to output a Locality entity as a string.

However, for the autocomplete field, I need to render the Locality instance a little bit differently to also show the US state name:

Nevada > Las Vegas
New Mexico > Las Vegas

For now, I'm fully overriding the autocomplete action of the base CRUD controller but that's a lot of work for just changing the way an entity is converted to string.

Potential solutions

  1. Add the possibility to configure a callback function in the CRUD object for the autocomplete only
public function configureCrud(Crud $crud): Crud
{
    return $crud
        ->setAutocompleteInstanceNormalizer(static fn (Locality $entity) => $entity->myCustomStringRendering());
}
  1. Inject this custom callback in the EntityPaginator::getResultsAsJson method
public function autocomplete(AdminContext $context): JsonResponse
{
    // ...

    return JsonResponse::fromJsonString($paginator->getResultsAsJson($context->getAutocompleteInstanceNormalizer()));
}
  1. The getResultsAsJson method will receive a callable or null value. If it's given a callable, then call it to compute the entity string name, otherwise fallback on __toString (if it exists) or the default implementation.
class EntityPaginator
{
    public function getResultsAsJson(callable $entityAsStringNormalizer = null): string
    {
        foreach ($this->getResults() ?? [] as $entityInstance) {
            $entityDto = $this->entityFactory->createForEntityInstance($entityInstance);

            // Solution 1: the normalizer is invoked here instead of the DTO::toString method
            $entityAsString = $entityAsStringNormalizer ? \call_user_func_array($callable, [$entityInstance]) : $entityDto->toString();

            // Solution 2: the normalizer is given to the DTO
            $entityAsString = $entityDto->toString($entityAsStringNormalizer);

            $jsonResult['results'][] = [
                'entityId' => $entityDto->getPrimaryKeyValueAsString(),
                'entityAsString' => $entityAsString
            ];
        }

        $jsonResult['has_next_page'] = $this->hasNextPage();

        return json_encode($jsonResult);
    }
}

What do you think of this pragmatic approach?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions