Implementing Custom Fields
- Posted May 1, 2018
- Addon Development
- Essentials
Adding field management into your custom modules is quite easy. In this tutorial we will go over to how incorporate custom field management as well as briefly touch on how to work with custom fields and distinguish them from fields installed by your addon.
{{ img('local://help_center/implementing-custom-fields--fields.jpg')|raw }}
Defining Routes
First let's define the routes used to manage fields. We will be using the routers
built-in to the Streams Platform.
To get started use the FieldRouter
and AssignmentRouter
router to automate defining routes for the fields and assignments management respectively:
FieldRouter::route(Addon $addon, $controller, $prefix = null, $base = '/fields');
AssignmentRouter::route(Addon $addon, $controller, $prefix = null, $base = '/assignments');
The $addon
is the instance of the addon that own's these routes. You can easily get this from your service provider (see below).
The $controller
is your addon's controller for field or assignment management. We will talk about these later but they extend the base FieldsController
and AssignmentsController
classes.
The $prefix
is a path prefix that is helpful if the routes need to be prefixed for some reason. This is useful for example if you are allowing management of assignments for multiple streams. You can prefix each management area and use controllers for either stream this way.
Below is a trimmed down real-world example of the Pages module implementation of these routers:
<?php namespace Anomaly\PagesModule;
use Anomaly\Streams\Platform\Addon\AddonServiceProvider;
use Anomaly\PagesModule\Http\Controller\Admin\AssignmentsController;
use Anomaly\PagesModule\Http\Controller\Admin\FieldsController;
use Anomaly\Streams\Platform\Assignment\AssignmentRouter;
use Anomaly\Streams\Platform\Field\FieldRouter;
class PagesModuleServiceProvider extends AddonServiceProvider
{
/**
* Map additional routes.
*
* @param FieldRouter $fields
* @param AssignmentRouter $assignments
*/
public function map(FieldRouter $fields, AssignmentRouter $assignments)
{
$fields->route($this->addon, FieldsController::class);
$assignments->route($this->addon, AssignmentsController::class, 'admin/pages/types');
}
}
Create Your Controllers
Notice how we specified the controllers for field and assignment management in the routers above. Let's go ahead and make those.
Fields Controller
To create a fields controller simply extend the base Anomaly\Streams\Platform\Http\Controller\FieldsController
controller.
The only property that is required is the $namespace
property which tells the controller which namespace to display fields for. By default only unlocked
fields will be displayed as locked
fields are typically installed by your addon.
<?php namespace Anomaly\PagesModule\Http\Controller\Admin;
class FieldsController extends \Anomaly\Streams\Platform\Http\Controller\FieldsController
{
/**
* The stream namespace.
*
* @var string
*/
protected $namespace = 'pages';
}
Assignments Controller
To create an assignments controller simply extend the base Anomaly\Streams\Platform\Http\Controller\FieldsController
controller.
Again the only property that is required is the $namespace
property. And just like before only assignments for unlocked
fields will be displayed by default.
<?php namespace Anomaly\PagesModule\Http\Controller\Admin;
class AssignmentsController extends \Anomaly\Streams\Platform\Http\Controller\AssignmentsController
{
/**
* The stream namespace.
*
* @var string
*/
protected $namespace = 'pages';
}
Specifying Streams
The assignments router assumes a stream ID or slug will be present when accessing it's routes. For examples: admin/pages/types/assignments/{stream}
Be sure to include the ID or slug of the stream you wish to manage in your button or link to access these controllers.
Below is how we do it in the pages module. This buttons definition is on the TypeTableBuilder
:
$builder->setButtons(
[
'edit',
'assignments' => [
'href' => function (TypeInterface $entry) {
return '/admin/pages/types/assignments/' . $entry->getEntryStreamId();
},
],
]
);
Defining Sections
Now that we have the routes defined and the controllers to handle those routes all we need to do is define the sections in the module class to help access these areas. Generally I only include a section for fields since the assignments falls under another section (like page types for example) anyways. However you could make sections for both if you like.
Below is an example of the pages module's fields section definition:
'fields' => [
'buttons' => [
'new_field' => [
'data-toggle' => 'modal',
'data-target' => '#modal',
'href' => 'admin/pages/fields/choose',
],
],
],
Note that the modal is built into the router and Streams Platform so the rest is completely automated.
Differentiating Fields
Now that you have addon fields and custom fields assigned to a stream you may need to distinguish between the two.
Primarily this is done with the locked
attribute. Addon fields are locked
and custom ones are unlocked
.
Here are a few common methods and objects you will likely need to use to incorporate these into a form for example:
/* @var \Anomaly\Streams\Platform\Stream\Contract\StreamInterface $stream */
$stream->getUnlockedAssignments();
/* @var \Anomaly\Streams\Platform\Field\FieldCollection $fields */
$fields->unlocked();
/* @var \Anomaly\Streams\Platform\Assignment\AssignmentCollection $assignments */
$assignments->unlocked();
Defining a Form Section for Custom Fields
Below is an example of how you might define multiple sections with one being ONLY custom fields. This is the section handler for the UserFormBuilder
for the Users module:
<?php namespace Anomaly\UsersModule\User\Form;
use Anomaly\UsersModule\User\UserModel;
class UserFormSections
{
public function handle(UserFormBuilder $builder, UserModel $users)
{
$fields = [
'first_name',
'last_name',
'display_name',
'username',
'email',
'activated',
'enabled',
'password',
'roles',
];
$assignments = $users->getAssignments();
$profileFields = $assignments->notLocked()->fieldSlugs();
$builder->setSections(
[
'user' => [
'tabs' => [
'account' => [
'title' => 'anomaly.module.users::tab.account',
'fields' => $fields,
],
'profile' => [
'title' => 'anomaly.module.users::tab.profile',
'fields' => $profileFields,
],
],
],
]
);
}
}
{{ img('local://help_center/implementing-custom-fields--assignments.jpg')|raw }}
{{ img('local://help_center/implementing-custom-fields--choose-field.jpg')|raw }}