EasyCorp / EasyCorp/EasyAdminBundle

Parameter-Specific State Providers for Hierarchical CRUDs

Open
#7,526 3 comments 2 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

1. The Core Problem: Mandatory Parameter Persistence

EasyAdmin's AdminUrlGenerator is hardcoded to prioritize entityId. When a developer defines a hierarchical route like this:

#[AdminRoute(path: '/{parentId}/sub-resource/{entityId}')]
class MyCrudController extends AbstractCrudController
{
    ...
}

the following occurs:

  • The Generator Error: When rendering links on the index or detail pages, the generator sees {parentId} in the route but has no source to fetch it from.
  • The Twig Crash: This results in a MissingMandatoryParametersException during the rendering of standard action buttons (Edit, Delete, etc.).
  • The Lifecycle Gap: Even if the URL is manually hacked together, the "Parent" context is lost when moving between index, new, and edit actions.
2. Proposed Solution: StateProviderInterface

I propose a single, focused interface. Its job is to resolve a specific value for a given parameter name based on the current context (the Request or the specific Entity being rendered in a row).

interface StateProviderInterface
{
    /**
     * @param string $paramName  The placeholder name (e.g., 'parentId')
     * @param AdminContext $context The current admin request context
     * @return mixed The value to be injected into the route (int, string, or object e.g Uuid::v4())
     */
    public function resolve(string $paramName, AdminContext $context): mixed;
}
3. Registration via #[AdminRoute]

The provider is linked directly to the controller via the existing AdminRoute attribute. This creates a clear contract: "If you use this route, use this provider to fill its placeholders (with the exception of entityId)."

#[AdminRoute(
    path: '/admin/{parentId}/category/{entityId}', 
    provider: CategoryStateProvider::class
)]
class CategoryCrudController extends AbstractCrudController 
{
    // ...
}

Usage Example

class CategoryStateProvider implements StateProviderInterface
{
    public function resolve(string $paramName, AdminContext $context): mixed
    {
        if ($paramName === 'parentId') {
            return $context->getEntity()->getInstance()->getParent()->getId();
        }

        return null; // This should fallback to the default exception for mandatory parameter
    }
}
4. How this Resolves the "Top-Level" Logic
A. Bulletproof URL Generation

When the ActionFactory generates a link for an EDIT action:

  1. It identifies that the route requires {parentId}.
  2. It calls the StateProvider::resolve('parentId', $context).
  3. The provider returns a value to fill the parentId (e.g., $entityDto->getInstance()->getParent()->getId()).
  4. The URL survives: The generator now has all required data to build a valid Symfony route.
B. Implicit Parent Association

Because the AdminContext now tracks the resolved parentId, standard CRUD methods become aware of the hierarchy:

  • createEntity(): Can automatically pull parentId from the context to assign the owner.
  • createIndexQueryBuilder(): Can automatically filter the list so users only see children of the current parentId.
5. Why this "Top-Level" Fix?

Instead of forcing developers to override every single action to manually inject a parentId, this solution fixes the Data Source for the AdminUrlGenerator.

By providing a single entity/value for a given parameter name, we allow EasyAdmin to handle Nested Resources and Multi-tenancy out of the box. It transforms EasyAdmin from a flat-file manager into a true hierarchical CMS.


Comparison: Current vs. Proposed
Feature Current State With StateProvider
Route Requirement Only entityId is auto-filled. parentId (or any name) is auto-filled.
Implementation Overriding 4-5 core methods. One Interface + Attribute.
Reliability Twig crashes on custom routes. Twig renders valid links dynamically.
Logic Location Scattered throughout Controller. Centralized in the Provider.

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 by tracing AdminUrlGenerator and ActionFactory for how mandatory route parameters, especially entityId, are resolved, then inspect AdminRoute and AdminContext integration. Compare the behavior of index, detail, new, and edit links for hierarchical routes. Done means a provider can supply non-entity parameters, missing values retain the existing exception behavior, and standard CRUD links preserve the parent context.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, symfony
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.