Search code examples
phpsymfonyarchitecturedoctrine-ormpropel

Strategies for cleanly extending Propel Models in Symfony2?


I want to do this:

// Model class
namespace Bookshop\Inventory\Model;
use Core\Inventory\Model\Product as BaseProduct;

class Book extends BaseProduct { 
    // ... 
}


// Query class
namespace Bookshop\Inventory\Model;
use Core\Inventory\Model\ProductQuery as BaseProductQuery;

class BookQuery extends BaseProductQuery { 
    // ... 
}

Looks fine, right? But:

$book = BookQuery::create()->find($id);
var_dump(get_class($book));
// expected: Bookshop\Inventory\Model\Book
// actual:   Core\Inventory\Model\Product

AFAIK this is due to the fact that Propel's relationships are defined at build-time, not runtime... The only way I have found of achieving this is by using the extend behaviour found in the GlorpenPropelBundle and defining the extended classes in my config:

glorpen_propel:
    extended_models:
        Core\Inventory\Model\Product: Bookshop\Inventory\Model\Book

Fine, it works, but surely there's a better way? Have I missed something, or is this really the only way to extend Models in Propel + Symfony? I really want to use Propel over Doctrine, but things like this leave me thinking that Propel simply isn't suited for projects over a certain size...

(Propel 1.6 + Symfony 2.3 btw)


Solution

  • The issue you have and ask about is as you already wrote about built-time / runtime. As BookQuery::create() is a static method it resolves the model classname to BaseProduct.

    This is as well outlined in the Propel manual:

    Due to late static binding issues in PHP 5.2, you cannot use the create() factory on an inherited query - unless you override it yourself in the descendant class. Alternatively, Propel offers a global query factory named PropelQuery:

    <?php
    // Use 'frontendBook' instead of 'Book' in the frontend to retrieve only 
    // published articles
    $books = PropelQuery::from('frontendBook')->find();
    

    Source: Propel Query ReferenceDOCS - scroll down to the very end.

    So either override it or specify it.

    I hope this solves your issue as it allows you to specify which query and therefore entity class to be used by propel.

    The Glorpen Propel Bundle takes a different route by the way, changing the code that is being executed for the static getOMClass method. This smells like a hack in my nose, but it's too early that I judge. You find the code in Behaviors/ExtendBehavior.php.