| description | Admin's DefaultController is a highly configurable boilerplate for simplifying day-to-day management of your application's entities. |
|---|
Nails\Admin\Controller\DefaultController is a highly configurable preset which will generate a CRUD interface bound to a specific model. Its purpose is to abstract the views, validation, and saving of model items.
To use this in your application's admin controller, they must extend it and define, at minimum, the constant CONFIG_MODEL_NAME.
namespace App\Admin\App;
use Nails\Admin\Controller\DefaultController;
class Books extends DefaultController
{
const CONFIG_MODEL_NAME = 'Books';
}{% hint style="info" %} Quickly generate admin DefaultController controllers using the console:
nails make:controller:admin
{% endhint %}
The DefaultController has many configuration constants. All described with their defaults shown below.
{% hint style="info" %}
These constants are used by the controller's getConfig method which populates the $aConfig class property. Some configurations accepts closures which cannot be set via a constant); to set these you can use Late Configuration.
{% endhint %}
The following constants define to which model the controller is bound:
const CONFIG_MODEL_NAME = '';
const CONFIG_MODEL_PROVIDER = 'app';The permission string to use when checking permissions; if not provided then no permissions required.
const CONFIG_PERMISSION = '';The singular and plural name of the item being managed; defaults to the model name.
const CONFIG_TITLE_SINGLE = '';
const CONFIG_TITLE_PLURAL = '';Where to display this controller in the admin sidebar (defaults to CONFIG_TITLE_PLURAL) and which icon to use.
const CONFIG_SIDEBAR_GROUP = '';
const CONFIG_SIDEBAR_ICON = '';The format for the sidebar link
const CONFIG_SIDEBAR_FORMAT = 'Manage %s';The base URL for this controller
const CONFIG_BASE_URL = '';Specify whether the controller supports item creation.
const CONFIG_CAN_CREATE = true;Specify whether the controller supports item editing.
const CONFIG_CAN_EDIT = true;Specify whether the controller supports linking to the item.
const CONFIG_CAN_VIEW = true;Specify whether the controller supports item deletion
const CONFIG_CAN_DELETE = true;Specify whether the controller supports item restoration
const CONFIG_CAN_RESTORE = true;The fields to show on the index view. Column name on the left, property name on the right. Dot notation can be used to reach deeper properties, for example items made available via CONFIG_INDEX_DATA.
const CONFIG_INDEX_FIELDS = [
'Label' => 'label',
'Created' => 'created',
'Modified' => 'modified',
'Modified By' => 'modified_by',
];{% hint style="info" %} If you need to return dynamic data then a closure can be set as the right hand side via Late Configuration. {% endhint %}
Any additional header buttons to add to the index page.
const CONFIG_INDEX_HEADER_BUTTONS = [];Any additional buttons to add to each row on the page.
const CONFIG_INDEX_ROW_BUTTONS = [];Additional data to pass into the getAll call on the index view.
const CONFIG_INDEX_DATA = [];The fields on the index view which should be rendered as user cells.
const CONFIG_INDEX_USER_FIELDS = [
'created_by',
'modified_by',
'user_id',
];The fields on the index view which should be rendered as boolean cells.
const CONFIG_INDEX_BOOL_FIELDS = [
'is_active',
'is_published',
'is_deleted',
];The fields on the index view which should be run through number_format.
const CONFIG_INDEX_NUMERIC_FIELDS = [
'id',
];The fields on the index view which should be centred.
const CONFIG_INDEX_CENTERED_FIELDS = [
'id',
];The ID to give the index page
const CONFIG_INDEX_PAGE_ID = '';The sorting options to give the user on the index view:
const CONFIG_SORT_OPTIONS = [
'Label' => 'label',
'Created' => 'created',
'Modified' => 'modified',
];The default sorting order:
const CONFIG_SORT_DIRECTION = 'asc';Fields which should be marked as readonly when creating an item
const CONFIG_CREATE_READONLY_FIELDS = [];The fields to ignore on the create view
const CONFIG_CREATE_IGNORE_FIELDS = [
'id',
'slug',
'token',
'is_deleted',
'created',
'created_by',
'modified',
'modified_by',
];The fields to ignore on the edit view
const CONFIG_EDIT_IGNORE_FIELDS = self::CONFIG_CREATE_IGNORE_FIELDS;Fields which should be marked as readonly when editing an item
const CONFIG_EDIT_READONLY_FIELDS = [];Additional data to pass into the getAll call on the edit view
const CONFIG_EDIT_DATA = [];Any additional header buttons to add to the edit page.
const CONFIG_EDIT_HEADER_BUTTONS = [];Specify a specific order for fieldsets
const CONFIG_EDIT_FIELDSET_ORDER = [];The ID to give the edit page
const CONFIG_EDIT_PAGE_ID = '';{% hint style="info" %}
The following apply to model's which implement the Sortable trait.
{% endhint %}
Additional data to pass into the getAll call on the sort view
const CONFIG_SORT_DATA = [];Which column to use for the label when sorting
const CONFIG_SORT_LABEL = 'label';Any additional columns to add to the sort view
const CONFIG_SORT_COLUMNS = [];Enable or disable the "Notes" feature
const EDIT_ENABLE_NOTES = true;Whether to record updates in the admin change log
const CHANGELOG_ENABLED = true;The name to use when creating changelog items, defaults to the resource class name
const CHANGELOG_ENTITY_NAME = null;An array of fields to ignore when processing change log updates
const CHANGELOG_FIELDS_IGNORE = [
'id',
'is_deleted',
'created',
'created_by',
'modified',
'modified_by',
];An array of fields to redact/mask when processing changelog updates
const CHANGELOG_FIELDS_REDACT = [
'password',
];Message displayed to user when an item is successfully created
const CREATE_SUCCESS_MESSAGE = 'Item created successfully. %s';Message displayed to user when an item fails to be created
const CREATE_ERROR_MESSAGE = 'Failed to create item.';Message displayed to user when an item is successfully updated
const EDIT_SUCCESS_MESSAGE = 'Item updated successfully. %s';Message displayed to user when an item fails to be created
const EDIT_ERROR_MESSAGE = 'Failed to update item.';Message displayed to user when an item is successfully deleted
const DELETE_SUCCESS_MESSAGE = 'Item deleted successfully.';Message displayed to user when an item fails to be deleted
const DELETE_ERROR_MESSAGE = 'Failed to delete item.';Message displayed to user when an item is successfully restored
const RESTORE_SUCCESS_MESSAGE = 'Item restored successfully.';Message displayed to user when an item fails to be restored
const RESTORE_ERROR_MESSAGE = 'Failed to restore item.';Message displayed to user when an items are ordered successfully
const ORDER_SUCCESS_MESSAGE = 'Items ordered successfully.';Message displayed to user when an item fails to be ordered
const ORDER_ERROR_MESSAGE = 'Failed to order items.';Message displayed to user when an item is successfully copied
const COPY_SUCCESS_MESSAGE = 'Item copied successfully.';Late configuration is the process of setting or changing a configuration value after the controller has been initiated. Typically this is used to apply closures to configuration fields which accept them, but might be used to change a config based on some other context.
For example, if you wish to show a column which contains dynamic information about the item you'd add a closure to the CONFIG_INDEX_FIELDS config field. The following example shows a count of the total number of reviews plus the average review score.
<?php
// Expand the book reviews
const CONFIG_INDEX_DATA = ['expand' => ['reviews']];
// Define the initial column layout, with palceholder for `Reviews`
const CONFIG_INDEX_FIELDS = [
'Label' => 'label',
'Reviews' => '',
'Created' => 'created',
'Created By' => 'created_by',
];
// Overwrite the `Reviews` column
public function __construct()
{
parent:__construct();
$this->aConfig['INDEX_FIELDS']['Reviews'] => function($oBook) {
$iTotal = 0;
foreach ($oBook->reviews->data as $oReview) {
$iTotal += $oReview->score;
}
return sprintf(
'%s total reviews; Average: %s/5',
$oBook->reviews->count,
number_format($iTotal / $oBook->reviews->count, 2)
);
};
}The index view is the overview of all the items managed by the controller. It is searchable, paginated and filterable.
The DefaultController offers two types of filters which can be applied to the index data set: Dropdown and checkbox. Both are almost identical in function, however the checkbox filter allows multiple values to be selected.
Both flavours are defined using the IndexFilter factory, depending on whether you want the filter be a dropdown or a checkbox then place the IndexFilter definition in the controller's indexDropdownFilters or indexcheckboxFilters method respectively:
protected function indexDropdownFilters(): array
{
return [
Factory::factory('IndexFilter', 'nails/module-admin')
->setLabel('Status')
->setColumn('status')
->addOptions([
Factory::factory('IndexFilterOption', 'nails/module-admin')
->setLabel('Published')
->setValue('PUBLISHED')
->setIsSelected(true),
Factory::factory('IndexFilterOption', 'nails/module-admin')
->setLabel('Draft')
->setValue('DRAFT')
]),
];
}Multiple distinct filters will be joined together using an AND operator. When using checkbox filters, the multiple selected options will be joined using an OR operator.
For example, if you had a dropdown filter Published[Yes|No] and a checkbox filter Publisher[ACME Books Ltd|Penguin Publishings] then the resulting query when both checkbox items were checked and the dropdown was set to Yes would be:
SELECT *
FROM books
WHERE
(is_published = 'Yes')
AND (publisher = 'ACME Books Ltd' OR publisher = 'Penguin Publishings')
{% hint style="info" %}
For advanced filtering, you may set the IndexFilterOption item to behave as an SQL query by using setIsQuery(true). This will tell the Filter that the value you supply using setValue() should be executed as an SQL query.
{% endhint %}
Each item on the index has actions which can be applied to it, typically these are View, Edit and Delete; however it is easy to add additional buttons via the CONFIG_INDEX_ROW_BUTTONS configuration:
const CONFIG_INDEX_ROW_BUTTONS = [
[
// The button's value/label
'label' => 'The button label',
// The button's URL, item properties can be
// substituted in using Mustache syntax.
'url' => 'edit/{{id}}'
// Additional classes to apply to the button
'class' => 'btn-primary'
// Permission required in order to render
// the button
'permission' => 'edit'
// Any additional attributes to apply to
// the button
'attr' => '',
// Whether the button is enabled or not
// Only available when the button is created
// via Late Configuration
'enabled' => function($oItem) {
return true;
},
]
];It is also possible to group buttons into a button group by passing in multiple URLs via the button's url property. The following example uses Late Configuration to add the button, which will only render for published books:
public function __construct()
{
parent::__construct();
$this->aConfig['INDEX_ROW_BUTTONS'][] = [
'label' => 'Download',
'class' => 'btn-primary',
'enabled' => function ($oItem) {
return $oItem->status === 'PUBLISHED';
},
'url' => [
'As PDF' => 'download/{{id}}/pdf',
'As HTML' => 'download/{{id}}/html',
'As eBook' => 'download/{{id}}/ebook',
],
];
}
