Repository: EasyCorp/EasyAdminBundle (language: php)

Issue:
Error “Some mandatory parameters are missing ("entityId")” with pretty URLs
**Describe the bug**
I enabled #6467 in one project, by following this upgrade guide: https://github.com/EasyCorp//EasyAdminBundle/blob/ddcb815d07fcda841ded26ddde9970470bafae19/UPGRADE.md#added-pretty-urls-support

Now I have this error:

> An exception has been thrown during the rendering of a template ("Some mandatory parameters are missing ("entityId") to generate a URL for route "admin_badge_detail".").

**To Reproduce**

I don't know yet what is the root issue, my configuration is pretty simple:

```yaml
# config/routes/easyadmin.yaml
easyadmin:
    resource: '../src/Controller/Admin/'
    type: easyadmin.routes

```

<details>
<summary><kbd>DashboardController</kbd></summary>

```php
// src/Controller/Admin/DashboardController.php

class DashboardController extends AbstractDashboardController
{
    // […]

    #[Route(
        '/admin',
        name: 'admin'
    )]
    public function index(): Response
    {
        $numberOfSites = $this->siteRepository->count();
        // […]

        return $this->render('admin/dashboard/index.html.twig', [
            'number_of_sites' => $numberOfSites,
            // […]
        ]);
    }

    public function configureDashboard(): Dashboard
    {
        return Dashboard::new()
            ->setTitle('App');
    }

    public function configureMenuItems(): iterable
    {
        yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');

        yield MenuItem::linkToCrud('Site', 'fas fa-sitemap', Site::class);
        yield MenuItem::linkToCrud('Badge', 'fas fa-medal', Badge::class);
        yield MenuItem::linkToCrud('User', 'fas fa-user', User::class);
        yield MenuItem::linkToCrud('UserSite', 'fas fa-user-plus', UserSite::class);
        yield MenuItem::linkToCrud('UserSiteBadge', 'fas fa-user-tag', UserSiteBadge::class);

        yield MenuItem::linkToUrl('Visit public website', 'fa fa-diamond-turn-right', '/');
    }
}
```
</details>


<details>
<summary><kbd>BadgeCrudController</kbd></summary>

```php
// src/Controller/Admin/BadgeCrudController.php


class BadgeCrudController extends AbstractCrudController
{
    public static function getEntityFqcn(): string
    {
        return Badge::class;
    }

    public function configureActions(Actions $actions): Actions
    {
        return $actions
            ->add(Crud::PAGE_INDEX, Action::DETAIL)
            ->remove(Crud::PAGE_INDEX, Action::NEW)
            ->remove(Crud::PAGE_INDEX, Action::EDIT)
            ->remove(Crud::PAGE_INDEX, Action::DELETE)
            ->remove(Crud::PAGE_DETAIL, Action::EDIT)
            ->remove(Crud::PAGE_DETAIL, Action::DELETE)
        ;
    }

    public function configureFields(string $pageName): iterable
    {
        return [
            IdField::new('id'),
            TextField::new('name'),
            TextField::new('description'),
            NumberField::new('badgeId'),
            NumberField::new('rank'),
            NumberField::new('badgeType'),
            NumberField::new('awardCount'),
            AssociationField::new('site'),
            NumberField::new('score'),
            DateTimeField::new('lastListed'),
            NumberField::new('errorCount'),
            AssociationField::new('userSites'),
        ];
    }
}
```
</details>

`easycorp/easyadmin-bundle` is `4.14.5`, `4.15.0` is affected too.

**Workaround**

See the comment below:

- https://github.com/EasyCorp/EasyAdminBundle/issues/6550#issuecomment-2483967864

**Additional context**

Removing `yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');` fixes the issue.

It looks like these lines are the root issue: https://github.com/EasyCorp/EasyAdminBundle/blob/cfeb013cfff2887a08ab9b79c6dc63ba50f32044/src/Factory/MenuFactory.php#L155-L157

Commenting these 3 lines while restoring `linkToDashboard` fixes the issue.

Disabling pretty URLs (and deleting the cache) also fixes the issue.

The issue is reproducible on CI:
- #6559

Related:

- #6514
- #6523
- #6535


Discussion / hints:
### Workarounds

```php
yield MenuItem::linkToRoute('Dashboard', 'fa fa-home', 'admin');
```

generates a strange link: `/admin?routeName=admin`

But 

```php
yield MenuItem::linkToUrl('Dashboard', 'fa fa-home', '/admin/');
```

fixes the issue.
I can concur, have the same issue and will use `MenuItem::linkToUrl()` for now.
I have the same issue too. The solution with `MenuItem::linkToUrl` is not the best one because the state "active" seems not supported with `linkToUrl`.
> I have the same issue too. The solution with `MenuItem::linkToUrl` is not the best one because the state "active" seems not supported with `linkToUrl`.

Indeed, that's why I labeled it as *workaround*.

Note that "active" links may be broken with pretty URLs: 

- #6541
I'm closing this issue since it's a duplicate of:

- #6523
I have the same problem in my project. Perhaps the problem are the UUID as identifiers in my case ( https://example.domain/admin/reminder/019344f2-6192-7e64-9190-cb3b209e5bfb/edit ). I will check this in the code in detail shortly.

Regards,
Tim
> I have the same problem in my project. Perhaps the problem are the UUID as identifiers in my case ( https://example.domain/admin/reminder/019344f2-6192-7e64-9190-cb3b209e5bfb/edit ). I will check this in the code in detail shortly.

I have this error and I don't use UUID, so I don't think it makes a difference.
This error also occurs with version 4.15.1, and the use of `MenuItem::linkToCrud`. It renders the editing action unusable for any entity. Commenting on `MenuItem::linkToDashboard` also temporarily solves the problem in my case.

Fix patch:
diff --git a/src/Router/AdminUrlGenerator.php b/src/Router/AdminUrlGenerator.php
index 2bae246cb1..0e6b515f3d 100644
--- a/src/Router/AdminUrlGenerator.php
+++ b/src/Router/AdminUrlGenerator.php
@@ -269,6 +269,12 @@ public function generateUrl(): string
         );
         ksort($routeParameters, \SORT_STRING);
 
+        // if no route parameters are passed, the route doesn't point to any CRUD controller
+        // action or to any custom action/route; consider it a link to the current dashboard
+        if ([] === $routeParameters) {
+            return $this->urlGenerator->generate($this->dashboardRoute);
+        }
+
         $context = $this->adminContextProvider->getContext();
         $usePrettyUrls = null !== $context && $context->usePrettyUrls();
         $urlType = null !== $context && false === $context->getAbsoluteUrls() ? UrlGeneratorInterface::ABSOLUTE_PATH : UrlGeneratorInterface::ABSOLUTE_URL;


Test patch:
diff --git a/tests/Controller/PrettyUrls/PrettyUrlsControllerTest.php b/tests/Controller/PrettyUrls/PrettyUrlsControllerTest.php
index 2404d0d512..5e96892b1f 100644
--- a/tests/Controller/PrettyUrls/PrettyUrlsControllerTest.php
+++ b/tests/Controller/PrettyUrls/PrettyUrlsControllerTest.php
@@ -129,7 +129,7 @@ public function testDefaultMainMenuUsesPrettyUrls()
         $crawler = $client->request('GET', '/admin/pretty/urls/blog_post');
 
         $this->assertSame('/admin/pretty/urls', $crawler->filter('#header-logo a.logo')->attr('href'), 'The main Dashboard logo link points to the dashboard entry URL');
-        $this->assertSame('http://localhost/admin/pretty/urls/blog_post', $crawler->filter('li.menu-item a:contains("Dashboard")')->attr('href'), 'The Dashboard link inside the menu points to the first entity of the menu');
+        $this->assertSame('/admin/pretty/urls', $crawler->filter('li.menu-item a:contains("Dashboard")')->attr('href'), 'The Dashboard link inside the menu points to the dashboard entry URL (even if it later redirects to some other entity)');
         $this->assertSame('http://localhost/admin/pretty/urls/blog_post', $crawler->filter('li.menu-item a:contains("Blog Posts")')->attr('href'));
         $this->assertSame('http://localhost/admin/pretty/urls/category', $crawler->filter('li.menu-item a:contains("Categories")')->attr('href'));
     }
@@ -142,7 +142,7 @@ public function testCustomMainMenuUsesPrettyUrls()
         $crawler = $client->request('GET', '/second/dashboard/user-editor/custom/path-for-index');
 
         $this->assertSame('/second/dashboard', $crawler->filter('#header-logo a.logo')->attr('href'), 'The main Dashboard logo link points to the dashboard entry URL');
-        $this->assertSame('http://localhost/second/dashboard/user-editor/custom/path-for-index', $crawler->filter('li.menu-item a:contains("Dashboard")')->attr('href'), 'The Dashboard link inside the menu points to the first entity of the menu');
+        $this->assertSame('/second/dashboard', $crawler->filter('li.menu-item a:contains("Dashboard")')->attr('href'), 'The Dashboard link inside the menu points to the dashboard entry URL (even if it later redirects to some other entity)');
         $this->assertSame('http://localhost/second/dashboard/user-editor/custom/path-for-index', $crawler->filter('li.menu-item a:contains("Users")')->attr('href'));
     }
 
@@ -160,6 +160,19 @@ public function testDefaultActionsUsePrettyUrls()
         $this->assertMatchesRegularExpression('#http://localhost/admin/pretty/urls/blog_post/1/edit\?csrfToken=.*&fieldName=content#', $crawler->filter('td.field-boolean input[type="checkbox"]')->attr('data-toggle-url'));
     }
 
+    /**
+     * @dataProvider provideDefaultPageUrls
+     */
+    public function testDefaultPagesWithPrettyUrls(string $uri)
+    {
+        $client = static::createClient();
+        $client->followRedirects();
+
+        $client->request('GET', $uri);
+
+        $this->assertResponseIsSuccessful();
+    }
+
     public function testCustomActionsUsePrettyUrls()
     {
         $client = static::createClient();
@@ -198,4 +211,11 @@ public function testCustomSortLinksUsePrettyUrls()
         $this->assertSame('http://localhost/second/dashboard/user-editor/custom/path-for-index?page=1&sort%5Bname%5D=DESC', $crawler->filter('th.searchable a')->eq(1)->attr('href'));
         $this->assertSame('http://localhost/second/dashboard/user-editor/custom/path-for-index?page=1&sort%5Bemail%5D=DESC', $crawler->filter('th.searchable a')->eq(2)->attr('href'));
     }
+
+    public static function provideDefaultPageUrls(): iterable
+    {
+        yield 'Create' => ['/admin/pretty/urls/category/new'];
+        yield 'Read' => ['/admin/pretty/urls/category/1'];
+        yield 'Update' => ['/admin/pretty/urls/category/1/edit'];
+    }
 }
diff --git a/tests/Router/AdminUrlGeneratorTest.php b/tests/Router/AdminUrlGeneratorTest.php
index 809f6999ff..2a91ae4b5a 100644
--- a/tests/Router/AdminUrlGeneratorTest.php
+++ b/tests/Router/AdminUrlGeneratorTest.php
@@ -69,7 +69,7 @@ public function testUnsetAll()
 
         $adminUrlGenerator->set('foo1', 'bar1');
         $adminUrlGenerator->unsetAll();
-        $this->assertSame('http://localhost/admin', $adminUrlGenerator->generateUrl());
+        $this->assertSame('/admin', $adminUrlGenerator->generateUrl());
     }
 
     public function testUnsetAllExcept()