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

NAME

Jifty::Manual::Models - Managing your datastore

DESCRIPTION

The idea behing a model is to give the user a database-independent way of defining how the data looks alike and how different parts of the data relate to each other. In database terms, you might think of a schema definition.

Besides the pure definition of a model, creation, updating and lookup of data is also possible in a comfortable way.

Creating a model

Every model consists of two classes: AppName::Model::ModelName and AppName::Model::ModelName::Schema. Behind the scenes, a class named AppName::Model::ModelNameCollection is created by Jifty::ClassLoader.

A simple model to store just one line of text might look like this:

    use strict;
    use warnings;

    package MyApp::Model::TextLine::Schema;
    use Jifty::DBI::Schema;

    column 'textline';

    package MyApp::Model::TextLine;
    use base qw/MyApp::Record/;

    1;

To create the database schema for a model inside an application you could simply run:

    jifty schema --name TextLine

from inside your application's directory and Jifty will create exactly this class structure for you (minus the column line, to be precise).

Schema definition language

Creating a model has important side effects:

  • correctly type your data inside the data-store

  • let Jifty create (and update) your database schema for you

  • tell Jifty the behaviour in terms of form display

  • allow to work with multiple records (referred to as Collections) without effort

To get all these things done, Jifty allows to describe the schema definition in a simply comprehendable but powerful syntax that looks more like written text than a programming language. The schema definition is made inside the MyApp::Model::XXX::Schema package and for every single column to get created, starts with the word column followed by the column's name.

A simple definition could look like this:

    column name =>
        type is 'text',
        label is 'Name',
        render_as 'Text',
        sincle '0.0.1';

The following BNF shows the full syntax supported (omitting non-terminals that are self explaining for perl-developers):

    schema_definition ::= column_definition+

    column_definition ::= 'column' string_columnname '=>'
                          column_info [ ',' column_info ]+ ';'

    column_info ::= 'type' 'is' string
       | 'label' 'is' string
       | 'render_as' string
       | 'hints' 'is' string
       | 'refers_to' class_name 'by' string_columnname
       | 'default' 'is' string
       | 'literal' 'is' string
       | 'validator' 'is' subroutine_reference
       | 'immutable'
       | 'unreadable'
       | 'length' 'is' number
       | 'mandatory'
       | 'not_null'
       | 'distinct'
       | 'virtual'
       | 'sort_order' 'is' number
       | 'input_filters' 'are' string_classname
       | 'output_filters' 'are' string_classname
       | 'filters' 'are' string_classname
       | 'since' string_version_number
       | 'valid_values' 'are' array_of_valid_values
       | 'hints' 'are' string

    * 'is', 'by', 'on' and 'are' are fill-words that may get omitted.

For a full description of all parameter's meaning, look at Jifty::DBI::Schema.

Versioning

Every time you run the jifty utility with schema as an argument, Jifty will keep track on what it has done for you. To get that done, the version-number being stored in your application's config file etc/config.yml under the key named framework/Database/Verson is matched agains your schema definition.

To force an update of your schema, simple create a new version number in your config file and modify your schema definition by using exactly this version number for every modified entry. After running

    jifty schema --setup

your database structure will be in sync to your schema definition.

Testing a model

After having created a schema, you might use the ADMINISTRATION Menu entry in Jifty's web view to browse through your models and add, edit or delete records in your database.

The classes behind a model

  • MyApp::Model::Xxx

    This is the model-class you created to access records of your desired type. You will directly deal with objects of this class.

  • MyApp::Record

    All records of MyApp::Model::Xxx will have this class as their base class. Usually, this class will be automatically created by Jifty::ClassLoader for you. But, if you want to automatically enable all your records to do something, you will have a chance to do so by manually creating this class.

  • Jifty::Record

    This is the super-class of MyApp::Record. Inside this class, loading of records as well as the checking of user capabilities is done before going one level down to the database layer.

  • Jifty::DBI::Record

    This is the lowest-level class that the database-stack provides. It directly deals with the underlying database.

  • App::Model::XxxCollection

    As the name applies, a collection is a set of typically more than one record. Every collection of this class conists of multiple App::Model::Xxx objects that can get retrieved from your data-store without explicit SQL statements, ordered by any criteria you like and iterated sequentially or accessed at random order.

  • App::Collection

    Every collection of your schemata will have this class as its base. Usually this class is autocreated by Jifty::ClassLoader. If you intend to create new features for all of your collection this will be your chance to do.

  • Jifty::Collection

    This is the base class of an App::Collection, managing user capabilities on records it will keep track of.

  • Jifty::DBI::Collection

    This is the low-level base class that directly manages the access to the underlying database.

Working with single records

Working with single records means work with objects of classes like MyApp::Model::Xxx. The typical creation and usage of a single record is:

    # create an object to allow data access
    my $object = new MyApp::Model::Xxx;

    # either create a representation in the DB
    $object->create(column=>'value', ...);

    # or load the data from DB somehow
    $object->load($id); # by a matching ID
    $object->load_by_cols(column=>'value', other_column=>'secondvalue');

    # try to load and if failed, create a record
    $object->load_or_create(column=>'value');

    # get the record's ID in the database
    # results in 'undef' if record is not valid (usually means not found)
    my $id = $object->id;

    # delete the record from the database
    $object->delete;

To access data stored in different columns of a record you may use some of the automagically created methods on the object:

    # read some column named 'colname'
    my $value = $object->colname;

    # write some value to a column named 'colname'
    $object->set_colname($value);

Especially, when writing to a record, you need not worry about how to write back the data to the database, the object will manage this step on its own.

Working with multiple records

Working with more than one record of the same object-class brings collections into the game. Usually, a collection you deal with is of a type that conforms to your model name, MyApp::Model::XxxCollection and usually holds records of class MyApp::Model::Xxx. You typically use a collection like this:

    # create a collection object
    my $collection = new MyApp::Model::XxxCollection;

    # get all items of the model into the collection
    $collection->unlimit;

    # or restrict items to match some condition
    $collection->limit(column=>'colname', operator=>'=', value=>42);

    # bring the items into some sorting order
    $collection->order_by(column=>'colname');

    # if neccesarry, directly jump to some record from the set
    $collection->goto_first_item;

    $collection->goto_item(42);
    
    # iterate through the result set
    while (my $record = $collection->next) {
          # do something with $record
    }

    # directly access the first or last item
    # be careful: this will set the current position also!
    my $first = $collection->first;
    my $last  = $collection->last;

    # get back an array-ref containing all items
    my $records = $collection->items_array_ref;

see Jifty::DBI::Collection about more ways or ordering and limiting collections.

Action - Model relationship

When writing templates you often simply access some record from a model and want to operate on this very record by modifying it or you might want to add a new record of some type. To do this, our faithful Jifty::ClassLoader will create classes named MyApp::Action::CreateXxx, MyApp::Action::UpdateXxx or MyApp::Action::DeleteXxx for you. This enables you to write a template to operate on a single record like this:

    <%init>
    my $id = some_value_obtained_somehow;
    my $record = new MyApp::Model::Xxx;
    $record->load($id);

    my $action = Jifty->web->new_action(class   => 'UpdateXxx',
                                        moniker => 'mymoniker',
                                        record  => $record);
    </%init>
    ...
    <% $action->form_field('colname') %>
    ...
    <% Jifty->web->link(label  => 'Update',
                        submit => $action,
                        ... ) %>

The elegant thing around here is that you could write the class name of your action-class simply as UpdateXxx instead of the full package name MyApp::Action::UpdateXxx and there is no need to write a repeating update procedure for every record class that comes along. DRY - don't repeat yourself :-)

SEE ALSO

Jifty::Record, Jifty::DBI::Record, Jifty::Collection, Jifty::DBI::Collection, Jifty::Manual::Actions, Jifty::Manual::Tutorial