Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

EasyAdmin! For an Awesomely Powerful Admin Area

4:19:16

What you'll be learning

// composer.json
{
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/doctrine-bundle": "^2.1", // 2.5.5
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
        "doctrine/orm": "^2.7", // 2.10.4
        "easycorp/easyadmin-bundle": "^4.0", // v4.0.2
        "handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
        "knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
        "knplabs/knp-time-bundle": "^1.11", // 1.17.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.5
        "stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.1
        "symfony/console": "6.0.*", // v6.0.2
        "symfony/dotenv": "6.0.*", // v6.0.2
        "symfony/flex": "^2.0.0", // v2.0.1
        "symfony/framework-bundle": "6.0.*", // v6.0.2
        "symfony/mime": "6.0.*", // v6.0.2
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.0
        "symfony/security-bundle": "6.0.*", // v6.0.2
        "symfony/stopwatch": "6.0.*", // v6.0.0
        "symfony/twig-bundle": "6.0.*", // v6.0.1
        "symfony/ux-chartjs": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.7", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.7
        "twig/twig": "^2.12|^3.0" // v3.3.7
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
        "symfony/debug-bundle": "6.0.*", // v6.0.2
        "symfony/maker-bundle": "^1.15", // v1.36.4
        "symfony/var-dumper": "6.0.*", // v6.0.2
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.2
        "zenstruck/foundry": "^1.1" // v1.16.0
    }
}
// composer.json
{
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/doctrine-bundle": "^2.1", // 2.5.5
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
        "doctrine/orm": "^2.7", // 2.10.4
        "easycorp/easyadmin-bundle": "^4.0", // v4.0.2
        "handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
        "knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
        "knplabs/knp-time-bundle": "^1.11", // 1.17.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.5
        "stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.1
        "symfony/console": "6.0.*", // v6.0.2
        "symfony/dotenv": "6.0.*", // v6.0.2
        "symfony/flex": "^2.0.0", // v2.0.1
        "symfony/framework-bundle": "6.0.*", // v6.0.2
        "symfony/mime": "6.0.*", // v6.0.2
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.0
        "symfony/security-bundle": "6.0.*", // v6.0.2
        "symfony/stopwatch": "6.0.*", // v6.0.0
        "symfony/twig-bundle": "6.0.*", // v6.0.1
        "symfony/ux-chartjs": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.7", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.7
        "twig/twig": "^2.12|^3.0" // v3.3.7
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
        "symfony/debug-bundle": "6.0.*", // v6.0.2
        "symfony/maker-bundle": "^1.15", // v1.36.4
        "symfony/var-dumper": "6.0.*", // v6.0.2
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.2
        "zenstruck/foundry": "^1.1" // v1.16.0
    }
}

So... your site needs an admin area. Do yourself a favor and skip all that custom code and jump straight into EasyAdmin bundle. Why #1? Because it'll take you a fraction of the time to build what you need. Why #2? Because it'll be even better than what you would build by hand, including built-in widgets for auto-completion and toggling boolean fields. It's... pretty sweet.

In this tutorial, we'll learn how to admin interfaces that are highly customized:

  • Install & Bootstrapping the bundle
  • Dashboards! CRUD controllers!
  • All about Fields
  • Customize everything: what properties to display, how they render, help messages, sorting, filters... and more!
  • Override templates... at many different levels
  • Take control of your forms
  • Handling security
  • Adding custom actions (and removing others)
  • Updating and configuring the menu (like adding a link to kitten videos!)
  • Hooking into events to do things before or after an entity is saved
  • Adding custom CSS/JS behaviors to the page with Webpack Encore
  • ... and more

So let's do a little bit of work for a lotta bit results (note: "lotta bit" is a term I just made up).

Tip

Love EasyAdmin? Consider sponsoring its maintainer Javier Eguiluz!


Your Guides

Victor Bocharsky Ryan Weaver

Buy Access
Login or register to track your progress!

Join the Conversation?

36
Login or Register to join the conversation
Nina-P Avatar
Nina-P Avatar Nina-P | posted 1 month ago | edited

Hello

Great tutorial. Thank you.

Please could you add an episode with

CollectionFields->useEntryCrudForm() and AssociationFields->renderAsEmbeddedForm()

or how to embed a form from another Entity with a to-many Association or point to detailed documentation?
Or maybe that's the limit of easy admin?

Looking forward for your answer.
Thank you in advance.

1 Reply

Hey Nina!

Thank you for your interest in SymfonyCasts tutorials! Unfortunately, we cannot add new chapters to the tutorials we've already released, but we can consider this as a good topic to cover for our next tutorial about EasyAdmin. We don't have specific plans to make it in a nearest feature, but I'll add it to our ideas pool.

If you want to know more about useEntryCrudForm() and renderAsEmbeddedForm() - take a look at the official docs:

Unfortunately, I personally haven't used those features yet. You can try to handle this task with that feature, but if it does not work well - you can always create a custom controller/action and write any custom logic you want there. I hope that helps!

Cheers!

Reply

Hi Knp lecturers,
thank you for this great tutorial.

I'm starting to learn symfony 6.
Looking forward for next symfony 6 track tutorial

1 Reply

Hey Suabahasa!

Thank you for your interest in SymfonyCasts tutorials and your feedback! Good time to start learning it ;) We're going to release a few more Symfony 6 tutorials shortly, and then we will create a Sf6 track - we just need at least a few courses there :) Thank you for your patience!

Cheers!

1 Reply

Hello, please help me, how to change redirect to route after save or edit row. T_T

Reply

Hey Yoelkj,

You can override the getRedirectResponseAfterSave() method in your custom CRUD controller and add the logic you need.

Cheers!

Reply

Hello, thanks for answering.

I have also seen that solution but I have not been able to override that method when it is protected. as I would do it

In the end I had to force it another way. I know it's not the best solution.

    public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
    {
        parent::updateEntity($entityManager, $entityInstance);

        $adminUrlGenerator = $this->container->get(AdminUrlGenerator::class);
        $url = $adminUrlGenerator
                ->setRoute('admin_show_user')
                ->generateUrl();

        die(header('Location: '.$url));

    }
Reply

Hey yoelkj,

I'm afraid I didn't get why you could not override a protected method. That's the purpose of the protected scope, to be overridden (and executed) by any of its derivates

Cheers!

Reply
Nick-F Avatar

Hello I'm having an issue trying to figure out rendering association fields as embedded forms in edit and new views.
The documentation here https://symfony.com/doc/4.x/EasyAdminBundle/fields/AssociationField.html
says that there is a method on AssociationField called renderAsEmbeddedForm() that is supposed to do exactly what I need, and I have the latest easyadmin version installed but that method does not exist...
I can't find anything on the internet about it missing either, the only google search result is the documentation page that says the method should be there.

Reply

Hey Nick!

Hmmm, yup! I see it! That feature is brand new - it has been merged - https://github.com/EasyCorp/EasyAdminBundle/pull/5353 - but not released yet. It should be part of 4.5.0 and Javier (the maintainer) is pretty good about not waiting too long to release. But if you need this now, you'll need to update your composer.json to use the 4.x branch.

Cheers!

Reply
Christina-V Avatar
Christina-V Avatar Christina-V | posted 3 months ago | edited

Hi everyone,

Easyadmin is great, no doubt about it.
But...

I can't believe that there is no way to create dependent select fields, like describe here for symfony: https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

I mean this is a really natural behaviour, an admin should provide this.
Anyway, now that my project is really advanced, I realise I'm in need of this so...

So what will be the best way to try to "hack' this and make it work ?

Reply

Hey Christina-V!

I can't believe that there is no way to create dependent select fields, like describe here for symfony: https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

I agree. This is probably THE thing in Symfony in general that is much harder than it should be. In general, my way of solving this these days is by using LiveComponents. However, I haven't used live components in EasyAdmin yet - it would require some research to figure out how to reuse the form built by EasyAdmin from inside of your component. A compelling thing to try, but I just haven't had a chance to look into it yet.

So, for this, the process would look something like this comment - https://github.com/EasyCorp/EasyAdminBundle/issues/4716#issuecomment-1127979067 - but more specifically, I would:

A) In configureCrud(), I would call:

$crud->setFormOptions(['attr' => [
    'data-controller' => 'admin-dependent-fields',
    'data-admin-dependent-fields-url-value' => $this->container->get(AdminUrlGenerator::class)->setAction(Action::NEW)->generateUrl()
]])

This should initialize a Stimulus controller called admin-dependent-fields and pass a "value" called url to it.

B) In configureFields(), we're going to add an "action" to the first field:

public function configureFields(string $pageName): iterable
{
    yield ChoiceField::new('state')->setFormTypeOption('attr', ['data-action' => 'admin-dependent-fields#changeState']);
}

C) Create the corresponding Stimulus controller:

// assets/controllers/admin-dependent-fields-controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    static values = {
        url: String
    }

    changeState(event) {
        const countryElement = event.currentTarget;
        const data = {};
        data[countryElement.name] = countryElement.value;
        fetch(this.urlValue, { method: 'POST' })
            .then(async (response) => {
                const currentStateElement = document.getElementById('the-id-attribute-on-state-field');
                const newElement = document.createElement('div');
                newElement.innerHTML = await response.text();
                const newStateElement = newElement.querySelector('#the-id-attribute-on-state-field');

                // I'm using parentElement because I'm assuming we might want to replace the entire
                // parent around the select, not just the select
                currentStateElement.parentElement.replaceWith(newStateElement.parentElement);
            });
    }
}

D) Then you will STILL need the form modification stuff found here - https://github.com/EasyCorp/EasyAdminBundle/issues/4716#issuecomment-1127979067

Phew! Probably I made some mistakes, but hopefully this will help. But yes, I hate this solution - that is WAY WAY too much work to accomplish this - I agree completely

Cheers!

Reply
Christina-V Avatar

Thanks for the reply Ryan!

It's duable I think. I'll have a look probably this week, and let you know the results ;-0

Reply
Default user avatar
Default user avatar unknown | posted 4 months ago | edited
Comment was deleted.

Hey Christina,

It depends on what you're trying to achieve but I think a good way would be to override the createIndexQueryBuilder() method

Cheers!

Reply
Christina-V Avatar

Hi Mollokan,

Indeed, my question was too "open", sorry for that.
Ok, so I'm gonna try to give some context.

I got this Registration entity, linked to some other entities (User, Activity ...).

Usually, in my CRUD controller, I'll use the default createIndexQueryBuilder, and in configureFields:

public function configureFields(string $pageName): iterable
{
        return [
            IdField::new('id')
                ->onlyOnIndex(),
            AssociationField::new('user')
            AssociationField::new('activity'),
        ];
}

And I'll have, for the index page, a table with;

ID | user | activity
1 username1 activity_x
2 username4 activity_y

But the final user of the website I'm building wants something different for this INDEX page, to have easy access to the informations they need the most:

ID | Activity.title | Activity.startDate | User.firstname | User.lastname | User.phonenumber

So I need to "choose" some specific fields, for Association.

Is it more clear ?

Thanks !

Reply

Hey Christina,

You can access that information via Doctrine relationships. For example, if User has a relationship with Activity, you can access the Activity fields by doing something like this

public function configureFields(string $pageName): iterable
{
    yield Field::new('activity.id', 'Activity ID');
}

Here you can learn more the EasyAdmin association field https://symfony.com/doc/current/EasyAdminBundle/fields/AssociationField.html

Cheers!

1 Reply
mofogasy Avatar
mofogasy Avatar mofogasy | posted 6 months ago

Hello,
your explanation is great. I want to extend this overriding possibility for my project.
I have 1 entity for article joined to another entity for images. 1 article can have many images.

when adding images to an article, I'd like to display the images and selecting them instead of just displaying their filename.
I have been trying with AssociationField but I can't get it.
Could you help me please ?

Reply
Huib B. Avatar

Symfony redirects to login page after successful login on chrome and logs out. This behavior is in some versions of chrome but not in Firefox, Edge or Safari although chrome does work in private mode and chrome (Version 105.0.5145.0) works even without private mode, any idea why login fails in chrome?

Reply

Hey Huib,

That's weird... especially the fact that it works in Chrome incognito mode but doesn't work in Chrome regular mode. It makes me think that you may have some extensions installed that causes this problem somehow. Most probably those Chrome extensions do not work in incognito mode (the default behavior) and that's why it works there - the only logical explanation about what's going on. I'd recommend you to turn off all the chrome extensions and try again. Also, try to upgrade your Chrome to the latest first. If the issue still persist - then I'm not sure. Well, you may want to open the Chrome dev toolbar, then long click on "reload" button (about 3 seconds) and choose "Empty cache and hard reload" - it might help sometimes.

I hope this helps!

Cheers!

Reply

Thanks for these tutorials. I am awaiting for your latest videos every single day!

Reply

Hey Lubna,

Thanks for the feedback! We are working to get it as soon as we can :-)

Thanks for staying with us!

Cheers!

3 Reply
Ruslan Avatar

26th December in five hours. We are waiting for! :)
Thank you for your casts, it's very helpful.

Reply

Hey Ruslan!

Sorry for some delays... Unfortunately, Christmas and new year schedule made some adjustments in the release schedule on SymfonyCasts, but this course will be the next for sure. Thank you for your patience! :)

Cheers!

Reply
Peter A. Avatar
Peter A. Avatar Peter A. | posted 1 year ago

Hi,
Do we have a date for release ? It's mentioned below that it'd be ready around December 2021..?

Thanks
Peter

Reply

Hey Peter,

Yes, it should be started releasing right after the Symfony 5 Security course which should be fully released in about a week... but considering some Christmas / new year eve holidays it might be started in early January I think. But this course will be definitely the next one we're going to release ;)

Thank you for your patience!

Cheers!

Reply
Peter A. Avatar

thanks much, looking forward.

Reply

Hey Peter!

I have more top-secret news for you! We're super close to start releasing this course - yay! And we even prepared a little surprise in this course - you will see it when we start releasing :) Btw, I think it may happen on the next week, or in 2 weeks max from now. We're finishing releasing our Symfony 5 Security course in full this week, and then will switch to EA completely :)

Thanks a lot for your patience!

Cheers!

Reply
Fabrice Avatar

Hey ! So, what's about this surprise? :D

Reply

Hey Kiuega!

Ha, good question, and I'm happy you remembered about it :) So, basically, as you might notice already, we're going to cover EasyAdminBundle v4 instead of v3 as it was planned, that's based on the newest Symfony 6! So, that's the little surprise I was talking about, and I hope you're not disappointed about it ;)

Cheers!

Reply
Kiuega Avatar

Hello ! I intend to take advantage of the black friday offer to subscribe for a month to take advantage of the training on EasyAdmin 3. Do you think that it will be fully published before December 26, 2021?

Reply

Hey Kiuega,

We do plan to start releasing it in December, but I'm not sure it will be completely published until December 26. It depends on how many chapters it will have, and when exactly we will start, but most probably it will be completely released in January only.

Cheers!

Reply
Abdul M. Avatar
Abdul M. Avatar Abdul M. | posted 1 year ago

any tutorial series coming on CQRS, DDD, Microservices with Symfony and Redis with Symfony Framework!

Reply

Hey Abdul M.

For the moment we do not have any plans on releasing a tutorial about those topics but thanks for letting us know what you'd like to learn next

Cheers!

Reply
Kiuega Avatar

Damn ! This tutorial that has been requested for so long! We can't wait for him to come out!

Reply

Hey Kiuega!

I'm happy to let you know that we're actively working on this tutorial right now :) Thank you for your interest in SymfonyCasts tutorials and your patience!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!