EasyAdmin 4 for admin panel based on PHP 8 and Symfony 6: Install and create a sample
Tags: php, symfony, doctrine, easyadmin
Published: 2022-03-07 12:45:44

## Summary

[EasyAdmin](https://symfony.com/bundles/EasyAdminBundle/current/index.html) enables you to create easily admin panel bound to storage such as RDBMS.

It is one of the bundles of [Symfony](https://symfony.com/), a powerful and flexible [PHP](https://www.php.net/) framework, also requiring [Doctrine](https://www.doctrine-project.org/) ORM entities.

### Environment

- OS: [Alpine Linux](https://www.alpinelinux.org/) 3.15
  - [Docker](https://www.docker.com/) VM
- Web: [Caddy](https://caddyserver.com/) 2
- App: EasyAdmin 4.0
  - based on PHP 8.0, Symfony 6.0 and Doctrine 2.11
- DB: [MariaDB](https://mariadb.org/) 10.6

## Tutorial

[`sudo`](https://www.sudo.ws/) can be used instead of [`doas`](https://pkgs.alpinelinux.org/package/edge/main/x86/doas) below.

### PHP

#### PHP8 and PHP-FPM

```console
$ doas apk add php8-fpm
```

#### PHP extensions

```console
$ 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

```console
$ doas apk add sqlite
$ doas apk add php8-pdo_sqlite
```

##### Case MariaDB

```console
$ doas apk add mariadb-client
$ doas apk add php8-pdo_mysql php8-mysqlnd
```

### PHP-FPM daemon

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

### Caddy

```console
$ doas apk add caddy
```

```console
$ # enable
$ doas rc-update add caddy
$ # start
$ doas rc-service caddy start
```

```caddyfile
your.host

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 "127.0.0.1:9000"
```

### Symfony

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

$ cd "<project-dir>"
```

![symfony welcome](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/diz6c2gg1hpgg6lhwiuj.png)

### EasyAdmin
#### Install

```console
$ composer require easycorp/easyadmin-bundle
```

The output was:

```console
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

##### Install

```consle
$ php bin/console make:admin:dashboard
```

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

```console
 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bsl7elxnq3cfml5q8lya.png)

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:

```console
$ mkdir templates/admin

$ nvim templates/admin/index.html.twig
```

Write just this in it:

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

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

```diff
  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:

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

Besides, you sometimes have to clear cache.

```console
$ php(8) bin/console cache:clear
```

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

![your own dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n01v6r50djzony0d63vn.png)

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:

```console
$ 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.

```console
$ php bin/console make:admin:crud
```

The output was:

```console
 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:

```diff
  (...)
+ 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zehloixqkh02ckchixhs.png)

Yay, got it. Let's click "The Label" on the left side:

![entity page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw1cn4s49swftvwcww2x.png)

We can manage storage here!!

## Conclusion

I tried a new feature released just at the beginning of the year, [menu badges](https://easycorp.github.io/blog//posts/new-in-easyadmin-4-menu-badges).

To show a count:

```diff
-         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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/usvzqvwec4n24eqktaf3.png)

Nice.

To show a message:

```diff
-         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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hh7ro32yvigpjfdvpha.png)

Sweet 💃💃