Closed javiereguiluz closed 9 years ago
1) New custom_actions option
I presume you're not in favor of some kind of magical values in order to inherit global configured actions and get rid of the custom_action
necessity ?
easy_admin:
actions: ['edit', 'new', 'show']
entities:
User:
class: AppBundle/Entity/User
actions:
- inherit # will inherit from easy_admin.action
- ... #same
- '<<' #same
- myAction
User.actions
will be merged with easy_admin.actions
during normalization.
But then, there are many other concerns: If we do that, should built-in actions like list
be overridable with a custom definition :
User:
class: AppBundle/Entity/User
actions: [ { route: 'user_list', action: 'list' } ]
What happens here ?
2) How do custom actions work
What if a custom action isn't dedicated to a single record, but the whole entity ? As a statistics panel ? Are custom actions only about performing something against one entity record ?
In this case, a link will be generated with the path() Twig function using that route name and passing the id of the active item as the only route parameter.
Could custom actions be registered globally ? If so, shouldn't we generate the link with the entity name + id as route parameters ?
NVM, i forgot about this part:
This is an entity-level option. You cannot define a custom_actions global option to apply it to all entities and you cannot specify custom actions per entity page (list, edit, etc.) All custom actions will be displayed in the list action of the entity.
But why not ?
@ogizanagi thanks for your comments. Answering your concerns:
inherit
value.custom_actions
option. You can override them by overriding the methods of the AdminController. That's possible as of today.EasyAdmin executes the grantAccess() method of the AdminController and it will pass as argument the id of the active item. Obviously you have to define your own AdminController extending from the default one.
A little throw-in: Couldn't we use the custom entity repositories instead for this? So there is no need for writing your own AdminController that extends.
EDIT:
BTW, i'm for the custom_actions
in the yaml configuration too.
And i think, it would be nice, to set defaults like you did before in your examples for default. And as long as there is no actions
key in the entity section, this will be used. But if there is an actions
key in the entity session it will override the default and just use the given actions. Plus, if set, the custom actions in custom_actions
I'm not sure about adding the option of defining entity repository methods, but let's see the opinions of others. I show bellow some examples of the three proposed types of custom actions.
User:
class: AppBundle/Entity/User
custom_actions:
- { method: 'grantAccess', label: 'Grant' } # 1
- { repository: 'softDelete', label: 'Delete' } # 2
- { route: 'update_user_information', label: 'Refresh' } # 3
1) A method of the AdminController (you have to extend the default one and create your own admin controller):
// EasyAdmin executes the following:
$this->{$method}();
// EXAMPLE:
$this->grantAccess();
// ...
public function grantAccess()
{
$entity = $this->em->find($this->request->query->get('id'));
}
2) A method of the repository related to the current entity:
// EasyAdmin executes the following:
$repository = $this->em->getRepository($this->entity['class'])
$repository->{$repository}($this->request->query->get('id'));
// EXAMPLE:
$repository = $this->em->getRepository('AppBundle\Entity\User');
$repository->softDelete(12);
3) A Symfony application route:
// EasyAdmin executes the following (in the 'list.html.twig' template):
<a href="{{ path(route, { entity: entity.name, id: entity.id }) }}">
{{ label }}
</a>
// EXAMPLE:
/**
* @Route('update_user_information')
*/
public function updateUserInformationAction(Request $request)
{
$user = $this->get('user_repository')->find($request->query->get('id'));
// ...
return $this->redirectToRoute('admin', array(
'action' => 'list',
'entity' => $request->query->get('entity'),
));
}
First, sorry for the huge paved text :smile:
I'd prefer to keep the use of the actions
parameter, and instead of adding a whole custom_actions
, using the global actions
parameter as reference, and if it's not set, use the six default actions as reference, and add a override_default_actions
boolean parameter that would be false
by default.
If override_default_actions
is true, then only the specified actions are allowed, PLUS the "list" action (which is mandatory).
If it's set to false, any action specified in the actions
parameter will be merged with the six default actions.
If override_default_actions
is true, the actions
will finally contain only specified actions (per-entity), and if it's false, the actions
will be merged with the back-end global actions (global + per-entity).
This would lead to something like this:
easy_admin:
actions: [ 'show', 'statistics' ]
# With this param set to "true", we'll never see the "edit" or "new"
# default actions, nor the "toggle" one.
override_default_actions: true
entities:
Product:
class: AppBundle\Entity\Products
# Here we force the override because we don't want "statistics"
# for products, but we have a "stocks" action dedicated to this entity
actions: [ 'show', 'stocks' ]
override_default_actions: true
User:
class: AppBundle\Entity\User
# Here, there is no "override_default_actions" attribute,
# so we'll see all global actions ("show" and "statistics")
# PLUS the "edit" action
actions: [ 'edit' ]
The very good point of this is that with a default false
override_default_actions
attribute, there is no fundamental BC break, and any actual back-end can work the same way.
One another bonus is that the default back-end actions can be treated as "custom" actions with the default parameters already set in the AdminController
.
And any other action can have its parameters overriden with the route
and label
parameter.
And with this, you can ALSO change the label of the current actions, like this:
easy_admin:
entities:
User:
class: AppBundle\Entity\User
actions:
- { name: 'edit', label: "Modifier les paramètres" }
(I'm writing while @javiereguiluz just posted his point of view with his 3 methods :smile: )
One another lack I see here is that with this system, we may need to specify which type of action we have.
What is an action type? Well... Remember CRUD! In fact, most of the custom actions can:
We always rely on CRUD in a back-end, and sometimes we want to do things that are NOT entity-related (mostly for search and dashboards).
So, using a entity_related
parameter in our action details might be one solution for this, because all non-entity-related actions can be showed elsewhere in the bundle (for example in the left navbar)
And for the rest, the six default actions already rely on the CRUD, so we just might change the default array('edit', 'new', 'list', 'show', 'toggle', 'delete')
with a much more complex array BUT with the use of our CRUD system.
This will allow any user to create a dasboard with a defaulted "READ" CRUD parameter, and (it's my case in my actual back-ends) create a specific EDIT crud action to manage entities in a much more different way.
I can give you my example if you need it to see:
One of my apps is a maps manager, but always custom maps (example here with a tabletop rpg map).
BUT, managing a map is made in 2 ways: managing the map parameters (name, slug, reference image, zoom level, description...), and managing the map datas: markers, routes and zones.
And for this use, I absolutely needed a custom interactive_edit
action, which would be a page that shows the same map as the link above, but with all the tools to dynamically and interactively add and position markers, zones and routes, and edit their parameters live onto the map. This action would be of "UPDATE" crud type. And if I don't specify a controller action, it would then use the editAction()
with some more parameters given in the config, for example the repository action used (in my case it would be getMapWithFullDatas($id)
) , and a simple template
attribute (in which I'll add edit_interactive.html.twig
, or it can also be defaulted) and voilà, you just have a custom action setup with no controller overridance, just a single template created in app/Resources/views/edit_interactive.html.twig
or app/Resources/views/EasyAdminBundle/edit_interactive.html.twig
.
Are they good ideas? :)
Ah, and if you'd like to know, I'd be very happy to work on this feature if it corresponds to my vision of it :laughing:
@Pierstoval thanks for your ideas. Let's go for another round of comments.
I don't like the override_default_actions
option because to me it looks like a hack (although it's not). What about implementing the following behavior:
actions
option: what you put is what you get.actions
option: you inherit the global actions and you can easily add and remove actions.Example:
easy_admin:
# these 3 actions are the only ones enabled by default
actions: ['show', 'search', 'statistics']
entities:
User:
# adds 1 custom action, removes 1 custom actions, removes 1 default
# action and adds two default actions only for this entity
actions: ['grantPermission', '-statistics', '-show', 'new', 'edit']
The last use case that you comment looks extremely advanced and customized to me. In any case, I think you can easily solve it with a custom action tied to a route
. You get the id
of the map to edit, execute some code, render your own templates, with your own JavaScript code and then you push some button in your template which sends you to your controller and at the end of it, redirect the user back to the EasyAdmin template.
:+1: for the global @Pierstoval's vision and about considering even built-in actions as custom actions. In fact, that was one of the purposes of my previous suggestion in #95 in which I was trying to cover the largest panel of actions we could imagine. Considering every actions the same way seems more natural, and promotes the bundle extensibility.
But, I think you're introducing, inside the easy_admin configuration, parameters which are only inherent to the actions logic. IMO, the configuration shouldn't know anything about the action type or whatever, but only knowing if this action should be available for an entity, or not.
That's why I suggested some extensions point (the hooks) in the templates, that custom actions could "subscribe" in order to show the appropriated control, at the appropriated location(s). But of course, it will require a more complex internal structure about the way custom actions should be handled.
I'm not fan of the repository
option, but I don't have any proper arguments against it for now.
@javiereguiluz : :+1: About the ability to easily remove actions with the -
at entity level
@javiereguiluz Your implementation with the -
to remove the action sounds really great to me, in fact that's exactly the behavior I'd like to have in the config, and it prevents using another attribute like the override_default_actions
I proposed!
If you all agree with it, I think we got our solution :+1:
For the last use about "action types" it was just an idea, and you're right about it: it looks too complex and maybe too specific to be loved by other developers. And the route
option can in the end override any "action type", so let's forget about this idea :wink:
@javiereguiluz : If you want to perform some action to the whole entity, you can do that in the code. But that's very strange, because custom actions are displayed as links for each list item. Users will assume that acton affects only to the given record.
@Pierstoval : Export a list of entities in CSV/PDF/TXT/XLS : READ
Exporting capabilities are spread enough in backends to require such a feature. EasyAdmin will not handle every possible formats (maybe at least CSV), so we have to keep the ability for the developers to provide their own custom actions to do that. It cannot be single-record related and I think there are many other usages for entity level actions (stats, advanced search, export, compare, ...).
From @Pierstoval comment, we could extract at least the 3 following actions types :
list
page in "actions" column for each record)list
page)But, it does not belong to the configuration to state which action should be a single record or entity related one, but to the action itself.
Last point: How would an external bundle provide custom actions in order to reuse them with other backends ? e.g: export to XLS.
I find your comment very good for the action types, as the tree types you noted are probably all we need as an action_type
parameter.
For your last point, I don't fully understand the question :confused:
Imagine a third party bundle wishing to add some capabilities to EasyAdmin built-in features, through custom actions. For instance: providing a custom action allowing to export entities to XLS.
How would this third party bundle do that ? A priori, not by extending the AdminController. It brings too many issues due to inheritance. Using routes ? :
User:
class: AppBundle/Entity/User
actions:
- { route: 'export_xls' , label: 'Export to XLS', action_type: 'entity' }
This will require the user of this third-party bundle to explicitly define the action with the route parameter (for each entity configuration ?!). Ditto for action_type
(which again, I don't believe should be in the configuration anyway). Which is not very pleasant, from a DX perspective. The best would be to only have to register an externally provided custom action like this:
User:
class: AppBundle/Entity/User
actions:
- export_xls
Hmm...
I'm not familiar enough with container build process, but the only thing I have in mind for this is to register a service with a tag, and the only goal of the service will be to return an array of actions
that will be added to the back-end global actions, and an array of entities
that will be added to the back-end entities.
@Pierstoval : Did you just say "tagged services" ? :smiley_cat:
and the only goal of the service will be to return an array of actions that will be added to the back-end global actions, and an array of entities that will be added to the back-end entities.
Not sure I understood.
Something like this:
# vendor/custom/VendorBundle/Resources/services.yml
vendor_bundle.admin.register:
class: VendorBundle\Admin\Register
tags:
- { name: easyadmin.register_config }
<?php
namespace VendorBundle\Admin;
use JavierEguiluz\Bundle\EasyAdmin\Configuration\RegisterConfigInterface;
class Register implements RegisterConfigInterface
{
public function getEntities()
{
return array(
'User' => array(
'class' => 'VendorBundle\Entity\User',
),
);
}
public function getGlobalActions()
{
return array(
'edit_interactive' => array(
'scope' => 'entity',
'label' => 'Édition interactive',
'route' => 'custom_action_edit_interactive',
),
);
}
}
Again, it will be the responsibility of the user of the third-party bundle to create this service and configure everything then, right ? Seems like this is only a second way to configure easy_admin configuration, through code, on application side, not bundle one.
No, it's the third-party bundle that will need to register this service and add it to its resources, so it's called after configuration computation. In the order it's this:
EasyAdminExtension
)Configurator
to compute iteasyadmin.register_config
, get all the configuration created by these services, and add them to the back-end config. If you have such service in your AppBundle
, it's gonna be loaded, but if you also have one service in a ThirdPartyVendorBundle
, it's gonna be loaded too.easyadmin.config
parameter to get the new modificationsI'm not familiar enough with tagged services, but I hope it's the expected behavior, and I hope I'm clear enough to be understandable :smile:
Why has your Register
class a method getEntities
returning the user application's entities if this class is defined in the third-party bundle ?
I see the namespace AppBundle\Admin;
, but then, I don't understand how the user is not responsible of creating this class... :confused:
I think we're complicating everything too much. I'll publish soon a new proposal to provide most (but not all) of your requirements.
@ogizanagi Of course, my example should have been in some kind of vendor
bundle, I'm gonna change that in my example
OK. Here are my latest ideas around actions. Let me know what do you thing about it.
By default, EasyAdmin displays all the built-in actions:
list
page: new
, show
, edit
, search
.edit
page: save
, delete
, list
.new
page: save
, list
.show
page: edit
, delete
, list
.If you want to remove any built-in action, use the -action_name
syntax:
easy_admin:
list:
actions: ['-edit', '-search']
edit:
actions: ['-delete']
show:
actions:
# same as above
new:
actions:
# same as above
The actions
option can also be defined for entities instead of the entire backend.
easy_admin:
list:
actions: ['publish']
edit:
actions:
- { action: 'grantAccess', type: 'method', label: 'Grant' }
The actions
option can also be defined for entities instead of the entire backend.
An example of a super complex action customization:
easy_admin:
list:
# global actions displayed for all items of the 'list' action of all entities
actions:
# built-in options (you can redefine their labels and their behavior)
- '-show'
- '-search'
- '-edit'
# custom action - implicit options: type = method, label = 'unpublish'|humanize
- 'unpublish'
# custom action - implicit options: label = 'publish'|humanize
- { action: 'publish', type: 'method' }
edit:
# global actions displayed for the 'edit' action of all items of all entities
actions:
# built-in options (you can redefine their labels and their behavior)
- 'save'
- 'delete'
# custom action - implicit options: type = method, label = 'unpublish'|humanize
- 'softDelete'
# custom action - implicit options: none
- { action: 'save_as_pdf', type: 'route', lavel: 'Save as PDF' }
show:
# same as above
new:
# same as above
entities:
User:
class: AppBundle/Entity/User
list:
actions:
# don't display the 'search' action for the 'list' action of this entity
- '-search'
# don't display the 'show' action for the 'list' action of this entity
- '-show'
# custom action - implicit options: type = method, label = 'resendRegistrationEmail'|humanize
- 'resendRegistrationEmail'
# custom action - implicit options: none
- { action: 'grantAccess', type: 'method', label: 'Grant' }
# custom action - implicit options: none
- { action: 'softDelete', type: 'repository', label: 'Delete' }
# custom action - implicit options: none
- { action: 'update_user_information', type: 'route', label: 'Refresh' }
edit:
actions:
# override the properties of a custom action for the 'edit' action of this entity
- { action: 'softDelete', type: 'repository', label: 'Remove' }
# remove this custom action for the 'edit' action of this entity
- '-save_as_pdf'
show:
actions:
# same as above
new:
actions:
# same as above
The actions
option can also be defined for entities instead of the entire backend.
Example:
easy_admin:
list:
actions:
- { type: 'method', action: 'grantAccess', label: 'Grant Access' }
# the following configuration is equivalent
# - 'grantAccess'
EasyAdmin executes the method of the AdminController
that matches the action
name: $this->{$method}();
$this->grantAccess();
// ...
// define this method in a controller extending AdminController
public function grantAccess()
{
$entity = $this->em->find($this->request->query->get('id'));
}
easy_admin:
list:
actions:
- { type: 'route', action: 'user_grant_access', label: 'Grant Access' }
EasyAdmin generates the following link in the list.html.twig
page:
<a href="{{ path(action.action, { entity: entity.name, id: entity.id }) }}">
{{ action.label }}
</a>
The route of the action can be replied by any controller of your application:
/**
* @Route('user_grant_access')
*/
public function grantAccessAction(Request $request)
{
$user = $this->get('user_repository')->find($request->query->get('id'));
// ...
// finish the custom action redirecting the user to the 'list' action
return $this->redirectToRoute('admin', array(
'action' => 'list',
'entity' => $request->query->get('entity'),
));
}
easy_admin:
list:
actions:
- { type: 'repository', action: 'grantAccess', label: 'Grant Access' }
EasyAdmin executes the method grantAccess()
of the repository associated with
the given entity, and passes the id
of the selected entity as argument:
$repository = $this->em->getRepository($this->entity['class'])
$repository->{$action}($this->request->query->get('id'));
$this->em->flush();
Example:
$repository = $this->em->getRepository('AppBundle\Entity\User');
$repository->grantAccess(12);
$this->em->flush();
easy_admin:
list:
actions:
- { type: 'entity', action: 'grantAccess', label: 'Grant Access' }
Very similar to the repository
type, but the method is executed on the Doctrine
entity itself, not on its repository:
$entity = $this->em->getRepository($this->entity['class'])->find($this->request->query->get('id'));
$entity->{$action}();
$this->em->flush();
Example:
$entity = $this->em->getRepository('AppBundle\Entity\User')->find(12);
$entity->grantAccess();
$this->em->flush();
action
, the name of the action (it depends a lot on the type
of action).type
, the type of action. Possible values: method
(default), route
, repository
, entity
.label
, the title displayed by the action on the browser. By default, the
label is the humanized version of the action name.icon
, (default = null
) the name of the FontAwesome icon to display next
to the action label. This value is displayed as <i class="fa fa-{{ icon }}"></i>
.target
, (default = item
). Only available for list
actions. If item
,
the action is displayed next to each listing item. If entity
, action is
displayed once for the entire listing (e.g. the new
and search
actions).We have:
Imagine that the backend configuration is as follows:
easy_admin:
list:
actions: ['global_action']
entities:
User:
list:
actions: ['local_action']
What are the actions of User
?
new
, show
, edit
, search
from the built-in actionslocal_action
for obvious reasonsglobal_action
?Entities inherit global backend actions and they use -action_name
syntax
to avoid inheriting unwanted actions:
easy_admin:
list:
actions: ['global_action']
entities:
User:
list:
actions: ['local_action', '-global_action']
On the contrary, if entities don't inherit the global actions, they should repeat them in case they want to display them:
easy_admin:
list:
actions: ['global_action']
entities:
User:
list:
actions: ['local_action', 'global_action']
In my opinion the first behavior (implicit) is more intuitive than the second one (explicit).
For the two last points, I think the first of them is the only one that should be kept.
Using the -
notation should become intuitive with this behavior.
About the action types, I don't think that the types repository
or entity
would be used most of the time... Even if they're handy, they're quite hard to understand the first time. A custom route
is obvious for example.
But it may be personal, I don't know what would think other people about these specific types. method
(which implies extending the controller for custom actions) and route
(which is handier) are good enough. Let me just share my feeling about it:
Remember that we don't need to "think for users", because the final users will be developers, which, most of the time, will choose this bundle for one of 2 reasons (or both): easy to setup, easy to customize.
Performances and adaptability come in background, that's precisely the reason why I abandoned Sonata
suite and chose EasyAdmin
.
A simple back-end with the actual functions EasyAdmin
now offers is "never enough" when creating an application (and it's the same for any other bundle) and many developers that are in companies that develop complex apps would want to always have a hand on every feature.
The simple entity
and repository
types will only have one feature, and worse than that, they can only apply on one entity at a time, and is a simple "customized edition", whereas method
and route
offer all possibilities provided by the framework, and the developer can do whatever he wants.
It may be tough to say it, but I don't think that they would be used whatsoever.
Nonetheless they're both very smart ideas! It just sounds too (very very very) much specific to me.
For the rest, I don't have anything to say: it's great :+1:
Sorry for double-posting, I'd prefer two notification mails than that you read the first message without reading the "edit" part.
I really forgot something...
Why did you "changed" the actions list notation?
I mean, the "old" one was this like:
easy_admin:
actions:
- { label: "edit", type: "method" }
- { label: "list", type: "method" }
- { label: "custom", type: "route", action: "custom_route" }
and the new one is like:
easy_admin:
list:
actions:
- { label: "edit", type: "method" }
- { label: "list", type: "method" }
- { label: "custom", type: "route", action: "custom_route" }
First, this notation implies a huge BC break (unless you accept to merge two big complex arrays, but I don't think it's worth it...), and second, a really simple and tiny show_in
array would be easier to implement in the current back-end:
easy_admin:
actions:
- { label: "edit", type: "method", show_in: [ 'show', 'list' ] }
- { label: "list", type: "method", show_in: [ 'new', 'edit', 'show', 'delete' ] }
- { label: "custom", type: "route", action: "custom_route", show_in: [ 'list' ] }
What do you think about this?
@Pierstoval thanks for your comments ... and for reading my long comments :smile:
method
and route
are enough for now. We'll leave entity
, repository
and other extravagant options for the future.list_actions
in 1.0.8 version. I propose to change it to list
-> actions
in the next stable version. You may have used the actions
option ... but that's for the development version which is no guaranteed to not change. That's why I've added a note in the documentation telling the user that he/she probably want to read the documentation of the most recent stable version (see fc69956).actions
option for each action (list
, edit
, etc.) There is no longer the concept of "action", but "action in some page". Your proposal (show_in
option) has the same effect but in my opinion feels less natural.@Pierstoval : About the action types, I don't think that the types repository or entity would be used most of the time... Even if they're handy, they're quite hard to understand the first time. A custom route is obvious for example. But it may be personal, I don't know what would think other people about these specific types. method (which implies extending the controller for custom actions) and route (which is handier) are good enough. Let me just [...]
Exactly my thoughts... I don't find any value to those helpers method. Not intuitive at first. Not useful subsequently, as everything could be done the exact same way, and very simply with the methods
and route
types.
Implicit way :+1:
As you said, it's "An example of a super complex action customization"...still, only a little part of it. It feels really really heavy and unintuitive to me. :confused:
I really see custom actions as a primary feature, and something that should be "shippable" in a third-party bundle. The user should not have more concerns than configuring the entities for which those actions are enabled in easyadmin config. The action by itself must be able to choose where the buttons/links are rendered (which page, and where in the page). The only responsibility for the user, is to register the action for the desired entities.
For more options, if needed, the action itself (I mean the bundle) should provided a way to configure specific actions' parameters. It's not the EasyAdminBundle responsibility anymore.
Same for the target
attribute. Only the action should be aware of this. The user shouldn't be able to register a custom action dedicated to single records as an entity
action.
Needless to say, implementing such a way to handle actions rendering will be kind of heavy in the code/templates too. IMO by wishing not to overcomplicate the bundle by extending constantly the configuration, and rely on it for everything, we're actually overcomplicating the way we handle features, instead of relying on the extensibility and the developers skills.
Yes, EasyAdmin should be easy to use for most simple backends. The 5 minutes it takes to generate the basic admin backend is amazing. But I think it must also limit its responsibilities, by providing extension points that will help reducing the internal code & templates complexity, and trust other developers' skills for most advanced features.
BTW, here :
easy_admin:
edit:
actions:
- { action: 'grantAccess', type: 'method', label: 'Grant' }
I think the action
attribute must be renamed name
in the action declaration, or we should differentiate the action's name, and its value (which is a route name or a method name). What do you think ?
I don't know, maybe a quatuor name
, label
, type
and value|resource|action
might sound more explicit?
@ogizanagi I've read your comments carefully (as I always do with your comments) and I've finally made my mind about custom actions. I admit that your proposal about "independent actions" is not wrong but I believe that it's too complicated to fit EasyAdmin philosophy. The same goes for your thoughts about third-party bundles and "reusable actions". You are not wrong, but right now we don't care at all about third-party bundles. EasyAdmin is a monolithic piece of software, and I proudly say that as a good feature, not as something wrong.
All in all, I'm going to implement what I explained in this comment. However, I'll make the following changes to simplify things:
method
(default) and route
edit
, show
, list
, new
, delete
, search
. You cannot override their type (they are always of type method
) or their target (new
and search
are always entity
and the rest are item
). And you cannot change the way they are visualized (search
will always be an inline form).label
, their icon
and if they are enabled or not.
- The main constraint of your custom actions is the template used to display them: this is hardcoded and cannot be modified: some actions are displayed as buttons and other as links, depending on the page and the type of action.
Do you have a concrete example of this behavior? I don't fully get it :confused:
@Pierstoval I was referring to "the thing" that users click to activate your action. Sometimes is a button and sometimes is a link (e.g. in the listings of items). You cannot change that to do fancy things. For example, the search
action is neither a button nor a link: it's a form. You cannot do that with your actions: you'll always get a button or a link.
So if I resume:
entity
will be showed next to the current add
button?item
will be showed in the actions
column of the list page and/or (depending on the location where you put your action in the config) next to the save
, delete
and back to list
buttons in the edit
, new
and show
pages?We're missing the non-entity related actions, such as dashboards.
@Pierstoval :+1: you explained it perfectly!!
Great then !
For non-entity related actions, I think we'll need to setup an easy method later, because I think these kind of actions will have their own place in the navbar...
By the way, I think, with some steps back over the situation, that making a difference between entity
and item
is not very easy. Maybe something like list_head
and object_view
would be easier to understand, because in fact this attribute is a link_position
or button_placement
.
What do you think about it?
@Pierstoval to be honest, I'm waiting for the implementation to decide if this option remains or if it's removed. At the end, we'll probably start with "item actions" for list
and "entity actions" for the rest of pages.
EasyAdmin is a monolithic piece of software, and I proudly say that as a good feature, not as something wrong.
Yeah, I don't see this as something wrong neither. You want to provide something strong and stable. Something far away and beyond the complexity of other admin generation bundles. I got it (this is not sarcasm). I'm glad to talk with you about all of this, and I thank you for considering all of my comments with so much attention. I only fear that this project is closing in onto itself, relying on its great capabilities of settling a very nice and clean CRUD backend in a snap, to argue that it should also do more, only by itself. You realized in your comment:
The main constraint of your custom actions is the template used to display them: this is hardcoded and cannot be modified
that this approach is quite limiting the developer ability to customize the backend, an there are many things that will be kept "hardcoded" that way.
The main drawback of other admin generators is the time and the effort necessary to build the very basis of an admin backend with our entities. Which EasyAdmin bundle already get rid of. Now it should not limit itself by providing implementations of specific behaviors, but instead providing enough extension points and interfaces for the developer to develop the whole admin backend around the easy admin core.
If you're not in this mindset for now, I don't even think that introducing actions types nor other advanced features about actions will be relevant. Therefore, your first proposal about only item actions perfectly fits the bundle's perspective, and I apologize for having continuously looped on my own concerns. Everything else might be done in another application part (we should at least provide a way to extend the bundle's layout more efficiently if the developer wants to keep consistency between the different backend parts).
Anyway, lately I was thinking about a system similar to symfony's form themes in order to override the easyadmin types widgets and other parts only from templating (The TwigExtension will not generate the templating code anymore but call the proper widget from a theme template instead). The same should have been applicable to actions. But it seems really really complicated to achieve such a thing as powerfully as the form theming.
With EasyAdmin it should be easy to customize forms rendering : create your own "form definition" like the form_div_layout.html.twig
in the framework, and add it in the Twig config, it's the simplest way to do it, and it works with EasyAdmin.
Plus, it also works when creating custom form types and adding their layout in your twig config.
For the rest, I agree in the fact that EasyAdmin should be extensible in every possible way, and in fact, I'm wondering if we had to create a whole new bundle for it, that adds features to EasyAdmin (exactly like what SensioFrameworkExtraBundle
does to the FrameworkBundle
, for instance)
I apologize for having continuously looped on my own concerns
No need to apologize at all. In fact, we must be very "selfish" and demand support for solving the needs of our backends (e.g. I added boolean flip switches because I selfishly need them in some of my backends). So please, keep asking and demanding anything that you (real backend users) need for your backends. The only thing we should keep in mind is that we target the 80% simplest backends. If your needs are very complex, go for the powerful SonataAdmin bundle.
Yes, we're going to allow to use your own themes, but let's do one thing a time. My current personal roadmap is as follows:
-to-many
fields and add support for many-to-many
)@Pierstoval : Yep, I'm actually talking about that...for non-form elements: entity fields that are rendered through the TwigExtension, custom actions links/buttons, any dynamically determined and generated part of the templating... The idea is that those elements are in a easyadmin_theme_layout.html.twig
, the same way as in form_div_layout.html.twig
, and if the user isn't satisfied of how is currently rendered the boolean
entity field or its custom actions buttons on the list page, he can provide his own theme.
As I said, it's a complex feature (to implement, not to use. You can have a look at how works the Symfony's FormExtension and FormThemeTokenParser) which may provide a not so much exciting result. But before digging into the code, I found this principle interesting for EasyAdmin theming.
@javiereguiluz => For the Doctrine Associations, work will be quite hard, but I still think that we can just use configuration values on "how to render the relation", combined with specific twig views for how to represent it. Managing the form is easier if you need an embedded form, a little JS script for *ToMany
relations (in order to duplicate the embedded form) , some loops, and in theory it does not sound so hard. And it may work on each relation type. (remember this comment on #133 )
@ogizanagi : Using a form theme is the easiest way to do it whatsoever, in my opinion. You create a theme, use it, and if you want to customize it, some docs about template overridance in Resources/EasyAdminBundle/views/easyadmin_form_theme.html.twig
, or even as a config parameter form_theme: AppBundle:Form:admin_form_theme.html.twig
and in the admin views you load the form_theme dynamically from the config parameter...
I think I might theorize very fast and very much about all of this, but I make the best I can to propose a very good solution to each needs with the most extensible way possible :confused:
That said, I think this issue should be renamed "State of the project" :wink: :smile:
I'm glad to finally close this issue as solved by #202
By the way, impressive stats of the related PR:
This is a huge step, you made it perfectly, and I'm really glad that this bundle is becoming really big in terms of features!
We've talked a lot about custom actions and there are already some PR to add them (#119 and #137). So it's time to add custom actions in EasyAdmin.
Before writing a single line of code, let's agree on what we want to do and how are going to do it. This is what I propose:
1) New custom_actions option
I prefer not to use the existing
actions
option because that would mean to redefine all entity actions whenever you add a custom action. Example:The
User
entity has just lost all the default actions:edit
,show
,delete
, etc. To avoid losing these actions, you have to write them whenever you use a custom action:If Symfony YAML component supported references, we could do the following trick:
But this is not supported, so I propose to define a new
custom_actions
, which solves all problems in a simple and pragmatic way:This is an entity-level option. You cannot define a
custom_actions
global option to apply it to all entities and you cannot specify custom actions per entity page (list
,edit
, etc.) All custom actions will be displayed in thelist
action of the entity.2) How do custom actions work
Common case: define a method called as the custom action
EasyAdmin executes the
grantAccess()
method of theAdminController
and it will pass as argument theid
of the active item. Obviously you have to define your ownAdminController
extending from the default one.Extended configuration for the common case: define a label for the action
Custom case: define the name of the route which will respond to the action
In this case, a link will be generated with the
path()
Twig function using that route name and passing theid
of the active item as the only route parameter.