- Laravel v5.3+
- PHP v7.0.0+
- Install package
- Add this to
composer.json
in theautoload.psr-4
hash (just below"App\\": "app/"
):"VivienLN\\Pilot\\": "vendor/vivienln/pilot"
(required until the package is on packagist) - In
config/app.php
, add this in theproviders
array:VivienLN\Pilot\PilotServiceProvider::class,
- In a shell, execute
composer dump-autoload
- In a shell, execute
php artisan pilot:install
. This will copy configuration files and assets to your main directory, run migrations and create empty policies if you need so. - Configure pilot in
config/pilot.php
and policies (see below)
All configuration is done within the config/pilot.php
file.
The most basic setup is done by providing model class names such as follow:
return [
'models' => [
['model' => 'App\Post'],
['model' => 'App\Category'],
],
];
This will, by default, show all columns of this models and use models and columns names for display. However, you still need to define policies to setup user permissions (see below).
Note: If you use config cache, do not forget to refresh it:
php artisan config:cache
Note:
model
is the only required key to make Pilot aware of your models, but you can customize many more things in each array. See Documentation for more information.
Each user can have one or more roles that define what permissions they have. By defaut, a super-admin
role is created.
Note: When you install pilot, 2 tables are created to handle roles:
pilot_roles
andpilot_role_user
.
To associate a user with a role, use the pilot:grant
command:
php artisan pilot:grant super-admin 1
The parameters are:
- The slug of the role to give the user(s)
- The user id
The pilot:deny
command let you do the exact opposite, removing a role from a user. The parameters are the same.
Pilot uses the Laravel Policies to determine whether or not a user can do an action. If you are not familiar with policies, read the official documentation about policies.
For each model you want to use with Pilot, you must create and declare a Policy. When installing pilot, you can automatically generate policies.
If you do not want to use this feature, or juste want to add policies later, you can still use artisan: php artisan make:policy MyModelPolicy --model=MyModel
In any case, you must register your policies in app/Providers/AuthServiceProvider.php
, in the $providers
array.
Note: You need to declare models in the Pilot config file (see below) and setup policies to browse them in pilot. Others are simply ignored.
The following methods are recognized by Pilot and will be automatically used:
view(User $user, Model $model)
Return whether the user can view the model. Models user cannot view are excluded from the table view.create(User $user)
Return whether the user can create a new model.update(User $user, Model $model)
Return whether the user can edit the model. If the user can view the model but not update it, the form will be shown but disabled.delete(User $user, Model $model)
Return whether the user can delete the model. //TODO: used??list(User $user)
Return whether the user can access the table view listing the instances of the model. Excludes the ones he/she cannot view.
Note: The
view
,create
,update
, anddelete
methods are automatically added when you use Artisan to generate a policy class. Only thelist
method must be manually added.
Note: If a method is not defined, no user will have the right to perform the action.
Do not forget you can use the before
method.
Let the user update only the posts they created. They still can view other posts but not update them (form fields are greyed-out).
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Let the user view only the posts they created. Others post will not be listed in the table view.
public function view(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Allow the user to view and edit all their posts, and hide unpublished posts from other users.
public function view(User $user, Post $post)
{
return $user->id === $post->user_id;
}
public function view(User $user, Post $post)
{
return $user->id === $post->user_id || $post->is_published;
}
Note: If a user have the update permission, but not the view permission, they will still be unable to create or update an item, because they won't have access to the form.
All configuration is done within the config/pilot.php
file, in the form of an array.
To better understand what follows, you must know what pages are used in Pilot. Here is the list:
- index: The dashboard of the admin panel.
- table: For one model, the list of all the instances in database.
- edit: The edit/create form of a model.
You can set a custom prefix for all the administration routes, with the prefix
key:
'prefix' => 'admin',
The default value is admin
, meaning you'll access the panel with /admin/
.
The title
key lets you provide a custom HTML <title>
for the admin panel pages.
'title' => 'My awesome panel',
[TODO]
The models
array is the most important part of the configuration.
Again, the simplest pilot.php
configuration file is as follow:
return [
'models' => [
['model' => 'App\Post'],
['model' => 'App\Category'],
],
];
Each sub arrays of models
can contain many variables to customize further how Pilot interacts with your models.
See below for more information. Each of the following properties can be added to the model arrays.
Array
(default: all columns)
For each model, you can provide a columns
array, specifying which properties/columns will be shown and editable. You can simply specifies columns in the array:
'models' => [
[
'model' => 'App\Post',
'columns' => ['id', 'title', 'category'],
],
['model' => 'App\Category'],
],
Or ou can pass more parameters for each column, by defining an array with the column name as key, ie.:
'models' => [
[
'model' => 'App\Post',
'columns' => [
'id'=> ['display' => '#'],
'title',
'category',
],
],
['model' => 'App\Category'],
],
Continue reading for all the properties of columns
.
String
(default: property name)
How the column/property will be displayed in admin panel.
Boolean
(default: true
)
If false, the input will be disabled, but it will be displayed. This is especially usefull for id fields.
Array
or String
(default: []
)
In a filters
array, you can pass pre-defined filters as string, that will be applied when displaying data in the table view.
Some filters accept a parameter, in which case they are passed with the syntax filter:parameter
.
limit:<limit>
: The maximum number of characters to display; adds an ellipsis ("...") if needed.asset:<width>
: Displays an image withasset(<value>)
assrc
attribute. You can provide a width to resize it.link
: Displays property as a link to the edit view.
Example:
'models' => [
[
'model' => 'App\Post',
'columns' => [
'title'=> [
'display' => 'Title',
'filters' => ['limit:10', 'link'],
],
'category',
'id',
],
],
]
Note: You can obviously set multiple filters, but note that they are applied in the order of the array, so for example
['link', 'limit:10']
will put a link around the text and then cut it at 10 characters. In this case you should write['limit:10', 'link']
Note: You can pass
filters
as a String if there is only one filter.
Callable
(default: null
)
If you need further customization, you can use on or more filter hooks:
filters_before
filters_after
Use them as keys in the model array, and a custom callback as value, ie:
'models' => [
[
'model' => 'App\Post',
'columns' => [
'title'=> [
'display' => 'Title',
'filters' => 'limit:10',
'filters_before' => function($item, $value, $reflector) {
// your code
}
],
'category',
'id',
],
],
]
As the names imply, filter_before
and filter_after
will be called respectively before and after the filters defined by filters
.
Both functions accept the following parameters (in this order):
$item
(Model): The current item whose value is filtered$value
(String): The value being filtered. May have been altered by previous filters in the case offilter_after
$reflector
An instance ofVivienLN\Pilot\ModelReflector
used for the current model
Both functions must return the filtered value as a string.
Note: value is initially filtered through PHP
strip_tags()
(before all filters and hooks), but the final output will be shown as HTML.
String
(default: null
)
When the "column" is actually a related model, use this property to define which attribute of the related model must be displayed.
Array
(default: []
)
Used for eager loading. Specify the relationships to load along the models, for the table view.
'models' => [
[
'model' => 'App\Post',
'with' => ['author', 'categories'],
],
]
Boolean
(default: true
)
Whether the column will be shown in table view. This lets you hide from this view some property with lengthy content (for example a post text).
For each column, you can specify with the form
key which form element will be used to edit the value.
These are the available values:
text
(default)textarea
checkbox
select:option1,option2,...
select_multiple:option1,option2,...
Note: If your column corresponds to a related model, you do not need to define the
form
property.
In the columns
array, you can use relationships, or even custom attributes.
In the end, the attribute simply should be callable, eg:
'columns' => [
'foo'=> [
...
],
],
Will automatically call $yourModel->foo()
, no matter how this method is defined.
However, there are some things to know to use relations.
If you have defined a relationship, when displaying its value, by default Laravel will show the whole related model as a JSON string.
Use the related_attribute
property on the column array to specify which attribute will be used.
Note: Currently, in filters and hooks, the
$item
is still the current model (not the related model). This means that, for example, thelink
filter will NOT link to the related model. Note: Many To Many relationships models will be displayed separated by commas. Note: You do not need to define theform
attribute for related models. Pilot will automatically use a select / multiple select, using therelated_attribute
for values.
Just define a dynamic attribute getter on your model, eg:
public function getFooAttribute()
{
return 'bar';
}
You can then use it in the columns
array of your model.
String
(default: "list"
)
For each model, you can customize which icon will be used.
You can use:
- One of the default icons by using its name (see list below)
- A custom image url (use the
asset()
helper)
Note: Any SVG icon will be inserted as inline svg, to allow control from CSS.
The available default icons are:
-
box
-
box2
-
calendar
-
chart
-
checkmark
-
comment
-
dashboard
-
down
-
edit
-
eye
-
file
-
folder
-
gear
-
heart
-
left
-
link
-
list
-
lock
-
mail
-
mail2
-
off
-
gamepad
-
pin
-
remove
-
right
-
rocket
-
search
-
star
-
tag
-
trash
-
up
-
user
Usage example:
[
'model' => 'App\Post',
'icon' => 'rocket',
...
]
Integer
(default: 25
)
Pilot uses pagination in table view, with LengthAwarePaginator
. With per_page
you can customize the page size for each model.
Array
(default: null
)
From Laravel documentation:
Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular".
You can configure Pilot to add tabs on top of the table view, each tab corresponding to a given scope.
To do so, add in your model array a scopes
array which will contain scope names as keys, and tab texts as values.
[
'model' => 'App\Post',
'scopes' => [
'published' => 'Published'
],
...
]
This will add a "Published" tab on top on the table view. This tab will call $model->published()
, that will look for a scopePublished()
method on your model. It is up to you to implement it such as described in Laravel documentation.
When you setup at least one scope, a "All" tab will automatically be added at the beginning and showing by default.
When you install Pilot, all the needed views are published to your app resources/views/vendor/pilot
folder.
You can edit these views as much as you want:
layout
: The layout of the Pilot administration panelindex
: The admin Dashboardtable
: The table listing all entries of one modeledit
: The edit/create form for one modelpartials/edit/
: Views used in the edit/creation form, one for each input typepartials/table/
: Views used in the table view
Note: When overriding default views, be careful of Auth checks (
@can
and@cannot
directives). Even if the controller will block unauthorized actions, you may not want to leave links and buttons leading to 403 errors.
Here is a list of all the variables used in the views.
- To all views:
$pilot
(VivienLN\Pilot\Pilot
): ThePilot
object$user
(App\User
): The current user$title
(String
): The title of the page, dynamically generated by controller
layout
- No additional variables
index
- No additional variables
table
$items
(Illuminate\Database\Eloquent\Collection
): A collection of all the models displayed in table$slug
(String
): The slug of the currentModel
class, used by reflectors and routes$reflector
(VivienLN\Pilot\ModelReflector
): A reflector for the currentModel
class$scopes
(Array
): All the scopes available for the currentModel
class$scope
(String
): Current scope (can be null; see Models > scopes
edit
$model
(Illuminate\Database\Eloquent\Model
): The currentModel
being edited$slug
(String
): The slug of the currentModel
, used by reflectors and routes$reflector
(VivienLN\Pilot\ModelReflector
): A reflector for the currentModel
class$canSave
(Boolean
): Flag that defines if a user can save (update model) or not.
You can set custom admin routes in your main routes file. Just remember to use the RedirectGuest
middleware provided by Pilot to redirect non-authenticated users:
['middleware' => [VivienLN\Pilot\Middleware\RedirectGuest::class]
You can of course create a controller extending PilotController
to keep and add features.
This is a sample pilot.php
file, combining all the options see above.
return [
'prefix' => 'admin123456',
'models' => [
['model' => 'App\Category'],
['model' => 'App\Tag'],
[
'model' => 'App\Post',
'display' => 'Blog posts',
'slug' => 'blog_post',
'with' => ['category', 'tags'],
'columns' => [
'id' => [
'display' => '#'
],
'picture' => [
'display' => 'Picture',
'filters' => 'asset:120',
],
'title' => [
'display' => 'Titre',
'filters' => ['limit:10', 'link'],
],
'is_published' => [
'show_in_table' => false,
],
],
],
],
];