EasyCorp / EasyCorp/EasyAdminBundle

AdminUrlGenerator loses filters/page/sort when generating a URL after a linkToRoute() action was generated in the same request

Open Beginner friendly
#7,734 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
4.3k
Forks
1.1k
Avg merge
8d 2h
Merged PRs (30d)
11

Description

Describe the bug

AdminUrlGenerator drops previously-set query parameters (filters, page, sort, query, batch-action params) when generating a URL for a linkToCrudAction()-based action, if that URL is generated after another action's URL was generated via linkToRoute() in the same request.

Concretely: on a filtered index page with a per-row action using linkToRoute() and a global action using linkToCrudAction() (e.g. a custom "export" action), the global action's rendered href is missing filters[...] entirely — even though every other link on the page (sort links, per-row links, the filters modal link) correctly keeps them.

To Reproduce

EasyAdmin v4.29.14.

  1. In a CRUD controller, add a per-row Action using ->linkToRoute('some_route', fn ($entity) => [...]).
  2. Add a global Action using ->linkToCrudAction('someMethod')->createAsGlobalAction().
  3. Add a filter via configureFilters().
  4. Visit the index page with the filter applied (e.g. ?crudAction=index&crudControllerFqcn=...&filters[status][comparison]=%21%3D&filters[status][value][0]=110).
  5. Inspect the global action's rendered href — it only contains crudAction/crudControllerFqcn, missing filters[...].

(OPTIONAL) Additional context

Root cause: AdminUrlGenerator::generateUrl() ends with a reset that's needed so each URL generation starts fresh — the surrounding comment says so explicitly:

// this is important to start the generation of each URL from the same initial state
// otherwise, some parameters used when generating some URL could leak to other URLs
$this->isInitialized = false;
return $url;

But three earlier returns in the same method skip this reset, which seems to contradict that stated intent:

if ([] === $routeParameters) {
    return $this->urlGenerator->generate($this->dashboardRoute, [], $urlType); // no reset
}

if (null !== $routeName = $this->get(EA::ROUTE_NAME)) {
    $adminRoutes = $this->cache->getItem(AdminRouteGenerator::CACHE_KEY_ROUTE_TO_FQCN)->get();
    if (null !== $adminRoutes && \array_key_exists($routeName, $adminRoutes)) {
        return $this->urlGenerator->generate($routeName, $routeParameters[EA::ROUTE_PARAMS] ?? [], $urlType); // no reset
    }
    return $this->urlGenerator->generate($this->dashboardRoute, $routeParameters, $urlType); // no reset
}

The EA::ROUTE_NAME branch is exactly what linkToRoute()-based actions hit (via setRoute()->generateUrl()). Since $isInitialized stays true afterwards, the next unsetAllExcept()/get()/set() call on the same instance skips re-reading the current request and instead reuses the stale $this->routeParameters left over from that setRoute() call — which only has dashboardControllerFqcn/routeName/routeParams, no filters. This isn't limited to legacy/non-pretty-URL apps — the EA::ROUTE_NAME check happens before the pretty-URLs branching, so it affects both.

Suggested fix — add $this->isInitialized = false; before each early return:

--- a/src/Router/AdminUrlGenerator.php
+++ b/src/Router/AdminUrlGenerator.php
@@ -288,15 +288,21 @@ final class AdminUrlGenerator implements \Stringable, AdminUrlGeneratorInterface
         if ([] === $routeParameters) {
+            $this->isInitialized = false;
+
             return $this->urlGenerator->generate($this->dashboardRoute, [], $urlType);
         }
 
         if (null !== $routeName = $this->get(EA::ROUTE_NAME)) {
             $adminRoutes = $this->cache->getItem(AdminRouteGenerator::CACHE_KEY_ROUTE_TO_FQCN)->get();
             if (null !== $adminRoutes && \array_key_exists($routeName, $adminRoutes)) {
+                $this->isInitialized = false;
+
                 return $this->urlGenerator->generate($routeName, $routeParameters[EA::ROUTE_PARAMS] ?? [], $urlType);
             }
 
+            $this->isInitialized = false;
+
             return $this->urlGenerator->generate($this->dashboardRoute, $routeParameters, $urlType);
         }

Verified locally (applied via cweagans/composer-patches) — fixes the issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/Router/AdminUrlGenerator.php, especially generateUrl() and its three early returns. Reproduce the described linkToRoute() then linkToCrudAction() sequence with an applied filter, and verify that generated URLs retain filters, pagination, sorting, query, and batch-action parameters after the fix.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, symfony
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.