Development

HowToExtendPropelPluginModel

You must first sign up to be able to contribute.

The problem

Some plugins like sfGuardPlugin were upgraded recently to allow flexible model extension by adding an additional level of class inheritance. Let me explain.

Traditionnaly, model is built like the following:

BaseMyObject     <--inherits-- MyObject
BaseMyObjectPeer <--inherits-- MyObjectPeer

If you want to extend your model, it's usually done easily by editing the child classes, and that's their only purpose.

Now let's imagine you want to do the same in a plugin. You create a schema.yml in plugins/MyPlugin/config/, and define your schema by adding a 'package' parameter to describe where propel generators should write their classes:

package: plugins.MyPlugin.lib.model

This will say "ok dude, send your crap in /plugins/MyPlugin/lib/model/". But the plugin should not be modified by the end user, or you'd loose all its avantages. Update will become impossible, and the D.R.Y principle would be completely laughed at. Still it is possible to create the child classes in your project to extends them, but you'd have either to copy the code or admit plugin has no model operation, so it's about ok 0% of the time. Last case which was proposed on mailing list and applied to sfGuardPlugin for example, was to use a proxy class pair, called pluginMyObject/pluginMyObjectPeer which comes on the scene like this:

BaseMyObject     <--inherits-- pluginMyObject     <--inherits-- MyObject
BaseMyObjectPeer <--inherits-- pluginMyObjectPeer <--inherits-- MyObjectPeer

The leaf classes are simple empty classes that are only present in case you want to extend the model. The two-cents howto is to copy thoose in your own /lib, clear the cache, and start coding. But in some case you'll notice an annoying bug, that will simply bypass your methods.

The solution

The culprit is propel, which defines some autoloading magic in its classes. After some tracking, here are the relevant parts we found in BasesfGuardSecurityUser?:

const CLASS_DEFAULT = 'plugins.sfGuardPlugin.lib.model.sfGuardUser';

.....

$cls = sfGuardUserPeer::getOMClass();
$cls = Propel::import($cls);

.....

public static function getOMClass()
{
  return sfGuardUserPeer::CLASS_DEFAULT;
}

Let's simplify by admitting that Propel::import is the Propel autoloader, replacinc dots by slashes in package and loading the matching file. It will (auto)load the plugin sfGuardUserClass instead of your own.

To override this stupid Propel behaviour (did I say I love Doctrine?), just override the getOMClass method:

class sfGuardUserPeer extends PluginsfGuardUserPeer
{

  public static function getOMClass()
  {
    return 'lib.model.sfGuardPlugin.sfGuardUser';
  }
}

This will be used by Propel import to load your very own class. Hallelujah!