Inheritance In Behaviors - Creating Function Aliases?

I’ve created a behavior for handling group of properties for a given model (such as abilities or preferences for User), usually represented by a long list of checkboxes.

The way I’ve done this is using single table inheritance, with a database table called ItemProperties, and then a class structure of e.g.




abstract class ItemProperty{


/*Single table inheritance parent class */


function getAvailableProperties();


function getProperty();


function setProperty();


}


class UserPreference extends ItemProperty();


class UserAbility extends ItemProperty();




(I’ve used child classes because they all have a list of different available properties)

Each instance of ItemProperty represents a single property (e.g. a checkbox)

I then use behaviors attached to the Item to handle groups of Properties.

E.g.




class ItemPropertyGroup extends CBehavior

{

   /*the class the property applies to*/

   public $propertyClass;


   function getPropertyList()

   {

       /*gets a property list ready for creating a checkbox*/

   }

}




class PreferencesGroup extends ItemPropertyGroup

{

  

    public $propertyClass = 'UserPreference'

  


    public function getPreferenceList()

    {

          return $this->getPropertyList();

    }  

}





class AbilitiesGroup extends ItemPropertyGroup

{

  

    public $propertyClass = 'UserAbility'

  


    public function getAbilityList()

    {

          return $this->getPropertyList();

    }  

}



and then in my User class (for example), adding




class User extends CActiveRecord

{

  behaviors()

  {

     return array (

           'UserPreferences' => array (

            'class'=> 'ext.PreferencesGroup'),

            'UserAbilities' => array (

           'class'=> 'ext.AbilitiesGroup'),

            )

            );




    );


  }

}



Which works fine, except every time I create a new set of properties, I have to create a new child class of ItemPropertyGroup with alias functions. The reason I did this was because as far as I’m aware, if I just use the same class and attach it several times over to my User model, the getPropertyList() function will just overwrite itself.

Is this correct? Can you recommend a more elegant way to achieve the same outcome?

Hi IainG,

This wiki may help you: Understanding Virtual Attributes and get/set methods.

Please post again if you need clarifications. Good luck!

What you can do is:




// same as $user->getPreferenceList();

$user->asa('UserPreferences')->getPropertyList();


// same as $user->getAbilityList();

$user->asa('UserAbilities')->getPropertyList();



This way, you wouldn’t need aliases. But honestly, I’m not sure if this is any better than the aliases…

@Rodrigo, thanks… what I’m really after are Virtual Functions rather than Virtual attributes.

@Ben… I’d not thought of method chaining. That might be a better way to handle it.

In this case, here are some inspiration for you:

  • PHP Magic Methods

  • _call

Those were what I’m after, but it looks like you can’t use the in Yii because that’s how behaviors work:

http://www.yiiframework.com/doc/api/1.1/CComponent#__call-detail

I’ve refactored my extension to use a single ItemProperty class and a single ItemPropertyGroup behavior.

Instead of using subclasses I’m using a private $_type field, and using a setPropertyType($type) function with method chaining as Ben suggested.

So now I just do $User->setPropertyType($type)->methodName().

The has the advantage that I don’t need to create a new subclass for each category, I can just do all my configuration in a separate config array which I include() in main.config.

Thanks for your help both!