The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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.