Active Record-like layer for RedBean

The ActiveBean provides functionnality similar to an Active Record implementation, using RedBean as the backend. RedBean upgrades your schema on-the-fly while you code, so you never have to manually alter or create your tables.

ActiveBean is part of the Shozu library/framework but can be used alone with Redbean.

Basic usage

To use ActiveBean, I will assume that you have a Redbean installed and "kickstarted".

Defining your model is as easy as extending the base class and then adding a simple method to it:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title');
        $this->addColumn('content');
    }
}

Then to create and save a new Post:

<?php
$post = new Post;
$post->title = 'an interesting post';
$post->content = 'some blah';
$post->save();

You may use this alternative syntax:

<?php
$post = new Post;
$post->setTitle('an interesting post');
$post->setContent('some blah');
$post->save();

Each record has an id. If you know the id, you can retrieve your record by passing it to the constructor:

<?php
$post = new Post(1);

If you are familiar with RedBean, you can achieve the same thing by passing a bean to the constructor:

<?php
$post = new Post($post_bean);

You might as well pass an array to the constructor:

<?php
$post = new Post(array('title' => 'an interesting post', 'content' => 'some blah'));

Validate and format data

Using ActiveBean you can enforce data validation by adding validators and formatters to the table definition:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title', array(
            'formatters' => array('trim'),
            'validators' => array('notblank')
        ));
        $this->addColumn('content', array(
            'formatters' => array('trim')
        ));
    }
}

Formatters and validators are simple protected methods. Validators are executed before save() and return a boolean value. Formatters are executed upon __set() and return the modified parameter. ActiveBean comes with standard validators (notblank, email, etc) and formatters (uppercase, lowercase, trim, nodiacritics, etc) and you can easily add your own:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title', array(
            'formatters' => array('trim', 'ucfirst'),
            'validators' => array('notblank','isscalar')
        ));
        $this->addColumn('content', array(
            'formatters' => array('trim')
        ));
    }
    protected function ucfirstFormatter($in)
    {
        return ucfirst($in);
    }
    protected function isscalarValidator($in)
    {
        return is_scalar($in);
    }
}

You can also use callbacks as validators and formatters:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title', array(
            'formatters' => array('trim', array('myNeatFormattersLib','someStaticMethod')),
            'validators' => array('notblank',array('myNeatValidatorsLib','someOtherStaticMethod'))
        ));
        $this->addColumn('content', array(
            'formatters' => array('trim')
        ));
    }
}

Invalid records will throw an exception on save().

Column typing

It is possible to enforce a type for a column. You don't have to limit yourself to scalar values: objects and arrays are automatically serialized/unserialized for you. Allowed types include string, boolean, array, datetime (automatically converts to a DateTime object), etc:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title');
        $this->addColumn('content');
        $this->addColumn('published', array('type' => 'boolean'));
        $this->addColumn('published_at', array('type' => 'time'));
    }
}

More options

You can specify unique indices for columns:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title');
        $this->addColumn('content');
        $this->addColumn('slug', array('unique' => true));
    }
}

If you set a length to your column and add the "limiter" formatter, your values will be truncated. With no limiter, an Exception is thrown if you try to assign a value that is larger:

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        // will be truncated
        $this->addColumn('title', array(
            'length' => 64,
            'formatters' => array('trim', 'limiter')
        ));
        // will throw an exception if you set text that is more than 140 cars.
        $this->addColumn('content', array(
           'length' => 140
        ));
        $this->addColumn('slug', array('unique' => true));
    }
}

You can also automatically add columns to stamp the record on creation and modification. Their values are set on save():

<?php
class Post extends \shozu\ActiveBean
{
    protected function setTableDefinition()
    {
        $this->addColumn('title');
        $this->addColumn('content');
        $this->addColumn('slug', array('unique' => true));
        $this->isStampable = true; // adds modified_at and created_at cols.
    }
}

Finding records

Finding records works as using RedBean's finder plugin. Just use SQL:

<?php
$posts = Post::find('title like ?', array('%test%'));

You can limit the search to one record:

<?php
$post = Post::findOne('title like ?', array('%test%'));

Relations

You can link records using the.... link() method:

<?php
$post->link($comment);
$post->save();

And unlink records using the.... unlink() method:

<?php
$post->unlink($comment);
$post->save();

You may unlink all comments of the post:

<?php
$post->unlink('Comment');
$post->save();

Please note the operation is not effective until you save the record.

Finding related records is pretty straight-forward too:

<?php
$commentsIds = $post->getRelated('Comment');

You can return only comments ids for saving memory. You can do so by setting the $hydrate param to false:

<?php
$comments = $post->getRelated('Comment', false);