4
votes

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)

2
I suspect GlorpenPropelBundle is your best bet. I have ran into similar problems with not being able to extend Propel models and generally what I have done is to add a new class that takes the base model in the constructor. Then in the query class we just fetch the original models then wrap them and return them.Gavin Love

2 Answers

3
votes

I'm the creator of GlorpenPropelBundle so i thought i could shed some light on issue :)

Propel does provide model classes for modification but sadly when talking about external Bundle's with their own model that classes are generated inside them. There is no second-level user classes.

In some cases you can however make use of Propel single inheritance behavior - http://propelorm.org/documentation/09-inheritance.html or solution provided by hakre .

If you want to simply add some methods to vendor bundle model, you are out of luck. In olden times there was http://trac.symfony-project.org/wiki/HowToExtendPropelPluginModel but now there are some cases it will not work - and that is where my bundle goes into action.

If you are inside own application, you can always do class extending in following way (since Propel user classes are generated only once):

namespace Bookshop\Inventory\Model;
//your custom class extending Propel base class
class Book extends \Core\Inventory\Model\om\BaseProduct { ... }

namespace Core\Inventory\Model;
//propel user class extending your custom class
class Book extends Bookshop\Inventory\Model\Book {...}
0
votes

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.