This module provides two simple form fields to manage has-one and has-many relations for a parent record.
AnyField
and ManyAnyField
are best suited to managing simple DataObjects that are tightly coupled to their owner. This module will not work well for DataObjects with complex relations.
silverstripe/linkfield
is a great complement to the AnyField
.
composer require maxime-rainville/anyfield
This module require Silverstripe CMS 5 or greater.
Screencast.from.10-08-23.21.46.36.webm
- Manage
has_one
orhas_many
relations with ease - Manage multiple Dataobject classes from a single field
- Allow your content authors to edit your child DataObjects from a modal within their parent page
- Works in regular Entwine forms and Elemental blocks.
<?php
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\AnyField\Form\AnyField;
use SilverStripe\AnyField\Form\ManyAnyField;
class Page extends SiteTree
{
private static array $has_one = [
'SingleLink' => Link::class,
];
private static array $has_many = [
'ManyLinks' => Link::class,
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldsToTab(
'Root.Main',
[
AnyField::create('SingleLink'),
ManyAnyField::create('ManyLinks'),
]
)
return $fields;
}
}
Any DataObject can be managed by the AnyField and ManyAnyField without any special tweaks. However, you can get a bit more value with some simple tweaks. Those tweaks can be applied with DataExtension.
The AnyFields displays the selected DataObject title. By defining a getTitle
method for DataObject class, you can customise the title displayed in the field.
The AnyFields displays the type of the selected DataObject below its title. You can also display a summary by implementing a getSummary
method on your DataObject class. This can be done with a DataExtension as well.
You can customise the icon to display for each class of DataObject by defining a private static $icon
config value for your DataObject.
Silverstripe CMS comes with some predefined icons you can use. Alternatively, the icon
value will be add as a CSS class to the relevant button which you can then target with your own custom icon.
# This will add a `font-icon-link` class to the AnyField when managing a Book class.
App\Models\Book:
icon: book
ManyManyField
can be configured to sort its results. Called setSort
to define what field should be used to sort the results
ManyAnyField::create('Pets')->setSort('Sort')
A convenience extension can be applied to your DataObject classes to add the required field to make them sortable. The extension will automatically add a Sort
integer field, define the default sort order and hide the Sort
field from the CMS form.
App\Models\FooBar:
extension:
- SilverStripe\AnyField\Extensions\Sortable
The AnyFields doesn't allow you to publish or delete child DataObjects independently from their owner. To avoid orphan objects or unpublished DataObject, you should explicitly define ownership and cascade rules on the owner DataObject class.
<?php
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\AnyField\Form\AnyField;
use SilverStripe\AnyField\Form\ManyAnyField;
class Page extends SiteTree
{
private static array $has_one = [
'SingleLink' => Link::class,
];
private static array $has_many = [
'ManyLinks' => Link::class,
];
/** Publishing the page will automatically publish those relations */
private static $owns = [
'SingleLink',
'ManyLinks'
];
/** The relations will be deleted when the page is deleted */
private static $cascade_deletes = [
'SingleLink',
'ManyLinks'
];
/** The relations will be duplicated when the page is duplicated */
private static $cascade_duplicates = [
'SingleLink',
'ManyLinks'
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldsToTab(
'Root.Main',
[
AnyField::create('SingleLink'),
ManyAnyField::create('ManyLinks'),
]
)
return $fields;
}
}
AnyField and ManyAnyField try to automatically detect what DataObject classes they are meant to manage by looking up the matching relations on their parent object.
If you decide to name the AnyFields something other than its intended relation, you'll have to explicitly tell it what DataObject class to use.
AnyField::create('MyItem', 'My Item', $this->Item)->setBaseClass(Link::class);
By the default, the AnyField automatically discovers any sub classes of the base class and allows the end user to create those sub classes. You can turn off this behaviour by calling setRecursivelyAddChildClass
.
AnyField::create('MyItem')->setRecursivelyAddChildClass(false);
You can also exclude individual classes as well.
// The filed will allow you to create child classes of Link, but not a plain Link
AnyField::create('MyLink')->setBaseClass(Link::class)->addExcludedClass(Link::class);