NAME
Strehler::Manual::ExtraEntityTutorial - How to add custom objects to a Strehler backend
RISE OF THE ROBOTS
So you have installed Strehler, but you need more power, you need a CRUD for something more specific than articles and images, but you want it in Strehler flavour. Usually this kind of tutorial uses books or CDs as example, but those are boring things. We'll create a collection of bad-ass giant robots.
DATABASE
Our data will be store in the database, so we'll start with it. We'll trace these data for the robots:
These are the robot characteristics that we'll trace:
name
pilot
strength
speed
defence
We'll trace also robots' descriptions, but we'll talk about them in a second moment as they'll be multilanguage field .
Considering this data structure you have to create database table for it by yourself, Strehler doesn't give you tools to manage custom database tables.
SQL script should be
CREATE TABLE ROBOTS (
ID INTEGER PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
PILOT VARCHAR(50),
STRENGHT INTEGER,
SPEED INTEGER,
DEFENCE INTEGER,
CATEGORY INTEGER,
PUBLISHED TINYINT(1)
);
Table fields are more than data elements from previous list. ID si mandatory in every Strehler entity table as primary key for it. We also decided to give this object two Strehler out-of-the-box featuers: published status and category. For it is enough to add a CATEGORY column and a PUBLISHED one (INTEGER and TINYINT respectivly) to your table.
For description, the multilangua field, we need a second table. Here's its script:
CREATE TABLE ROBOTS_MULTI(
ID INTEGER PRIMARY KEY AUTO_INCREMENT,
ROBOT INTEGER,
DESCRIPTION VARCHAR(120),
LANGUAGE VARCHAR(2)
);
An ID as for the main table, a column for the foreign key on ROBOTS table (ROBOT) and a LANGUAGE VARCHAR(2) field. These elements are mandatory for a multilanguage Strehler table.
Our last SQL activity will create indexes needed by the system:
ALTER TABLE `ROBOTS_MULTI` ENGINE = InnoDB;
CREATE INDEX robot ON ROBOTS_MULTI (robot);
ALTER TABLE `ROBOTS_MULTI`
ADD CONSTRAINT robot FOREIGN KEY ( `ROBOT` )
REFERENCES `ROBOTS` (`ID` );
ALTER TABLE `ROBOTS` ENGINE = InnoDB;
CREATE INDEX category_for_robot ON ROBOTS (CATEGORY);
ALTER TABLE `ROBOTS`
ADD CONSTRAINT category_of_robot FOREIGN KEY ( `CATEGORY` )
REFERENCES `CATEGORIES` (`ID` );
Now you can just dump all these new tables with the schema loader to make DBIx::Class manage them.
WARNING: If you don't want to create indexes because, for example, you are working with SQLite this is not a problem, but remember to write down the relationships in your DBIx::Classes in any case, Strehler will use them!
Forms
Strehler does no introspection to elaborate the form to create/edit robots so we have to write forms by ourself. It's just HTML::FormFu syntax. You can do everything you want, just remember to keep fields names the same of the database fields names. For Strehler standard features use custom FormFu::Elements. In this example we'll use Strehler::Formfu::Element::Category and Strehler::Formfu::Element::Tags.
attributes:
class: well
elements:
- type: "+Strehler::Formfu::Element::Category"
- type: "+Strehler::Formfu::Element::Tags"
- name: name
label: Name
attributes:
class: span8
constraints:
type: Required
message: 'Name required<br />'
- name: pilot
label: Pilot
attributes:
class: span8
- name: strenght
label: Strength
attributes:
class: span8
- name: speed
label: Speed
attributes:
class: span8
- name: defence
label: Defence
attributes:
class: span8
- name: save
type: Block
tag: button
content: Submit
attributes:
type: submit
class: btn btn-primary
CSS classes are assigned according to Strehler layout.
Multilang fields need a file by themselves
elements:
- name: description
label: Description
Class
Now you need a class that manage robots. Basic implementation of it is just an extension of Element with metaclass_data overridden. Class namespace is not important at all.
package Site::Element::Robot;
use Moo;
use Dancer2;
extends 'Strehler::Element';
sub metaclass_data
{
my $self = shift;
my $param = shift;
my %element_conf = ( item_type => 'robot',
ORMObj => 'Robot',
category_accessor => 'robots',
multilang_children => 'robots_multis' );
return $element_conf{$param};
}
METACLASS DATA
metaclass_data just gives back basic informations about the entity to all the Strehler system. These informations are:
- item_type
-
Label that will be used to identify your entity. It can be any string you want, it will be used also to build admin URLs for the entity (/admin/$item_type/list etc...)
- ORMObj
-
Name of the module in DBIx::Class structure that manage the main table of the entity.
- category_accessor
-
The relationship name in Category module. You can read it from Your::Module::Result::Category as here:
__PACKAGE__->has_many( "robots", "Your::Module::Result::Robot", { "foreign.category" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, );
- multilang_children
-
The relationship name to the multilang fields. You can read it from Your::Module::Result::Robot module, as here:
__PACKAGE__->has_many( "robots_multis", "Your::Module::Result::RobotsMulti", { "foreign.robot" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, );
CONFIGURATION FLAGS
You can also use this class to tell Strehler the label that will be used in the menu and that category feature and publish feature are active for the entity, just adding relative methods returning true.
sub label { return "Robots"; }
sub categorized { return 1; }
sub publishable { return 1; }
Same for forms you created:
sub form { return "forms/robots.yml" }
sub multilang_form { return "forms/robots_multi.yml" }
This configuration can be done through config.yml too, but this could make config file very big, with a lot of entries and difficult to be read. This is probably a better solution.
HOOKS
Extending Strehler::Element to create new entities allow you to override every method of that class to fit your needs. In particular, you'll probably work a lot with the get_form_data and the save_form methods, if the logic of your data is more complicated than expected by Strehler.
There're also two hooks to make life easier.
Think about a situation where you have an Entity Site::Element::Foo with an attribute bar. If you want it back from the get_basic_data or get_ext_data in a shape different by what you have in your database, just define a sub bar in the class returning it the way you want it. A good example of this is the image field in the Strehler::Element::Article. Sub bar will receive as arguments the value of the attribute (the one returned with no hook) and the language, if the attribute is multilanguage.
If bar is a field elaborated from the form but not contained in the form, you can implement the logic to save it creating in your class a sub save_bar that returns what you want to save. Again, look at Strehler::Element::Article, save_slug method. In this case the hook receives the id of the object and the form object as input.
Hooks will not be used in this tutorial, but you can see how they work in the Strehler::Manual::MarkdownArticleTutorial.
STRELHER CONFIGURATION
New entity must be registered in Strehler configuration, in the Dancer2 config.yml file. As we configured most of the characteristics in the class itself, we'll keep this configuration as tiny as possible.
Strehler:
language: ['it', 'en']
default_language: 'it'
extra_menu:
robot:
auto: 1
class: Site::Element::Robot
SEE ALSO
Strehler::Manual::ExtraEntityConfiguration enumerates and describes all the configuration flags not considered in this tutorial.