gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Open up the "Users" section. EasyAdmin has a concept of fields. A field controls how a property is displayed on the index and detail pages, but also how it renders inside of a form. So the field completely defines the property inside the admin. By default, EasyAdmin just... guesses which fields to include. But usually you'll want to control this. How? Via the configureFields()
method in the CRUD controller.
In this case, open UserCrudController.php
... and you can see that it already has a commented-out configureFields()
method:
... lines 1 - 7 | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 10 - 14 | |
/* | |
public function configureFields(string $pageName): iterable | |
{ | |
return [ | |
IdField::new('id'), | |
TextField::new('title'), | |
TextEditorField::new('description'), | |
]; | |
} | |
*/ | |
} |
Go ahead and uncomment that.
Notice that you can either return an array
or an iterable
. I usually return an iterable
by saying yield Field::new()
and passing the property name, like id
:
... lines 1 - 6 | |
use EasyCorp\Bundle\EasyAdminBundle\Field\Field; | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 11 - 15 | |
public function configureFields(string $pageName): iterable | |
{ | |
yield Field::new('id'); | |
} | |
} |
When I refresh... we have "ID" and nothing else.
So EasyAdmin has many different types of fields, like text fields, boolean fields, and association fields... and it does its best to guess which type to use. In this case, you can't really see it, but when we said id
, it guessed that this is an IdField
. Instead of just saying Field::new()
and letting it guess, I often prefer being explicit: IdField::new()
:
... lines 1 - 7 | |
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 12 - 16 | |
public function configureFields(string $pageName): iterable | |
{ | |
yield IdField::new('id'); | |
} | |
} |
Watch: when we refresh... that makes absolutely no difference! It was already guessing that this was an IdField
.
Cool! So how do we figure out what all of the field types are? Documentation is the most obvious way. If you look on the web debug toolbar, there's a little EasyAdmin icon.
Tip
If you're using EasyAdmin 4.4.2 or later, you won't find an EasyAdmin icon on the Web Debug Toolbar. Instead, click on any link on the toolbar to get to the Profiler, then look for the "EasyAdmin" section near the bottom of the left sidebar.
Click into that... to see some basic info about the page... with a handy link to the documentation. Open that up. It has a "Field Types" section down a ways. Yup, there's your big list of all the different field types inside of EasyAdmin.
Or, if you want to go rogue, you find this directly in the source code. Check out vendor/easycorp/easyadmin-bundle/src/Field
. Here is the directory that holds all the different possible field types.
Back in our CRUD controller, let's add a few more fields.
If you look in the User
entity, you can see $id
, $email
, $roles
, $password
, $enabled
, $firstName
, $lastName
, $avatar
... and then a couple of association fields:
... lines 1 - 15 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
use TimestampableEntity; | |
#[ORM\Id] | |
#[ORM\GeneratedValue] | |
#[ORM\Column] | |
private ?int $id; | |
#[ORM\Column(length: 180, unique: true)] | |
private ?string $email; | |
#[ORM\Column(type: Types::JSON)] | |
private array $roles = []; | |
/** | |
* The hashed password | |
*/ | |
#[ORM\Column] | |
private ?string $password; | |
/** | |
* The plain non-persisted password | |
*/ | |
private ?string $plainPassword; | |
#[ORM\Column] | |
private bool $enabled = true; | |
#[ORM\Column] | |
private ?string $firstName; | |
#[ORM\Column] | |
private ?string $lastName; | |
#[ORM\Column(nullable: true)] | |
private ?string $avatar; | |
#[ORM\OneToMany('askedBy', Question::class)] | |
private Collection $questions; | |
#[ORM\OneToMany('answeredBy', Answer::class)] | |
private Collection $answers; | |
... lines 59 - 281 | |
} |
We won't need to manage all of these in the admin, but we will want most of them.
Add yield TextField::new('firstName')
... repeat that for $lastName
... and then for the $enabled
field, let's yield BooleanField::new('enabled')
. We also have a $createdAt
field... so yield DateField::new('createdAt')
:
... lines 1 - 6 | |
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField; | |
use EasyCorp\Bundle\EasyAdminBundle\Field\DateField; | |
... lines 9 - 10 | |
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 15 - 19 | |
public function configureFields(string $pageName): iterable | |
{ | |
... line 22 | |
yield TextField::new('email'); | |
yield TextField::new('firstName'); | |
yield TextField::new('lastName'); | |
yield BooleanField::new('enabled'); | |
yield DateField::new('createdAt'); | |
} | |
} |
So I'm just listing the same properties that we see in the entity. Well, we don't see $createdAt
... but that's only because it lives inside of the TimestampableEntity
trait:
... lines 1 - 9 | |
use Gedmo\Timestampable\Traits\TimestampableEntity; | |
... lines 11 - 15 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
use TimestampableEntity; | |
... lines 19 - 281 | |
} |
Anyways, with just this config, if we move over and refresh... beautiful! The text fields render normal text, the DateField
knows how to print dates and the BooleanField
gives us this nice little switch!
As a challenge, instead of rendering "First Name" and "Last Name" columns, could we combine them into a single "Full Name" field? Let's try it!
I'll say yield TextField::new('fullName')
:
... lines 1 - 12 | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 15 - 19 | |
public function configureFields(string $pageName): iterable | |
{ | |
... lines 22 - 23 | |
yield TextField::new('fullName'); | |
... lines 25 - 26 | |
} | |
} |
This is not a real property. If you open User
, there is no $fullName
property. But, I do have a getFullName()
method:
... lines 1 - 15 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
... lines 18 - 194 | |
public function getFullName(): ?string | |
{ | |
return $this->firstName.' '.$this->lastName; | |
} | |
... lines 199 - 281 | |
} |
So the question is: is it smart enough - because the field is called fullName
- to call the getFullName()
method?
Let's find out. I bet you can guess the answer. Yup! That works!
Behind the scenes, EasyAdmin uses the PropertyAccess Component from Symfony. It's the same component that's used inside of the form system... and it's really good at reading properties by leveraging their getter method.
Back in configureFields()
, I forgot to add an "email" field. So, yield TextField::new('email')
:
... lines 1 - 12 | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 15 - 19 | |
public function configureFields(string $pageName): iterable | |
{ | |
... line 22 | |
yield TextField::new('email'); | |
... lines 24 - 26 | |
} | |
} |
And... no surprise, it renders correctly. But, this is a case where there's actually a more specific field for this: EmailField
:
... lines 1 - 13 | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 16 - 20 | |
public function configureFields(string $pageName): iterable | |
{ | |
... line 23 | |
yield EmailField::new('email'); | |
... lines 25 - 27 | |
} | |
} |
The only difference is that it renders with a link to the email. And, when you look at the form, it will now be rendering as an <input type="email">
.
The real power of fields is that each has a ton of options. Some field options are shared by all field types. For example, you can call ->addCssClass()
on any field to add a CSS class to it. That's super handy. But other options are specific to the field type itself. For example, BooleanField
has a ->renderAsSwitch()
method... and we can pass this false
:
... lines 1 - 13 | |
class UserCrudController extends AbstractCrudController | |
{ | |
... lines 16 - 20 | |
public function configureFields(string $pageName): iterable | |
{ | |
... lines 23 - 25 | |
yield BooleanField::new('enabled') | |
->renderAsSwitch(false); | |
... line 28 | |
} | |
} |
Now, instead of rendering this cute switch, it just says "YES". This... is probably a good idea anyways... because it was a bit too easy to accidentally disable a user before this.
So... this is great! We can control which fields are displayed and we know that there are methods we can call on each field object to configure its behavior. But remember, fields control both how things are rendered on the index and detail pages and how they're rendered on the form. Right now, if we go to the form... yup! That's what I expected: these are the five fields that we've configured.
It's not perfect, though. I do like having an "ID" column on my index page, but I do not like having an "ID" field in my form.
So next, let's learn how to only show certain fields on certain pages. We'll also learn a few more tricks for configuring them.
Hey Sébastien J.!
Thanks for reporting that! I have no idea what went wrong, but I basically "refreshed" the video on our end, and it seems to be working now. Please let me know if you still have any problems!
Cheers!
Please note that we started adding docs for Fields recently. That's why some fields are still undocumented. Sorry about that. The good thing is that if you use PhpStorm, you get autocompletion for all options, because they are just methods that you call on each field object.
Here's the list of field types: https://symfony.com/bundles...
We hope to finish the docs for all fields soon.
Hey Javier,
That's awesome news! Yeah, most fields are clear base on their names, but having more detailed docs on them will definitely help newcomers. And yeah, PHP autocompletion helps a lot too.
Thanks for the head ups :)
Cheers!
In case anyone else has trouble finding the EasyAdmin documentation icon, it was removed from the Profiler bar in EasyAdmin 4.4.2 per this issue:
https://github.com/EasyCorp/EasyAdminBundle/pull/5453
So.. if you're using 4.4.2 or later, just click anywhere in the Profiler to open it full screen and then look for the new EasyAdmin button near the bottom of the sidebar. There are now several links including one for documentation.
Hey CDesign,
Thank you for raising this up and providing the link to the PR that explains the reasons behind of this change. Yeah, it makes sense
Cheers!
In documentation for fields i missing info for choicetype, i'm not
sure how to deal with roles to be assigned from easyadmin, and encode
new password for user. Another one, upload some files from easyadmin?
Would we find something later on about that kind of stuff in this course?
Hey Hubert,
Yes, it will be covered a bit later in this course, so stick with us :) For now, it's more like simple field rendering and acquaintance with field types :)
Thank you for your patience!
Cheers!
Hi,
I'm using EasyAdmin 3.x, and I'm having issues with TExtEditorField; apparently, it strip all tags that are not div or H1; I need to handle H2, H3, etc ....
I have found this solution:
yield TextEditorField::new('...')->setTrixEditorConfig([
'blockAttributes' => [
'default' => ['tagName' => 'p'],
'heading1' => ['tagName' => 'h2'],
],
'css' => [
'attachment' => 'admin_file_field_attachment',
],
]);
But it works only on EasyAdmin 4.x. Does Someone know some tricks to add other html tags mamagement?
Thanks in advance
Hey Gianluca-F,
Did you try this code in your EA 3 version? It might work I suppose because I see that v3 still has support, though it might be only security fixes support though. But it does not work - not sure then, try to ask this question on the bundle's repo, maybe someone who tried this will give you some tips how to achieve it, because I can't give you any tips about it, never do this before.
But if it does not work out of the box with the. TextEditorField - I'd recommend you to use simple TextField and attach any JS editor you love to it, you can easily add JS fields to specific CRUD controllers.
Cheers!
Hi Victor,
I have installed CKEditorBundle, I have created a new CustomField that use CkEditorType and I'm using it; it is awsome and works like a charm :-)
Thanks !
I have installed CKEditorBundle - ran the configuration but still cannot get it to display - going out my mind here :/
try to add these lines to
config/packages/fos_ckeditor.yaml
twig:
form_themes:
- '@FOSCKEditor/Form/ckeditor_widget.html.twig'
Hi GianLuca finally managed it, after installing CK Editor bundle & installing it I created a new Custom Field class called ContentField
<?php
namespace App\EasyAdmin;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
class ContentField implements FieldInterface
{
use FieldTrait;
public static function new(string $propertyName, ?string $label = null, ?string $configName = null): self
{
return (new self())
->setProperty($propertyName)
->setLabel($label)
// this template is used in 'index' and 'detail' pages
->setTemplatePath('@EasyAdmin/crud/field/text_editor.html.twig')
// this is used in 'edit' and 'new' pages to edit the field contents
// you can use your own form types too
->setFormType(CKEditorType::class)
->setFormTypeOptions(
[
'config'=>[
'toolbar' => 'full',
'extraPlugins' => 'templates',
'rows' => '20',
],
'attr' => ['rows' => '20'] ,
])
->addCssClass('field-ck-editor');
}
}
And then in my PagesCrudController added configureCrud function to call my twig template
public function configureCrud(Crud $crud): Crud
{
return $crud
->addFormTheme('@FOSCKEditor/Form/ckeditor_widget.html.twig')
;
}
And finally called the CustomField I created above in the configureFields function
yield ContentField::new('pageCopy', false)
->setColumns('col-sm-12 col-md-12 col-lg-12');
Hey Paul,
Well done! And thanks for sharing your solution with others, it might be helpful :)
Cheers!
Hey Gianluca-F,
That's awesome! And thank you for letting us know that it works for you well this way, I bet some users might be interested in it as well :) Yep, EA has some tools out of the box, but sometimes you can do extra work to make it even better for your project like with the CKEditor integration you made, well done!
Cheers!
Hello there,
How could I display computed fields in index view ?
I can't simply use a getter in my entity.
I need my entity data AND using my entity manager, but I can't find a way to do that.
By example, what I would like to do:
DateField::new('myEntityDate', 'date')
->formatValue(function ($value) use ($helper) {
return $helper->someFunction($entity, $value);
}),
In my closure, I've no way to access "$entity", or at least I've not found any. This is pretty annoying.
And injecting my helper in my entity class is bad practice.
Any idea ?
Autoresponding myself :)))
The solution is:
DateField::new('myEntityDate', 'date')
->formatValue(function ($value, $entity) use ($helper) {
return $helper->someFunction($entity, $value);
}),
The second parameter of the closure seems to be the entity object.
Hey Kershin
Yep! You're right, the second argument of the closure gives you the entity, but be aware that the $entity argument may be null, so you may want to code defensively
Cheers!
Hello Diego !
Thanks for your answer.
In which case the $entity argument could be null ? (in index & detail views)
Regards,
Hello,
I want to ask how to manage this situation. I have two fields A and B defined on Entity as nullable=true.
But I need to make one of them (A or B) required.
For example: When users writes some value to field A, field B should be empty. When users wtites some value on field B, field A should be empty.
Thank you for reply
Hey Tomáš S.!
Ah, then this is a "validation" trick: you need to make sure that you show the user a validation error if A & B are empty OR if A & B are both NOT empty. This actually has nothing to do with EasyAdmin, which is great! EasyAdmin simple "runs your entity's validation rules". And so, if we can get the validation rules setup on your entity correctly, it will just work :).
For this, I would use the Callback constraint: https://symfony.com/doc/cur...
This allows you to write a function in your entity that contains the exact validation logic you need. The only thing you'll need to decide is which field to attach the error to (field A or field B) in case they have filled out neither or both.
Let me know if this helps :).
Cheers!
// composer.json
{
"require": {
"php": ">=8.1.0",
"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
}
}
Hi. I have a message : The media could not be loaded, either because the server or network failed or because the format is not supported.