How to Define Custom Actions

One of the most powerful features of EasyAdmin is the possibility of defining your own actions. In addition to the built-in edit, list, new, search, delete and show actions, you can create any number of custom actions and display them in any view (edit, list, new, search, show).

There are two types of custom actions:

  • Method based actions, which execute a method of the AdminController class. This is the default type in EasyAdmin;
  • Route based actions, which execute the controller associated with the given route (and which can be defined anywhere in your application).

Method Based Actions

This is the most simple type of action and it just executes a method of the AdminController associated with the backend. Suppose that in your backend, one of the most common tasks is to restock a product adding 100 units to its current stock. Instead of editing a product, manually adding those 100 units and saving the changes, you can display a new Restock action in the list view.

First, define a new restock action using the actions option:

# config/packages/easy_admin.yaml
easy_admin:
    entities:
        Product:
            list:
                actions: ['restock']
        # ...

If you reload the backend, you’ll see a new Restock action displayed as a link in the Actions column of the Product entity listing. However, if you click on any of the Restock links, you’ll see an error because the restockAction() method is not defined in the AdminController.

Therefore, the next step is to create a custom AdminController in your Symfony application and to make it extend from the base AdminController provided by EasyAdmin. This will take you less than a minute and it’s explained in detail in the :ref:Customization Based on Controller Methods <overriding-the-default-controller> section. Make sure to read it before continuing.

Now you can define the restockAction() method in your own controller:

// src/Controller/AdminController.php
namespace App\Controller;

use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
// ...

class AdminController extends EasyAdminController
{
    // ...

    public function restockAction()
    {
        // controllers extending the EasyAdminController get access to the
        // following variables:
        //   $this->request, stores the current request
        //   $this->em, stores the Entity Manager for this Doctrine entity

        // change the properties of the given entity and save the changes
        $id = $this->request->query->get('id');
        $entity = $this->em->getRepository(Product::class)->find($id);
        $entity->setStock(100 + $entity->getStock());
        $this->em->flush();

        // redirect to the 'list' view of the given entity ...
        return $this->redirectToRoute('easyadmin', [
            'action' => 'list',
            'entity' => $this->request->query->get('entity'),
        ]);

        // ... or redirect to the 'edit' view of the given entity item
        return $this->redirectToRoute('easyadmin', [
            'action' => 'edit',
            'id' => $id,
            'entity' => $this->request->query->get('entity'),
        ]);
    }
}

And that’s it! Click again on the Restock action and everything will work as expected. Custom actions can define any of the properties available for the built-in actions:

# config/packages/easy_admin.yaml
easy_admin:
    entities:
        Product:
            list:
                actions:
                    - { name: 'restock', icon: 'plus-square' }
        # ...

The inheritance of actions is also applied to custom actions:

# config/packages/easy_admin.yaml
easy_admin:
    list:
        # show the 'restock' action for all entities except those which remove it
        actions:
            - { name: 'restock', icon: 'plus-square' }

    entities:
        Product:
            # ...
        User:
            list:
                actions: ['-restock']
            # ...

Route Based Actions

This type of actions allows you to execute any controller defined in your existing application, without the need to define a custom AdminController. In this case, the name of the action is treated as the route name and you must add a type option with the route value:

# config/packages/easy_admin.yaml
easy_admin:
    entities:
        Product:
            list:
                actions:
                    - { name: 'product_restock', type: 'route' }

                    # these actions can define route parameters too
                    - { name: 'product_restock', type: 'route', route_parameters: { 'notify': true, 'amount': 30 } }
        # ...

Route based actions are displayed as regular links or buttons, but they don’t link to the usual easyadmin route but to the route configured by the action. In addition, the route is passed two parameters in the query string: entity (the name of the entity) and, when available, the id of the related entity.

Following the same example as above, the controller of this route based action would look as follows:

// src/Controller/ProductController.php
namespace App\Controller;

// ...
use Symfony\Component\HttpFoundation\Request;

class ProductController extends Controller
{
    // ...

    /**
     * @Route(path = "/admin/product/restock", name = "product_restock")
     * @Security("has_role('ROLE_ADMIN')")
     */
    public function restockAction(Request $request)
    {
        // change the properties of the given entity and save the changes
        $em = $this->getDoctrine()->getManager();
        $repository = $this->getDoctrine()->getRepository(Product::class);

        $id = $request->query->get('id');
        $entity = $repository->find($id);
        $entity->setStock(100 + $entity->getStock());
        $em->flush();

        // redirect to the 'list' view of the given entity ...
        return $this->redirectToRoute('easyadmin', [
            'action' => 'list',
            'entity' => $request->query->get('entity'),
        ]);

        // ... or redirect to the 'edit' view of the given entity item
        return $this->redirectToRoute('easyadmin', [
            'action' => 'edit',
            'id' => $id,
            'entity' => $request->query->get('entity'),
        ]);
    }
}

Similarly to method based actions, you can configure any option for these actions (icons, labels, etc.) and you can also leverage the action inheritance mechanism.

Custom Templates for Actions

The link to the action is rendered using a default template (@EasyAdmin/default/action.html.twig) which displays the icon and label of the action according to its configuration.

If you prefer to use your own template to render that link, define the template option in the action configuration:

# config/packages/easy_admin.yaml
easy_admin:
    entities:
        Product:
            show:
                actions:
                    - { name: 'restock', template: 'admin/restock_action.html.twig' }
        # ...

This option is not only useful to customize the action link, but to display it or hide it depending on some conditions. For example, if you only want to display the Restock action when the stock of the item is less than 10, create this template for the action:

{# templates/admin/restock_action.html.twig #}

{# if the stock is low, include the default action template to render the
   action link. Otherwise, don't include the template so the link is not displayed #}
{% if item.stock < 10 %}
    {{ include('@EasyAdmin/default/action.html.twig') }}
{% endif %}

Batch Actions

Batch actions are the actions which are applied to multiple items at the same time. They are only available in the views that display more than one item: list and search. The only built-in batch action is delete, but you can create your own batch actions.

Imagine that you manage users with a User entity and a common administration task is to approve their sign ups. Instead of creating a normal approve action as explained in the previous section, create a batch action to be more productive and approve multiple users at once.

The first step is to :ref:create a custom AdminController <overriding-the-default-controller>. Then, create a new method to handle the batch action. The method name must follow the pattern action_name + BatchAction() and they receive an array argument with the IDs of the entities the action should be applied to. In this example, create an approveBatchAction() method:

// src/Controller/AdminController.php
namespace App\Controller;

use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
// ...

class AdminController extends EasyAdminController
{
    // ...

    public function approveBatchAction(array $ids)
    {
        $class = $this->entity['class'];
        $em = $this->getDoctrine()->getManagerForClass($class);

        foreach ($ids as $id) {
            $user = $em->find($id);
            $user->approve();
        }

        $this->em->flush();

        // don't return anything or redirect to any URL because it will be ignored
        // when a batch action finishes, user is redirected to the original page
    }
}

Now that the action logic is ready, :ref:configure the batch action <batch-actions> to add it to the backend and define its icon, label, etc.