EasyAdmin 4 for admin panel based on PHP 8.0 and Symfony 6.0: Install and create a sample

( modified )


EasyAdmin enables you to create easily admin panel bound to storage such as RDBMS.

It is one of the bundles of Symfony, a powerful and flexible PHP framework, also requiring Doctrine ORM entities.



sudo can be used instead of doas below.



$ doas apk add php8-fpm

PHP extensions

$ doas apk add php8-gd php8-curl php8-dom php8-iconv php8-json php8-mbstring php8-openssl php8-pdo php8-session php8-tokenizer php8-session php8-xml php8-simplexml php8-xmlwriter php8-zip php8-ctype php8-intl php8-opcache php8-pcntl
Case SQLite3
$ doas apk add sqlite
$ doas apk add php8-pdo_sqlite
Case MariaDB
$ doas apk add mariadb-client
$ doas apk add php8-pdo_mysql php8-mysqlnd

PHP-FPM daemon

$ # enable
$ doas rc-update add php_fpm
$ # start
$ doas rc-service php_fpm start


$ doas apk add caddy
$ # enable
$ doas rc-update add caddy
$ # start
$ doas rc-service caddy start

log {
	output file "/var/log/caddy/caddy.log"

root * "/var/local/appbc/public"

@blocked {
	path /.git*
respond @blocked 403

@assets {
	path_regexp ^.*(\.html|\.css|\.js|\.jpg|\.jpeg|\.png|\.webp|\.gif|\.svg|\.woff2)$
file_server @assets

@approutes {
	path_regexp ^.*/[^\./]*/?$
#rewrite @approutes /index.php?{query}&c={path}
rewrite @approutes /index.php?{query}

@indexphp {
	path /index.php*
#php_fastcgi @indexphp "unix//run/php-fpm7/php-fpm.pid"
php_fastcgi @indexphp ""


$ composer create-project symfony/website-skeleton "<project-dir>"

$ cd "<project-dir>"

symfony welcome



$ composer require easycorp/easyadmin-bundle

The output was:

Using version ^4.0 for easycorp/easyadmin-bundle
./composer.json has been updated
Running composer update easycorp/easyadmin-bundle
Loading composer repositories with package information
Restricting packages listed in "symfony/symfony" to "6.0.*"
Updating dependencies
Lock file operations: 3 installs, 0 updates, 0 removals
  - Locking easycorp/easyadmin-bundle (v4.0.6)
  - Locking symfony/polyfill-uuid (v1.24.0)
  - Locking symfony/uid (v6.0.3)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Downloading symfony/polyfill-uuid (v1.24.0)
  - Downloading symfony/uid (v6.0.3)
  - Downloading easycorp/easyadmin-bundle (v4.0.6)
  - Installing symfony/polyfill-uuid (v1.24.0): Extracting archive
  - Installing symfony/uid (v6.0.3): Extracting archive
  - Installing easycorp/easyadmin-bundle (v4.0.6): Extracting archive
Generating optimized autoload files
110 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Symfony operations: 1 recipe (...)
  - Configuring easycorp/easyadmin-bundle (>=3.0): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install public [OK]
 What's next? 

Some files have been created and/or updated to configure your new packages.
Please review, edit and commit them: these files are yours.

Create a Dashboard

$ php bin/console make:admin:dashboard

I was asked a couple of times and used the default:

 Which class name do you prefer for your Dashboard controller? [DashboardController]:

 In which directory of your project do you want to generate "DashboardController"? [src/Controller/Admin/]:

 [OK] Your dashboard class has been successfully generated.                                                             

 Next steps:
 * Configure your Dashboard at "src/Controller/Admin/DashboardController.php"
 * Run "make:admin:crud" to generate CRUD controllers and link them from the Dashboard.

Here, you can see the default dashboard!!

easyadmin default dashboard

Well, there is no menu! Let’s create your own dashboard and add menus to it.

Create the first dashboard of your own

Let’s create a custom template:

$ mkdir templates/admin

$ nvim templates/admin/index.html.twig

Write just this in it:

+ {% extends '@EasyAdmin/page/content.html.twig' %}

Then, edit the controller (src/Controller/Admin/DashboardController.php):

  class DashboardController extends AbstractDashboardController
      #[Route('/admin', name: 'admin')]
      public function index(): Response
-         return parent::index();
+         return $this->render('admin/index.html.twig');

Then prepare assets:

$ yarn encore dev
$ # alternatively, `npm run dev` is available

Besides, you sometimes have to clear cache.

$ php(8) bin/console cache:clear

Reload the dashboard page, and you will see it’s replaced with your own.

your own dashboard

There are few menus. Next, let’s implement to manage table records.

Prepare an entity

Let’s make some entity. It’s dealt with by Symfony and Doctrine.

Use make bundle. You will be asked on its schema:

$ php bin/console make:entity

Then make it substantial:

$ php bin/console make:migration
$ # reply yes
$ php bin/console doctrine:migrations:migrate
$ # reply yes

Create a CRUD controller

It’s EasyAdmin’s turn.

$ php bin/console make:admin:crud

The output was:

 Which Doctrine entity are you going to manage with this CRUD controller?:
  [0] App\Entity\(...)
 > 0

 Which directory do you want to generate the CRUD controller in? [src/Controller/Admin/]:

 Namespace of the generated CRUD controller [App\Controller\Admin]:

 [OK] Your CRUD controller class has been successfully generated.                                                       

 Next steps:
 * Configure your controller at "src/Controller/Admin/(...)CrudController.php"
 * Read EasyAdmin docs: https://symfony.com/doc/master/bundles/EasyAdminBundle/index.html

Add a menu on the entity:

+ use App\Entity\SamplePack;
  class DashboardController extends AbstractDashboardController
      public function configureMenuItems(): iterable
          yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
-         // yield MenuItem::linkToCrud('The Label', 'fas fa-list', EntityClass::class);
+         yield MenuItem::linkToCrud('The Label', 'fas fa-list', SamplePack::class);

Then, reload the page.

menu to entity

Yay, got it. Let’s click “The Label” on the left side:

entity page

We can manage storage here!!


I tried a new feature released just at the beginning of the year, menu badges.

To show a count:

-         yield MenuItem::linkToCrud('The Label', 'fas fa-list', SamplePackClass::class);
+         yield MenuItem::linkToCrud('The Label', 'fas fa-list', SamplePackClass::class)
+         ->setBadge(468, 'success');

badge of number


To show a message:

-         yield MenuItem::linkToCrud('The Label', 'fas fa-list', SamplePackClass::class);
+         yield MenuItem::linkToCrud('The Label', 'fas fa-list', SamplePackClass::class)
+         ->setBadge('Ciao!', 'background: transparent; color: #44bb88; outline: 2px solid white');

badge of string

Sweet 💃💃

Comments or feedbacks are welcomed and appreciated.