Search by

kikwik / easyadmin-utils-bundle

kikwik

Utility for easycorp/easyadmin-bundle v5.x: Dynamic Paginator Page Size, Dynamic index fields, Relative time, Sticky header, Sticky footer

Package info

github.com/kikwik/easyadmin-utils-bundle

Type:symfony-bundle

pkg:composer/kikwik/easyadmin-utils-bundle

Statistics

Installs: 12

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.5 2026-08-17 10:31 UTC

This package is auto-updated.

Last update: 2026-10-18 08:28:01 UTC


README

Utility for easycorp/easyadmin-bundle v5.x: Dynamic Paginator Page Size, Dynamic index fields, Relative time, Sticky header, Sticky footer

Features

  • Dynamic Paginator Page Size: Paginator page size can be choosen by the user per controller
  • Dynamic index fields: Field shown on index page can be choosen by the user per controller
  • Relative time: Add a tooltip with distance of time in words
  • Sticky header: Table head can be stiky on top in the index pages
  • Sticky footer: Footer with pagination can be sticky on bottom in the index pages

Installation

Make sure Composer is installed globally, as explained in the installation chapter of the Composer documentation.

Open a command console, enter your project directory and execute:

$ composer require kikwik/easyadmin-utils-bundle

Usage

Dynamic Paginator Page Size and Dynamic index fields

  • Extends your EasyAdmin crud controller from AbstractConfigurableCrudController
  • call parent methods in configureCrud, and configureActions.
  • DO NOT define configureFields method, instead define a configureAvailableFields with the same signature
  • in case you need some services inside the controller remember to call the parent constructor with the CrudConfiguration parameter

an example for src/Controller/Admin/UserCrudController.php:

namespace App\Controller\Admin;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use Kikwik\EasyAdminUtilsBundle\Controller\AbstractConfigurableCrudController;
use Kikwik\EasyAdminUtilsBundle\Service\CrudConfiguration;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class UserCrudController extends AbstractConfigurableCrudController
{
    public function __construct(
        CrudConfiguration $crudConfiguration,
        private UserPasswordHasherInterface $userPasswordHasher,
        private UrlGeneratorInterface $urlGenerator,
    )
    {
        parent::__construct($crudConfiguration); // call the parent constructor
    }

    public static function getEntityFqcn(): string
    {
        return User::class;
    }

    public function configureCrud(Crud $crud): Crud
    {
        return parent::configureCrud($crud) // this will eventually set the $crud->setPaginatorPageSize()
        // set here your crud configuration
        ->setEntityLabelInPlural('Utenti')
            ->setEntityLabelInSingular(
                fn (?User $user, ?string $pageName) => $user ? sprintf('Utente %s', $user) : 'Utente'
            )
            ;
    }

    public function configureActions(Actions $actions): Actions
    {
        $impersonateAction = Action::new('impersonate', 'Imporsona', 'fa fa-user-secret')
            ->linkToUrl(function (?User $user) {
                return $this->urlGenerator->generate('app_home', ['_switch_user' => $user?->getUserIdentifier()]);
            })
        ;

        return parent::configureActions($actions) // this will add the "Configure" global action
        // add here your custom actions
        ->add(Crud::PAGE_INDEX, $impersonateAction)
            ->add(Crud::PAGE_DETAIL, $impersonateAction)
            ->setPermission('impersonate','ROLE_ALLOWED_TO_SWITCH')
            ;
    }

    public function configureAvailableFields(string $pageName): iterable
    {
        $roleHierarchy = $this->getParameter('security.role_hierarchy.roles');
        $rolesChoices = [];
        foreach($roleHierarchy as $role => $children)
        {
            $label = str_replace('ROLE_', '', $role);
            $value = $role;

            $rolesChoices[$label] = $value;
        }

        return [
            FormField::addColumn('col-xl-4'),
            FormField::addFieldset('Credenziali'),
            TextField::new('email'),
            TextField::new('plainPassword', 'Nuova Password')
                ->onlyOnForms()
                ->setFormType(RepeatedType::class)
                ->setFormTypeOptions([
                    'type' => PasswordType::class,
                    'required' => false,
                    'first_options' => ['label' => 'Nuova Password'],
                    'second_options' => ['label' => 'Conferma Password'],
                    'invalid_message' => 'Le password non coincidono.',
                ]),
            ChoiceField::new('roles', 'Permessi')
                ->setPermission('ROLE_SUPER_ADMIN')
                ->setChoices($rolesChoices)
                ->allowMultipleChoices()
                ->renderExpanded()
                ->formatValue(function($value) {
                    $badges = '';
                    if(is_array($value))
                    {
                        foreach($value as $role)
                        {
                            switch($role)
                            {
                                case 'ROLE_SUPER_ADMIN':
                                case 'ROLE_DEVELOPER':
                                    $badgeClass = 'badge-danger';
                                    break;
                                default:
                                    $badgeClass = 'badge-secondary';
                                    break;
                            }
                            $label = str_replace('ROLE_', '', $role);
                            $badges .= sprintf('<span class="badge %s">%s</span>', $badgeClass, $label);
                        }
                    }
                    return $badges;
                }),
            BooleanField::new('isEnabled'),

            FormField::addFieldset('Accessi')->hideOnForm(),
            IntegerField::new('loginCount')->hideOnForm(),
            DateTimeField::new('lastLoginAt')->hideOnForm(),
            TextField::new('lastLoginFromIp')->hideOnForm()->hideOnIndex(),
            DateTimeField::new('previousLoginAt')->hideOnForm(),
            TextField::new('previousFromIp')->hideOnForm()->hideOnIndex(),

            FormField::addColumn('col-xl-4'),
            FormField::addFieldset('Password')->hideOnForm(),
            DateTimeField::new('changePasswordRequestedAt')->hideOnForm()->hideOnIndex(),
            DateTimeField::new('passwordChangedAt')->hideOnForm()->hideOnIndex(),
            TextField::new('passwordChangedFromIp')->hideOnForm()->hideOnIndex(),

            FormField::addColumn('col-xl-4'),
            FormField::addFieldset('Creazione')->hideOnForm(),
            DateTimeField::new('createdAt')->hideOnForm()->hideOnIndex(),
            TextField::new('createdBy')->hideOnForm()->hideOnIndex(),
            TextField::new('createdFromIp')->hideOnForm()->hideOnIndex(),

            FormField::addFieldset('Modifica')->hideOnForm(),
            DateTimeField::new('updatedAt')->hideOnForm()->hideOnIndex(),
            TextField::new('updatedBy')->hideOnForm()->hideOnIndex(),
            TextField::new('updatedFromIp')->hideOnForm()->hideOnIndex(),
        ];
    }

    public function persistEntity(EntityManagerInterface $entityManager, object $entityInstance): void
    {
        $this->updatePassword($entityInstance);
        parent::persistEntity($entityManager, $entityInstance);
    }

    public function updateEntity(EntityManagerInterface $entityManager, object $entityInstance): void
    {
        $this->updatePassword($entityInstance);
        parent::updateEntity($entityManager, $entityInstance);
    }


    private function updatePassword($entityInstance)
    {
        if ($entityInstance instanceof User && $entityInstance->getPlainPassword()) {
            $hashedPassword = $this->userPasswordHasher->hashPassword($entityInstance, $entityInstance->getPlainPassword());
            $entityInstance->setPassword($hashedPassword);
        }
    }
}

Relative time, Sticky header and Sticky footer

In you dashboard controller add these assets:

namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;

class DashboardController extends AbstractDashboardController
{
    public function configureAssets(): Assets
    {
        return Assets::new()
            ->addJsFile('bundles/kikwikeasyadminutils/js/relative-time.js')
            ->addCssFile('bundles/kikwikeasyadminutils/css/index-sticky-header.css')
            ->addCssFile('bundles/kikwikeasyadminutils/css/index-sticky-footer.css')
            ;
    }
}