---
# this is an empty front matter
---
{% include header.html %}
<h2>Object Relational Mapping</h2>

<p>
 ClearPress comes with a built-in, basic ORM layer in the
 ClearPress::model superclass. Like views, data model objects should
 be systematically named to avoid adding complexity (though it is
 possible to override everything if you really want to!). Usually a
 class maps to a table and an instance of a class (yes, an object)
 usually maps to an individual row. Tables are usually named in the
 singular form (e.g. 'child' rather than 'children') and the class
 name-suffix has the same name as the table,
 e.g. application::model::child.
</p>

<h3>Table fields / data members</h3>
<p>
 Data model objects at the bare minimum carry the following
 information - the package name and an array of fields which map to
 the database table the class represents.
</p>

<p>
 Fields can be reflected in automatic get/set accessors courtesy of
 Class::Accessor. Most data models come with this near their head:
 {% highlight perl %}__PACKAGE__->mk_accessors(fields()){% endhighlight %} which uses the array
 from sub fields() { } to construct the accessors.
</p>

<p>
 The ClearPress ORM does not (currently) perform database inspection
 to build attributes and relationships automatically (at run- or
 compile- time). It relies on the list of fields always reflecting
 what's in the database. For example:

 {% highlight perl %}sub fields {
 return qw(id_child name dob id_family);
}{% endhighlight %}
</p>

<h3>Creating stuff</h3>
<p>
 Here's our data model:
 {% highlight perl %}package Grandkids::model::kid;
use strict;
use warnings;
use base qw(ClearPress::model);

__PACKAGE__->mk_accessors(fields());

sub fields {
 return qw(id_kid name dob id_family);
}

1;
{% endhighlight %}

 which represents a database table:
{% highlight sql %}create table kid(
 id_kid bigint unsigned primary key auto_increment not null,
 name char(128) default '' not null,
 dob datetime not null,
 id_family bigint unsigned
);{% endhighlight %}

 and here's how we could use it to add a new entry in the database:
{% highlight perl %}use Grandkids::model::kid;
my $oKid = Grandkids::model::kid->new();
$oKid->name('Bob');
$oKid->id_family(1);
$oKid->dob('2009-02-10 10:00:00');

$oKid->create or die;{% endhighlight %}

 Using accessors to set all the values is unwieldy so we can also do it during construction:
{% highlight perl %}my $oKid = Grandkids::model::kid->new({
 name      => 'Bob',
 id_family => 1,
 dob       => '2009-02-10 10:00:00',
});

$oKid->create or die;{% endhighlight %}

 Notice that we didn't add an id_kid to either of these, because we
 want our database to allocate them for us. id_kid is the first field
 in <i>sub fields</i> and as such it's recognised by ClearPress::model
 as the primary key for this class. If we did add an id_kid, the new
 entry would be created with that id unless it already exists, in
 which case an error would be raised.
</p>

<h3>Reading stuff</h3>
<p>
 Read access from the database comes in a couple of flavours - lists
 and individuals. The latter (read) requires an entity's id. Listings
 are fractionally simpler as the basics are implemented in the
 superclass.
</p>

<h4>List</h4>
<p>
 To activate the basic list method (list everything) we add this to
 the top of the class {% highlight perl %}__PACKAGE__->has_all(){% endhighlight %} which adds a
 pluralised accessor (in this case <i>kids</i>) returning an array
 reference of all kid objects from the database. This can now be used
 like so:

 {% highlight perl %} my $oRootKid = Grandkids::model::kid->new(); # note no id required
 my $arKids   = $oRootKid->kids();
 for my $oKid (@{$arKids}) {
  printf q[%d %s %s %d\n],
         $oKid->id_kid,
         $oKid->name,
         $oKid->dob,
         $oKid->id_family;
 }{% endhighlight %}
</p>

<h4>Read</h4>
<p>
 To read a specific entity given its id (primary key) we can specify
 it in the constructor:

 {% highlight perl %}my $oKid = Grandkids::model::kid->new({id_kid=>1});
 printf q[%d %s %s %d\n],
        $oKid->id_kid,
        $oKid->name,
        $oKid->dob,
        $oKid->id_family;{% endhighlight %}
</p>

<h3>Updating stuff</h3>
<p>
 To modify an existing entity we load it with its primary key, add the
 value to change then ask it to update:

 {% highlight perl %}my $oKid = Grandkids::model::kid->new({id_kid=>1});
$oKid->name('Billy');
$oKid->update or die;{% endhighlight %}

 If the row in the database is heavy (e.g. contains a large text field
 or blob) you don't need to worry. ClearPress::model tries to be lazy
 by default - as we don't ask it for anything we didn't give it, it
 won't try to fetch the row from the database. It will just save the
 values it has.
</p>

<h3>Deleting stuff</h3>
<p>
 Deleting entries from the database is easy. Read with an entity's
 primary key, then call delete:

 {% highlight perl %}my $oKid = Grandkids::model::kid->new({id_kid=>1});
$oKid->delete or die;{% endhighlight %}
</p>

<h3>Secondary Keys</h3>
<p>
  ClearPress::model supports the use of a unique secondary key (not compound). If, say, a user has a unique username, the primary id would be id_user or similar, and the secondary key would be username. ClearPress::model::init will check for a non-numeric scalar value in the constructor, and attempt to load on that when the primary key is not present. Similarly it will also attempt to load against a named field.
</p>
<p>
  {% highlight perl %}
  package app::model::user;
  use strict;
  use warnings;
  __PACKAGE__->mk_accessors(fields());
  sub fields {
    return qw(id_user username);
  }

  sub secondary_key {
    return q[username];
  }
  1;
  {% endhighlight %}

  {% highlight sql %}
  INSERT INTO user VALUES(1,'bob123');
  {% endhighlight %}

  {% highlight perl %}
  use app::model::user;
  my $u = app::model::user->new(1);
  my $u = app::model::user->new('bob123');
  my $u = app::model::user->new({ id_user => 1 });
  my $u = app::model::user->new({ username => 'bob123' });
  {% endhighlight %}

  The named-field method is particularly useful when you wish to load based on a numeric secondary key.
</p>
{% include footer.html %}
