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.
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.
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.
"gabordemooij/redbean": "v3.5.1" to your require section
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);
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
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';