PHP
Article
By Lukas White

Introduction to Redbean

By Lukas White

An Introduction to Redbean

When it comes to RAD and prototyping, there are lots of tools available to help get projects up-and-running quickly. From quick-starts to micro-frameworks, from build tools such as Composer to one-click installers, and of course ORMs – there are all sorts of ways to speed up development time. I'm going to look at Redbean, an ORM with a difference, which is ideal for prototyping.

Enter Redbean

Redbean is an ORM (Object Relational Mapper), but it does more than that – it creates and modifies the underlying schema on-the-fly. Traditionally, you would use an ORM as a wrapper to a pre-planned, pre-existing database schema. That's fine for most projects, but when you're prototyping – or making it up as you go along! – having it done for you behind the scenes can speed things up even more.

When you create an object with Redbean – or bean, to use the specific terminology – as soon as you save it to the database, the schema adapts itself to fit. This applies even if you're trying to save an object without a corresponding table!

Installation

The easiest way to install Redbean is to download the all-in-one package. Then, you simply need to require the single file, rb.php.

You can also install it via Composer; however, the author of the library does not recommend this approach – see the installation page for details .

Dispensing

The first step when using Redbean is to “dispense” a bean – which is basically an object. Note that I'll be using the term “bean” and object interchangeably throughout the rest of the post.

Dispensing is done via a static method of the overarching Redbean class, “R”, which takes the type as an argument.

For example:

$user = R::dispense('user');

This will create an empty object for a user, to which you can then assign properties. You can also dispense several at the same time by adding the number you want as a second parameter:

$users = R::dispense('user', 10);

It doesn't matter at this stage whether there's a table in the database to hold it; as soon as we try and store it, Redbean will take care of that.

Let's try this – note the use of the static method store, which is used to write to the database:

$user->name = 'Joe Bloggs';
$user->email = 'joe.bloggs@example.com';
$id = R::store($user);
print $id;

In most ORM's, you'd expect this to fail if you haven't yet created a table to hold users. However, using Redbean this succeeds because it creates the table for you. If you take a look in your database at this stage, you should see a table like this:

user
----

id      int(11) UN PK AI
name    varchar(255)
email   varchar(255)

By necessity, the naming convention is pretty strict; it's a singular, lower-case representation of the type you specified.

Now let's look at how the schema changes as we iterate. A user record will likely need a password field – but we forgot to create one.

If you do this:

$user->password = 'secret';
R::store($user);

This time, Redbean knows how to store a user but there's a new field that the database table can't accommodate. No matter – it simply adds one, and your table will now look like this:

user
----

id          int(11) UN PK AI
name        varchar(255)
email       varchar(255)
password    varchar(255)

Field Data Types

Redbean tries to guess the data type of a field based on the information you provide. So if you did this:

$user->age = 30;
R::store($user);

You'll find that the age field has been created as a tiny integer.

If a field type doesn't prove sufficient later, Redbean simply alters it on-the-fly. If you tried to assign 3000 to the age now, the column would be changed to a normal integer. If you spelt out the name as “thirty”, it would be changed to a varchar. Set a varchar field to something greater than 255 characters and it becomes a TEXT field, and so on.

Finding Beans

You can load a specific object by primary key using load:

$user = R::load('user', 1);

You can load several objects at a time (by primary key) by using the batch method:

$users = R::batch('user', array(1, 2, 3));

This returns an array of beans.

You can also find beans using SQL. The second argument to the find method is essentially SQL from the WHERE clause onwards, excluding the WHERE keyword itself. For example, to find users who aren't yet twenty:

$users = R::find('user', 'age < ?',
    array(20)
);

Note that we're binding the parameters, hence the array as the third argument. This returns an array of beans using their ID's as keys.

You can add more clauses to your SQL, for example:

$users = R::find('user', 'age < ? ORDER BY age ASC',
    array(20)
);
--ADVERTISEMENT--

Counting

You can find out the number of records using the count method:

$number_of_users = R::count('user');

Deleting

To remove a single bean, use trash:

R::trash($user);

To remove more of a particular type, use trashAll:

R::trashAll('user');

To delete all beans of a particular type, use wipe:

R::wipe('user');

Or, to wipe out everything – which can be useful while prototyping – you nuke it:

R::nuke();

Relations

As with any ORM, relationships between objects are supported.

One-to-many relations are referred to using the concept of “owning” related objects. For example if orders have a single user, that user is said to “own” those orders. By using a specific variable name, we can build that relationship like this:

$orders = R::dispense('order', 2);
$orders[0]->order_id = '000001';
R::store($orders[0]);
$orders[1]->order_id = '000002';
R::store($orders[0]);

$user->ownOrders = $orders;
R::store($user);

The key element here is the property ownOrders. If you now inspect your database, you should find that Redbean has added the field user_id to the order table, along with a corresponding foreign key.

The user that “owns” the order can simply be accessed as a property, e.g.:

$user = $order->user;
print 'This order belongs to ' . $user->name;

In order to demonstrate many-to-many relationships, let's create some roles:

$roles = R::dispense('role', 3);

$roles[0]->name = 'member';  
R::store($roles[0]);
$roles[1]->name = 'admin';  
R::store($roles[1]);
$roles[2]->name = 'super_admin';
R::store($roles[2]);

Roles don't just belong to individual users; they are shared. So to assign the first two roles to a user, and in doing so build the relationship in the database, we can do this:

$roles = R::batch('user', array(1,2));
$user->sharedRole = $roles;
R::store($user);

This time, you should find a new table called role_user, which defines this relationship as a many-to-many.

You can get the roles belonging to a user by reading the property:

$roles = $user->sharedRole;

This lazy-loads the roles the first time they're accessed.

There's a lot more to relationships, including the ability to add properties to the relation using link, filtering by link, eager loading, and so on – check out the documentation for details.

Models

You can create models to correspond to bean types simply by following naming certain conventions. Models are then connected to beans using FUSE; as in, by following the relevant naming convention they are FUSEd together.

The convention is simple; separate “Model” and the type with an underscore, e.g. Model_Type. So for example, to create a model for users, you simply do this:

class Model_User extends RedBean_SimpleModel { }

Once you've defined a model, there are a number of methods you can implement which will get called at different points in the bean's lifecycle. This is illustrated in the table below, which maps the CRUD operations to the “hooks” you can implement:

R::store        $model->update()
R::store        $model->after_update()
R::load            $model->open()
R::trash        $model->delete()
R::trash        $model->after_delete()
R::dispense        $model->dispense()

So, for example, you could add validation by implementing update():

class Model_User extends RedBean_SimpleModel { 
  public function update() {
    if (strlen($this->password) < 8) {
      throw new Exception('Password must be at least 8 characters long');
    }
  }
}

You can also create your own methods, of course, as well as custom GETters.

Querying the Database

You can execute a raw SQL query like this:

R::exec('UPDATE user SET status = 1');

You can return a multidimensional array of rows like this:

R::getAll('SELECT * FROM user');

Additionally, you can use parameter binding:

R::getAll('SELECT * FROM user WHERE status = :status',
    array(':status' => 1)
);

You can fetch a single column like this:

R::getCol('SELECT email FROM user');

You can also get an associative array using two columns of a table like so:

R::$adapter->getAssoc('SELECT id, email FROM user');

Deploying your Application

Although it's arguably best suited for prototyping – or at least development – there's no reason you can't continue your use of Redbean into production, provided you follow certain steps.

Redbean's mechanism for updating the schema on-the-fly is called “fluid mode”; however this isn't appropriate for production, and has a pretty significant performance overhead. However, you can turn it off by “freezing” it:

R::freeze( true );

In going into production, you're going to want to follow a few more steps:

  • review the column data types
  • review the foreign keys and relations
  • check the indexes – Redbean creates a few – and modify appropriately

Summary

In this article, I've introduced Redbean as a handy way to kick-start your development process, by allowing you to play around with your domain models without worrying about building the underlying schema first. Whether it's viable for production remains to be seen – but for prototyping and rapid iterations, it's a great little tool. There's much I haven't covered, so be sure to check out the documentation.

Recommended
Sponsors
The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account