The definitive guide of Symfony 1.2

8.6. Extending the Model

The generated model methods are great but often not sufficient. As soon as you implement your own business logic, you need to extend it, either by adding new methods or by overriding existing ones.

8.6.1. Adding New Methods

You can add new methods to the empty model classes generated in the lib/model/ directory. Use $this to call methods of the current object, and use self:: to call static methods of the current class. Remember that the custom classes inherit methods from the Base classes located in the lib/model/om/ directory.

For instance, for the Article object generated based on Listing 8-3, you can add a magic __toString() method so that echoing an object of class Article displays its title, as shown in Listing 8-20.

Listing 8-20 - Customizing the Model, in lib/model/Article.php

class Article extends BaseArticle
{
  public function __toString()
  {
    return $this->getTitle();  // getTitle() is inherited from BaseArticle
  }
}

You can also extend the peer classes — for instance, to add a method to retrieve all articles ordered by creation date, as shown in Listing 8-21.

Listing 8-21 - Customizing the Model, in lib/model/ArticlePeer.php

class ArticlePeer extends BaseArticlePeer
{
  public static function getAllOrderedByDate()
  {
    $c = new Criteria();
    $c->addAscendingOrderByColumn(self::CREATED_AT);
    return self::doSelect($c);

  }
}

The new methods are available in the same way as the generated ones, as shown in Listing 8-22.

Listing 8-22 - Using Custom Model Methods Is Like Using the Generated Methods

foreach (ArticlePeer::getAllOrderedByDate() as $article)
{
  echo $article;      // Will call the magic __toString() method
}

8.6.2. Overriding Existing Methods

If some of the generated methods in the Base classes don't fit your requirements, you can still override them in the custom classes. Just make sure that you use the same method signature (that is, the same number of arguments).

For instance, the $article->getComments() method returns an array of Comment objects, in no particular order. If you want to have the results ordered by creation date, with the latest comment coming first, then override the getComments() method, as shown in Listing 8-23. Be aware that the original getComments() method (found in lib/model/om/BaseArticle.php) expects a criteria value and a connection value as parameters, so your function must do the same.

Listing 8-23 - Overriding Existing Model Methods, in lib/model/Article.php

public function getComments($criteria = null, $con = null)
{
  if (is_null($criteria))
  {
    $criteria = new Criteria();
  }
  else
  {
    // Objects are passed by reference in PHP5, so to avoid modifying the original, you must clone it
    $criteria = clone $criteria;
  }
  $criteria->addDescendingOrderByColumn(CommentPeer::CREATED_AT);

  return parent::getComments($criteria, $con);
}

The custom method eventually calls the one of the parent Base class, and that's good practice. However, you can completely bypass it and return the result you want.

8.6.3. Using Model Behaviors

Some model modifications are generic and can be reused. For instance, methods to make a model object sortable and an optimistic lock to prevent conflicts between concurrent object saving are generic extensions that can be added to many classes.

Symfony packages these extensions into behaviors. Behaviors are external classes that provide additional methods to model classes. The model classes already contain hooks, and symfony knows how to extend them by way of sfMixer (see Chapter 17 for details).

To enable behaviors in your model classes, you must modify one setting in the config/propel.ini file:

propel.builder.AddBehaviors = true     ; Default value is false

There is no behavior bundled by default in symfony, but they can be installed via plug-ins. Once a behavior plug-in is installed, you can assign the behavior to a class with a single line. For instance, if you install the sfPropelParanoidBehaviorPlugin in your application, you can extend an Article class with this behavior by adding the following at the end of the Article.class.php:

sfPropelBehavior::add('Article', array(
  'paranoid' => array('column' => 'deleted_at')
));

After rebuilding the model, deleted Article objects will remain in the database, invisible to the queries using the ORM, unless you temporarily disable the behavior with sfPropelParanoidBehavior::disable().

New in symfony 1.1: Alternatively, you can also declare behaviors directly in the schema.yml, by listing them under the _behaviors key (see Listing 8-34 below).

Check the list of symfony plug-ins in the wiki to find behaviors (http://trac.symfony-project.org/wiki/SymfonyPlugins#Behaviors). Each has its own documentation and installation guide.