In another post we saw how to get started with Doctrine quickly. However nothing beats RedBeanPHP when it comes to quick prototyping. Not only that, RedBeanPHP (from now on: Redbean) is very well suitable for basic applications that don't require more enterprisy features like hydrating to an object graph.

If you are not familiar with Redbean read the front page and the Quick tour. The main reason why this ORM is so great is because it will manage your database schema on-the-fly!

At the end of this post i will show you how to migrate quickly from Redbean to Doctrine, in case you want more advanced features.

Setup

The setup is similar to the project setup outlined in the post about Setting up Doctrine. Read this first to setup the right composer configuration and directory structure.

Composer

Add "gabordemooij/redbean": "v3.5.1" to your require section

Bootstrap

You can either create a new bootstrap file if you just want to use Redbean or merge it together with a Doctrine bootstrap file.

<?php
use RedBean_Facade as R;

require_once './vendor/autoload.php';

R::setup('pgsql:host=localhost;dbname=', 'postgres', '');
R::debug(false);
R::freeze(false);

Usage

Below you can find an example for app.php that just shows you how to create a book table and a chapter table in the database. Obviously book and chapters are related. Redbean will set a foreign key with a constraint for the relation.

<?php
$book = R::dispense('book');
$book->name = 'The Da Vinci Code';
$book->author = 'Dan Brown';

$chapter1 = R::dispense('chapter');
$chapter1->number = 1;
$chapter1->name = 'Pax Romana or Roman Beast?';

$chapter2 = R::dispense('chapter');
$chapter2->number = 2;
$chapter2->name = 'Religious Harmony: The Harlot of Spiritual Deception';

$book->ownChapter = array($chapter1, $chapter2);

R::store($book);

Note that your tables do not have to be in place for this to work, again: Redbean will just create it for you!

Migrating to Doctrine

Creating the model classes

First thing up is creating model classes for doctrine. The Doctrine command line tool can automatically creates classes from your schema. But this tool it's not perfect, so be sure to read up on the doctrine notes on reverse engineering.

First let's create the models from your schema

doctrine orm:convert-mapping --from-database --namespace=Vendor\Entity\ annotation ./src

Tweaking the models

On inspection of the Chapter model we see Doctrine has created a Many-To-One, Unidirectional relation to Book for us. However it would be nice to have a One-To-Many, Bidirectional so we can get the Chapters from a book easily.

Open up de Book model and add:

/**
 * @var \Vendor\Entity\Chapter
 *
 * @ORM\OneToMany(targetEntity="Chapter", mappedBy="book")
 */
private $chapters;

Our models are now ready so let's create the class methods for them for easy OO access.

doctrine orm:generate:entities src

Querying with objects as result

Redbean can make objects (hydrating) for you when just returning single objects (example 1). But it can not make a complete object graph (example 2), to do things like $book->getChapters().

To give a further introduction to Redbean example 1 will show case the Redbean Query Builder called sql function. You can get the SQL it produces with the getQuery() (instead of get()) function. It doesn't matter at all for making objects from the database results.

Example 1: Hydrating with Redbean

In this first example we will use the Redbean's Query Builder and try hydrating single objects into an array.

$rows = R::$f->begin()
  ->select('chapter.id,'
    . 'chapter.number,'
    . 'chapter.name,'
    . 'chapter.book_id')
  ->from('chapter')
  ->where('book_id = ?')->put(1)->get();
$chapters = R::convertToBeans('chapter', $rows);

$chapters will now contain several Chapter objects belonging to book nr 1.

Example 2: Hydrating with Doctrine

Let's try a more difficult query with Redbean.

$sql = 'SELECT '
  . 'b.id AS id, b.name AS name, b.author AS author, c.id AS id0, c.number AS number1, c.name AS name2, c.book_id AS book_id3'
  . ' FROM book AS b JOIN chapter AS c ON b.id = c.book_id';
$rows = R::getAll($sql);

Redbean is unable to convert these results into objects. So let's take a look how we can map the results from query $sql to objects using Doctrine.

use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Query;

$rsm = new ResultSetMappingBuilder($em);
$rsm->addRootEntityFromClassMetadata('Vendor\Entity\Book', 'b');
$rsm->addJoinedEntityFromClassMetadata('Vendor\Entity\Chapter', 'c', 'b', 'chapters', array(
  'id' => 'b.id',
  'name' => 'b.name',
  'author' => 'b.author',
  'id0' => 'c.id',
  'number1' => 'c.number',
  'name2' => 'c.name',
  'book_id3' => 'c.book_id'
));

$stmt = $conn->query($sql);

$books = $em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm);

Wow, that's actually a lot more code then Redbean. Well one way or the other Doctrine has to know how to make the relations and map the columns to the right fields. If you are not using the ORM directly but use native queries, this is the way to do it.

However we can automate generating the select clause, which is useful if you always want to map all the columns. We can do this simply by casting the RSM to a string and inserting it into the query.

$rsm->addJoinedEntityFromClassMetadata('Vendor\Entity\Chapter', 'c', 'b', 'chapters', array(), ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);

$sql = 'SELECT '
  . (string)$rsm
  . ' FROM book AS b JOIN chapter AS c ON b.id = c.book_id';