Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom buttons #38

Closed
tabacitu opened this issue Jun 15, 2016 · 8 comments
Closed

Custom buttons #38

tabacitu opened this issue Jun 15, 2016 · 8 comments
Milestone

Comments

@tabacitu
Copy link
Member

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:

  • show the button to some users and others not.
  • show the button for some entries and others not.
  • show different url depending on some model attribute.
  • etc.

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.

@axyr
Copy link

axyr commented Jun 15, 2016

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);
}

@tabacitu
Copy link
Member Author

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:

  • you should be able to create a CRUD panel with as few files as possible;
  • any PHP MVC developer should be able to understand how a CRUD panel works, from beginner to expert;

That being said, I'm not saying no, I'm just wondering which way to go. @Ghitu @cristiantone @mariusconstantin2503 @fede91it ? What do you think?

@axyr
Copy link

axyr commented Jun 15, 2016

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.

@axyr
Copy link

axyr commented Jun 15, 2016

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.

@codydh
Copy link

codydh commented Jun 24, 2016

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.

@tabacitu
Copy link
Member Author

tabacitu commented Jul 3, 2016

Hmm, you're right, @codydh. So the places where you will be able to add buttons should be:

  • top of the list view (next to the Add button);
  • each line (before or after the Edit and Delete buttons);

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

  • you can create buttons that just go to a page or buttons that do something with javascript;
  • when bulk actions come along, I can easily see the 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?

@codydh
Copy link

codydh commented Jul 5, 2016

This makes complete sense to me.

should we just name the stacks top, bottom and line?

Seems sensible also.

@tabacitu
Copy link
Member Author

Done in 2.1 (dev branch right now). Documentation here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants