Developers should be able to add custom buttons. Proposed syntax:
$this->crud->addButton('button_name', 'model_function_name');
// by default this will place the button before edit and delete
// ex: $this->crud->addButton('send_email', 'getSendEmailButton');
// other methods:
$this->crud->removeButton('send_email');
$this->crud->setButtonsOrder(['edit', 'send_email', 'delete']);
This way, I think, we'll leave a lot of space for customization. On your model, you could do something like:
public function getSendEmailButton() {
return "<a class='btn btn-warning' href='some_link'><i class='fa fa-envelope'> Send email</a>";
}
and you can have your checks or complex logic there. Ex:
What I don't like about this is that it does imply you'll be writing some HTML in the model, which is a bit ugly... What do you think? Any better solutions?
Note: I also see the need for custom bulk buttons in the future. But that's another topic.
How about something like this :
$this->crud->addButton(new SendEmailFormAction()); // addButton typehint implements CrudFormActionHandler
// extends CrudFormAction ?
class SendEmailFormAction implements CrudFormActionHandler
{
public function getView()
{
return '<button name="handleRequest" type="submit">Send Email</button>';
}
public function handleRequest(CrudRequest $request, Crud $crud)
{
// send the email to $crud->getRecord() and use $request->all();
// redirect, show message, something
}
}
interface CrudFormActionHandler
{
public function handleRequest(CrudRequest $request, Crud $crud);
}
This is a more correct approach from a programming perspective, I agree.
Hmmm... I think this brings up the question of how abstract do we want the core to be? A lot of questions I get on Github/readme.io/email are very very basic, so I conclude a lot of beginner/intermediate PHP programmers are using Backpack. I'm already spending about 3-4 hours a day answering questions, with only 1000 downloads. I can't imagine what would come after 10.000 downloads or 20.000 downloads and a deeper level of abstraction.
The only solution I see here is that, in order to cater to them too, but also to advanced programmers, we need to keep abstraction as low as possible, because advanced/expert programmers will always be able to customize it however they like, use extra classes, interfaces, traits, whereas beginner/intermmediates won't know how to.
I think we should keep these core principles in mind:
That being said, I'm not saying no, I'm just wondering which way to go. @Ghitu @cristiantone @mariusconstantin2503 @fede91it ? What do you think?
https://laravel-backpack.readme.io/docs/crud-fields
This will become an array hell:
[ // PageOrLink
'name' => 'type',
'label' => "Type",
'type' => 'page_or_link',
'page_model' => '\Backpack\PageManager\app\Models\Page'
],
[ // Select
'label' => "Category",
'type' => 'select',
'name' => 'category_id', // the db column for the foreign key
'entity' => 'category', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'model' => "App\Models\Tag" // foreign key model
],
[ // SelectMultiple = n-n relationship (with pivot table)
'label' => "Tags",
'type' => 'select_multiple',
'name' => 'tags', // the method that defines the relationship in your Model
'entity' => 'tags', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'model' => "App\Models\Tag", // foreign key model
'pivot' => true, // on create&update, do you need to add/delete pivot table entries?
]
No IDE autocompletion possible, so you always have to go to the docs to see the options possible for maybe tens (>100?) of different form fields.
When using objects, you can apply sensible defaults for, name, attribute, label based on the field name, with auto translations like label('MyModel.field_name', 'default label');
Also the model can be guessed for a FormField user_id that it should find the hasOne User();
So you can do :
new DropdownField('user_id', /*optional name*/, /* optional collection or array of items*/);
And by default it will will provide a map of ['id', getTilte()] or something.
That will help beginners a lot, by having real usable sensible defaults by just boilerplating their models and controllers.
Please see these examples how I use your crud right now (work in progress):
https://gist.github.com/axyr/4328d6dba6462d6639347e226182d218
https://gist.github.com/axyr/336934ff114cb943e26bdf9823affd97
https://gist.github.com/axyr/2bf47beaa6af77d7fd48836a694372d2
By applying namingconventions, I only have to create MyModelController extends ModelAdminController to get a list and form view for MyModel.
I'm not sure if this is within your thinking as well, but there's also (IMO) a pretty strong need for custom buttons on a given view, e.g. if I would want to add an "Export to CSV" button that exports the current view to a CSV.
Hmm, you're right, @codydh. So the places where you will be able to add buttons should be:
So then, I think it's best to define some "stacks" where we have buttons and let developers insert buttons there. The stacks above could be named top_buttons and line_buttons
$this->crud->addButtonFromModelFunction('stack_name', 'button_name', 'model_function_name');
$this->crud->addButtonFromView('stack_name', 'button_name', 'view_path');
// other methods:
$this->crud->removeButton('stack_name', 'send_email');
$this->crud->setButtonsOrder('stack_name', ['edit', 'send_email', 'delete']);
This should be pretty future-proof, because
bottom_buttons stack coming too, and the syntax doesn't need to change then;...or should we just name the stacks top, bottom and line?
This makes complete sense to me.
should we just name the stacks top, bottom and line?
Seems sensible also.
Done in 2.1 (dev branch right now). Documentation here.