2011-02-07 07:48:44 +01:00
|
|
|
# ModelAdmin
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
Provides a simple way to utilize the SilverStripe CMS UI with your own data models,
|
|
|
|
and create searchable list and edit views of them, and even providing import and export of your data.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
It uses the framework's knowledge about the model to provide sensible defaults,
|
|
|
|
allowing you to get started in a couple of lines of code,
|
|
|
|
while still providing a solid base for customization.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-10-30 18:58:32 +01:00
|
|
|
The interface is mainly powered by the `[GridField](/reference/grid-field)` class,
|
2012-03-09 09:52:03 +01:00
|
|
|
which can also be used in other CMS areas (e.g. to manage a relation on a `SiteTree`
|
|
|
|
record in the standard CMS interface).
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Setup
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
Let's assume we want to manage a simple product listing as a sample data model:
|
|
|
|
A product can have a name, price, and a category.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
:::php
|
|
|
|
class Product extends DataObject {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $db = array('Name' => 'Varchar', 'ProductCode' => 'Varchar', 'Price' => 'Currency');
|
|
|
|
private static $has_one = array('Category' => 'Category');
|
2012-03-09 09:52:03 +01:00
|
|
|
}
|
|
|
|
class Category extends DataObject {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $db = array('Title' => 'Text');
|
|
|
|
private static $has_many = array('Products' => 'Product');
|
2012-03-09 09:52:03 +01:00
|
|
|
}
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
To create your own `ModelAdmin`, simply extend the base class,
|
|
|
|
and edit the `$managed_models` property with the list of
|
|
|
|
data objects you want to scaffold an interface for.
|
|
|
|
The class can manage multiple models in parallel, if required.
|
|
|
|
We'll name it `MyAdmin`, but the class name can be anything you want.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
|
|
|
:::php
|
2012-03-09 09:52:03 +01:00
|
|
|
class MyAdmin extends ModelAdmin {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $managed_models = array('Product','Category'); // Can manage multiple models
|
|
|
|
private static $url_segment = 'products'; // Linked as /admin/products/
|
2013-12-23 19:59:05 +01:00
|
|
|
private static $menu_title = 'My Product Admin';
|
2011-02-07 07:48:44 +01:00
|
|
|
}
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
This will automatically add a new menu entry to the CMS, and you're ready to go!
|
|
|
|
Try opening http://localhost/admin/products/?flush=all.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-12-17 00:47:30 +01:00
|
|
|
## Permissions
|
|
|
|
|
|
|
|
Each new `ModelAdmin` subclass creates its own [permission code](/reference/permission),
|
|
|
|
for the example above this would be `CMS_ACCESS_MyAdmin`. Users with access to the CMS
|
|
|
|
need to have this permission assigned through `admin/security/` in order to gain
|
|
|
|
access to the controller (unless they're admins).
|
|
|
|
|
|
|
|
The `DataObject` API has more granular permission control, which is enforced in ModelAdmin by default.
|
|
|
|
Available checks are `canEdit()`, `canCreate()`, `canView()` and `canDelete()`.
|
|
|
|
Models check for administrator permissions by default. For most cases,
|
|
|
|
less restrictive checks make sense, e.g. checking for general CMS access rights.
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class Category extends DataObject {
|
|
|
|
// ...
|
|
|
|
public function canView($member = null) {
|
|
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
|
|
}
|
|
|
|
public function canEdit($member = null) {
|
|
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
|
|
}
|
|
|
|
public function canDelete($member = null) {
|
|
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
|
|
}
|
|
|
|
public function canCreate($member = null) {
|
|
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
|
|
}
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Search Fields
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
ModelAdmin uses the `[SearchContext](/reference/searchcontext)` class to provide
|
|
|
|
a search form, as well as get the searched results. Every DataObject can have its own context,
|
|
|
|
based on the fields which should be searchable. The class makes a guess at how those fields
|
|
|
|
should be searched, e.g. showing a checkbox for any boolean fields in your `$db` definition.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
To remove, add or modify searchable fields, define a new `[$searchable_fields](api:DataObject::$searchable_fields)`
|
|
|
|
static on your model class (see `[SearchContext](/reference/searchcontext)` docs for details).
|
2011-02-07 07:48:44 +01:00
|
|
|
|
|
|
|
:::php
|
|
|
|
class Product extends DataObject {
|
2012-03-09 09:52:03 +01:00
|
|
|
// ...
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $searchable_fields = array(
|
2011-02-07 07:48:44 +01:00
|
|
|
'Name',
|
2012-03-09 09:52:03 +01:00
|
|
|
'ProductCode'
|
|
|
|
// leaves out the 'Price' field, removing it from the search
|
2011-02-07 07:48:44 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
For a more sophisticated customization, for example configuring the form fields
|
|
|
|
for the search form, override `[api:DataObject->getCustomSearchContext()]` on your model class.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Result Columns
|
|
|
|
|
2012-10-30 18:58:32 +01:00
|
|
|
The results are shown in a tabular listing, powered by the `[GridField](/reference/grid-field)`,
|
2012-03-09 09:52:03 +01:00
|
|
|
more specifically the `[api:GridFieldDataColumns]` component.
|
|
|
|
It looks for a `[api:DataObject::$summary_fields]` static on your model class,
|
2012-11-30 09:53:01 +01:00
|
|
|
where you can add or remove columns. To change the title, use `[api:DataObject::$field_labels]`.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
|
|
|
:::php
|
2012-03-09 09:52:03 +01:00
|
|
|
class Product extends DataObject {
|
|
|
|
// ...
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $field_labels = array(
|
2012-11-30 09:53:01 +01:00
|
|
|
'Price' => 'Cost' // renames the column to "Cost"
|
|
|
|
);
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $summary_fields = array(
|
2012-11-30 09:53:01 +01:00
|
|
|
'Name',
|
|
|
|
'Price',
|
2012-03-09 09:52:03 +01:00
|
|
|
// leaves out the 'ProductCode' field, removing the column
|
2011-02-07 07:48:44 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Results Customization
|
|
|
|
|
|
|
|
The results are retrieved from `[api:SearchContext->getResults()]`,
|
|
|
|
based on the parameters passed through the search form.
|
|
|
|
If no search parameters are given, the results will show every record.
|
|
|
|
Results are a `[api:DataList]` instance, so can be customized by additional
|
|
|
|
SQL filters, joins, etc (see [datamodel](/topics/datamodel) for more info).
|
|
|
|
|
|
|
|
For example, we might want to exclude all products without prices in our sample `MyAdmin` implementation.
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class MyAdmin extends ModelAdmin {
|
|
|
|
// ...
|
2012-09-19 12:07:39 +02:00
|
|
|
public function getList() {
|
2012-03-09 09:52:03 +01:00
|
|
|
$list = parent::getList();
|
|
|
|
// Always limit by model class, in case you're managing multiple
|
|
|
|
if($this->modelClass == 'Product') {
|
|
|
|
$list->exclude('Price', '0');
|
|
|
|
}
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
You can also customize the search behaviour directly on your `ModelAdmin` instance.
|
|
|
|
For example, we might want to have a checkbox which limits search results to expensive products (over $100).
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class MyAdmin extends ModelAdmin {
|
|
|
|
// ...
|
2012-09-19 12:07:39 +02:00
|
|
|
public function getSearchContext() {
|
2012-03-09 09:52:03 +01:00
|
|
|
$context = parent::getSearchContext();
|
|
|
|
if($this->modelClass == 'Product') {
|
|
|
|
$context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
|
|
|
|
}
|
|
|
|
return $context;
|
|
|
|
}
|
2012-09-19 12:07:39 +02:00
|
|
|
public function getList() {
|
2012-03-09 09:52:03 +01:00
|
|
|
$list = parent::getList();
|
|
|
|
$params = $this->request->requestVar('q'); // use this to access search parameters
|
|
|
|
if($this->modelClass == 'Product' && isset($params['ExpensiveOnly']) && $params['ExpensiveOnly']) {
|
|
|
|
$list->exclude('Price:LessThan', '100');
|
|
|
|
}
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-15 22:14:09 +01:00
|
|
|
To alter how the results are displayed (via `[api:GridField]`), you can also overload the `getEditForm()` method. For example, to add a new component.
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class MyAdmin extends ModelAdmin {
|
|
|
|
// ...
|
|
|
|
public function getEditForm($id = null, $fields = null) {
|
|
|
|
$form = parent::getEditForm($id, $fields);
|
|
|
|
$gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass));
|
|
|
|
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
|
|
|
return $form;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Managing Relationships
|
|
|
|
|
|
|
|
Has-one relationships are simply implemented as a `[api:DropdownField]` by default.
|
|
|
|
Consider replacing it with a more powerful interface in case you have many records
|
|
|
|
(through customizing `[api:DataObject->getCMSFields]`).
|
|
|
|
|
2012-10-30 18:58:32 +01:00
|
|
|
Has-many and many-many relationships are usually handled via the `[GridField](/reference/grid-field)` class,
|
2012-03-09 09:52:03 +01:00
|
|
|
more specifically the `[api:GridFieldAddExistingAutocompleter]` and `[api:GridFieldRelationDelete]` components.
|
|
|
|
They provide a list/detail interface within a single record edited in your ModelAdmin.
|
2012-11-08 01:26:38 +01:00
|
|
|
The `[GridField](/reference/grid-field)` docs also explain how to manage
|
|
|
|
extra relation fields on join tables through its detail forms.
|
2012-10-04 15:59:36 +02:00
|
|
|
The autocompleter can also search attributes on relations,
|
|
|
|
based on the search fields defined through `[api:DataObject::searchableFields()]`.
|
2012-03-09 09:52:03 +01:00
|
|
|
|
|
|
|
## Permissions
|
|
|
|
|
|
|
|
`ModelAdmin` respects the permissions set on the model, through methods on your `DataObject` implementations:
|
|
|
|
`canView()`, `canEdit()`, `canDelete()`, and `canCreate`.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
In terms of access control to the interface itself, every `ModelAdmin` subclass
|
|
|
|
creates its own "[permission code](/reference/permissions)", which can be assigned
|
|
|
|
to groups through the `admin/security` management interface. To further limit
|
|
|
|
permission, either override checks in `ModelAdmin->init()`, or define
|
|
|
|
more permission codes through the `ModelAdmin::$required_permission_codes` static.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Data Import
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
The `ModelAdmin` class provides import of CSV files through the `[api:CsvBulkLoader]` API.
|
|
|
|
which has support for column mapping, updating existing records,
|
|
|
|
and identifying relationships - so its a powerful tool to get your data into a SilverStripe database.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
By default, each model management interface allows uploading a CSV file
|
|
|
|
with all columns autodetected. To override with a more specific importer implementation,
|
|
|
|
use the `[api:ModelAdmin::$model_importers] static.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Data Export
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
Export is also available, although at the moment only to the CSV format,
|
|
|
|
through a button at the end of a results list. You can also export search results.
|
|
|
|
It is handled through the `[api:GridFieldExportButton]` component.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-25 09:01:43 +02:00
|
|
|
To customize the exported columns, create a new method called `getExportFields` in your `ModelAdmin`:
|
2012-03-09 09:52:03 +01:00
|
|
|
|
|
|
|
:::php
|
|
|
|
class MyAdmin extends ModelAdmin {
|
|
|
|
// ...
|
2012-09-19 12:07:39 +02:00
|
|
|
public function getExportFields() {
|
2012-03-25 09:01:43 +02:00
|
|
|
return array(
|
|
|
|
'Name' => 'Name',
|
|
|
|
'ProductCode' => 'Product Code',
|
|
|
|
'Category.Title' => 'Category'
|
|
|
|
);
|
2012-03-09 09:52:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-25 09:01:43 +02:00
|
|
|
Dot syntax support allows you to select a field on a related `has_one` object.
|
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Extending existing ModelAdmins
|
|
|
|
|
|
|
|
Sometimes you'll work with ModelAdmins from other modules, e.g. the product management
|
|
|
|
of an ecommerce module. To customize this, you can always subclass. But there's
|
|
|
|
also another tool at your disposal: The `[api:Extension]` API.
|
|
|
|
|
|
|
|
:::php
|
|
|
|
class MyAdminExtension extends Extension {
|
2012-03-25 09:01:43 +02:00
|
|
|
// ...
|
2012-09-19 12:07:39 +02:00
|
|
|
public function updateEditForm(&$form) {
|
2012-03-09 09:52:03 +01:00
|
|
|
$form->Fields()->push(/* ... */)
|
|
|
|
}
|
|
|
|
}
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2013-03-27 12:06:57 +01:00
|
|
|
Now enable this extension through your `[config.yml](/topics/configuration)` file.
|
|
|
|
|
|
|
|
:::yml
|
|
|
|
MyAdmin:
|
|
|
|
extensions:
|
|
|
|
- MyAdminExtension
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
|
|
|
`updateSearchForm()`, `updateList()`, `updateImportForm`.
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
## Customizing the interface
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
Interfaces like `ModelAdmin` can be customized in many ways:
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
* JavaScript behaviour (e.g. overwritten jQuery.entwine rules)
|
|
|
|
* CSS styles
|
|
|
|
* HTML markup through templates
|
2011-02-07 07:48:44 +01:00
|
|
|
|
2012-03-09 09:52:03 +01:00
|
|
|
In general, use your `ModelAdmin->init()` method to add additional requirements
|
|
|
|
through the `[Requirements](/reference/requirements)` API.
|
|
|
|
For an introduction how to customize the CMS templates, see our [CMS Architecture Guide](/reference/cms-architecture).
|
2011-02-07 07:48:44 +01:00
|
|
|
|
|
|
|
## Related
|
|
|
|
|
2012-11-30 23:57:07 +01:00
|
|
|
* [GridField](../reference/grid-field): The UI component powering ModelAdmin
|
|
|
|
* [Tutorial 5: Dataobject Relationship Management](../tutorials/5-dataobject-relationship-management)
|
2011-02-07 07:48:44 +01:00
|
|
|
* `[api:SearchContext]`
|
2012-01-01 18:24:09 +01:00
|
|
|
* [genericviews Module](http://silverstripe.org/generic-views-module)
|
|
|
|
* [Presentation about ModelAdmin at SupperHappyDevHouse Wellington](http://www.slideshare.net/chillu/modeladmin-in-silverstripe-23)
|
|
|
|
* [Reference: CMS Architecture](../reference/cms-architecture)
|
2012-03-23 22:30:47 +01:00
|
|
|
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|