Release: Pomm 2.0 beta 1

Posted 4 years ago.

We are thrilled to begin the stable release process of Pomm2. This version is almost a complete rewrite of Pomm but still shares the exact same philosophy:

  • Data structure is defined in the place it is the more constrained: the database.
  • Entities are flexible containers and know nothing about databases.
  • There will be no query builders (but there is a condition builder).
  • SQL is first class citizen.

The idea behind Pomm took place in a old -- and symfony1 only compatible -- project: PgLook. This is where the converter system did first appear. Some of the converters still used in Pomm2 were written at that time. When symfony2 went out, a complete rework did create a new project: Pomm. This time, new ideas did emerge and since we did stick to the four commandments above, a method getSelectFields appeared. The concept behind was so powerful, it began slowly to take an important place in the way queries were written.

Pomm2 is a new complete rework of these ideas with the aim of being as simple to use as possible still allowing to perform complex things.

So what's new ?

From the surface, ones acquainted with Pomm 1 will notice changes in the vocabulary. Connection is now known as Session, Database did disappear etc. but the result is so much simpler that it is no problem to get in. It may result a feeling things have changed instead of being new in this release. That was the goal, in fact, the whole engine below has been completely rewritten to be more simple for simple needs and to be able to handle more complex database architecture. This led to the project to be split in several components:

The model manager

The ModelManager is the Object Model Manager as known in Pomm 1. It provides a high level API to manipulate PHP entity objects through SQL queries. Things did change here, former Map classes are now known as Model, multiple inheritance is supported, Projection is now a keystone class of the query system. To be short, yes there is a backward compatibility break and code working with Pomm1 will not work as is with Pomm2. The previous awkward virtual fields system coupled with the field formatter were superseded by the new Projection system. When, using Pomm 1.x, one wanted to add a field with a count(*) in a query and remove one of its field, he had to do something like

    $fields = $this->getSelectFields();
    unset($fields['a_field']);
    $fields['a_count'] = 'count(*)';
    $this->addVirtualField('a_count', 'int4');
$sql = strtr($sql, array( ':fields' => $this->formatFieldsWithAlias($fields), …

Now this is as simple as

    $projection = $this->createProjection()
        ->unsetField('a_field')
        ->setField('a_count', 'count(*)', 'int4')
        ;
$sql = strtr($sql, [ ':fields' => $projection, …

It is even possible to create combinations of existing fields with table alias support: $projection->setField('captain_age', 'age(%birthdate)', 'interval'). Note the % sign, it will be expanded with the table alias if provided.

The Model classes have been completely re-shaped to be the best tool to write model methods dedicated to a relation. A look at a bare model class shows how things did evolve (comments and namespace related lines have been stripped):

<?php
// … 
class CommentModel extends Model { use WriteTrait; // <-- Predefined queries are imported as trait.
protected function __construct() { $this->structure = new CommentStructure; // <- Support multiple inheritance through inherit() method. $this->flexible_entity_class = "\Model\PommProject\PommSchema\Comment"; // <- associated flexible entity. } }

In fact, the only classes which did not change are BaseObject (renamed into FlexibleEntity) and Where, the condition builder.

There is something new with Pomm2 (it has therefore been ported to Pomm 1.3), the ModelLayer. In some cases, application require complex interaction with its model. The layer can be used to isolate several model calls into a transaction, use postgresql event system, transform technical exception into business ones etc. Using it is as simple as creating a class that extends \PommProject\ModelLayer\ModelLayer:

<?php 
class MyModelLayer extends ModelLayer { public function doComplexTreatment($var1, …) { $this->startTransaction(); try { $this->getModel('\MyDb\PublicSchema\MyEntityModel') ->myMethod($varN, …) ; … // do more stuff
$this->commitTransaction(); } catch (PommException $e) { $this->rollbackTransaction(); } } }

With this layer it is possible to use all the Postgresql transactions goodness: savepoints, constraints deferring, isolation levels but also sending notifications. Calling the model layer described above from a controller is pretty straightforward:

<?php
// …
    $pomm['my_db']
        ->getModelLayer('\Cool\Namespace\MyModelLayer')
        ->doComplexTreatment($var1, …)
        ;

Foundation

The Foundation package is what can be called a database framework. It is an extensible API to share a database physical connection between clients. To be short, the Model Manager is a client of Foundation as the Model Layer presented above. Pomm 1 users will be happy to see every aspect of using Pomm is simpler:

<?php
$loader = require DIR.'/vendor/autoload.php';
$pomm = new \PommProject\Foundation\Pomm(['my_db' => ['dsn' => 'pgsql://user:pass@host:port/db_name']]);

$iterator = $pomm['my_db'] ->getModel('\MyDb\PublicSchema\MyEntityModel') ->findWhere('a_field = $ and another_field >= $', [$val1, $val2]);

The Pomm class is a real service usable with dependency injection containers and spawning sessions from it is as simple as calling an ArrayAccess. The getModel() method is caught by __call to call the asked model client through a Pool manager named Pooler. There is a query pooler to perform queries, a converter client to converter data back and from the database, a notifier pooler to catch NOTIFY events in the database. Each client can use each other with a full access to the Session.

If you just need an interface to perform simple queries and get an iterator on arrays of properly converted values back, just do

<?php
$loader = require DIR.'/vendor/autoload.php';
$pomm = new \PommProject\Foundation\Pomm(['my_db' => ['dsn' => 'pgsql://user:pass@host:port/db_name']]); 
$iterator = $pomm['my_db'] ->getQueryManager() ->query('select * from my_table where a_field = $*', [$_GET['a_var']]) ;
echo json_encode($iterator->extract());

Foundation will most of the time be used indirectly by developers through the model manager but there could be cases the model manager would not be adapted. By example, if an existing database API uses store procedures to call every aspect of the model layer. In that case, it is possible to develop a dedicated PHP layer on top of Foundation to use efficiently this legacy code.

CLI

Yes, there is a CLI coming in its own package. For now, there are two kind of tasks: database inspection and model classes generation. It unsurprisingly uses Symfony's console component and offers what was the missing feature of Pomm 1.x.

$ php vendor/bin/pomm.php inspect:database MyProject
Found 2 schemas in database.
+-----------+--------+-----------+-------------------------+
| name      | oid    | relations | comment                 |
+-----------+--------+-----------+-------------------------+
| elcaro    | 123773 | 2         | ElCaro tutorial schema. |
| public    | 2200   | 14        | standard public schema  |
+-----------+--------+-----------+-------------------------+

The inspectors can give informations about schemas, tables and field. It also gives a real importance to database comments. Because weeks after the creation of the database structure one can ask himself «why did we do this that way ?», comments are the way database auto documents itself hence they must be visible. Table and fields comments are also written in generated class PHPDoc.

The model class generator is featured with the CLI:

$ php vendor/bin/pomm.php generate:relation-all MyProject factory
 ✓  Creating file './Myproject/PublicSchema/AutoStructure/Factory.php'.
 ✓  Creating file './Myproject/PublicSchema/FactoryModel.php'.
 ✓  Creating file './Myproject/PublicSchema/Factory.php'.

And now ?

Pomm 2 is still in beta but did prove to be really stable. Thanks to Scrunitinizer we could really did code followed by a very picky compiler for PHP. It did force us to stay between the lines to keep the code clean and maintainable. Foundation alone is 3k lines of PHP code (according to cloc) and we were able to keep the quality score over 9.6. Pomm2 also aims at being the PHP fastest database access layer. We started using Sensiolabs Blackfire during last symfonyCon in Madrid and we will carefully bench Pomm2 on every aspect we can.

Pomm2 is still missing a Symfony2 bundle, it lacks a comprehensive documentation, there is a Faker extension in the oven… Now this is the time to let you test Pomm2, bootstrap projects with it. Most of us, Pomm developers, did switch our personal projects to Pomm2 with ease (and pleasure). Send us feedback would help us to make the best database access layer for PHP.