beforeSave / validate in behavior

I just wrote a small behavior for saving slugs… only how can i make it that if there is a protected beforeSave / beforeValidate in the model that these will not overwrite the functionality from the behavior?


<?php

class SlugBehavior extends CActiveRecordBehavior {


    /**

    * The column name for the unqiue url

    */

    public $slug_col = 'slug';


    /**

     * Character to replace spaces

     * @var string

     */

    public $replace_space = '-';


    /**

     * Overwrite slug when updating

     */

     public $overwrite = true;


    /**

     * Before saving to database

     * @param <type> $on 

     */

    public function beforeSave() {


        $slug = $this->getSlug($this->Owner->title);

        $this->Owner->{$this->slug_col} = $slug;


        $matches = $this->getMatches($this->Owner->slug);


        if($matches){

	    $ar_matches = array();

	    foreach ($matches as $match){

		$ar_matches[] = $match->slug;

	    }

	    

	    $new_slug = $slug;

	    $index = 1;

	    while ($index > 0) {

		if (in_array($new_slug, $ar_matches)){

		    $new_slug = $slug.$this->replace_space.$index;

		    $index++;

		} else {

		    $slug = $new_slug;

		    $index = -1;

		}

	    }

	}

	die();

        if($this->Owner->isNewRecord){

            $this->Owner->{$this->slug_col} = $slug;

        }

        elseif ($this->overwrite === true) {

            $this->Owner->{$this->slug_col} = $slug;

        }

        

    }


    /**

     * Lookup if string already exists in database

     * @param string $title 

     */

    public function getMatches($slug){

        $slugs = $this->Owner->findAll(array(

            'select'=> $this->slug_col,

            'condition'=> $this->slug_col." LIKE '%".$slug."%'"

        ));

        return $slugs;

    }


    /**

     * Convert string to slug 

     * @param string $title

     * @return mixed $slug

     */

    public function getSlug($title){

        $clean = trim($title);

	$clean = iconv('UTF-8', 'ASCII//TRANSLIT', $clean);

	$clean = preg_replace("/[^a-zA-Z0-9\/_| -]/", '', $clean);

	$clean = strtolower($clean);

	$clean = preg_replace("/[\/_| -]+/", $this->replace_space, $clean);


	return $clean;

    }

}

I might be wrong, but according to my interpretation of the behavior concept, it extends the class attached to with additional methods. That’s an example of aggregation and can be thought of as a way to accomplish multiple inheritance.

On the other hand, I just had a look at the AutoTimestampBehavior from the skeleton app. It seems to use beforeValidate() similar to the beforeSave() method in your behavior. So now I’m confused.

I guess you could use a different method name (possibly check for existence before the call). Another option would be to register a handler for the OnBeforeSave event. Not clear to me how to handle the ‘on’ parameter, though.

/Tommy

I see another extension use:

if (!parent::beforeSave()) return false;

In the beforeSave of the model… it works, but not really nice solution. Easy to forget and you don’t know which methods an extension use.

I don’t understand. I thought your problem was that your own beforeSave() (in your model) wasn’t called with the behavior attached. But I’ll rest my case. I really need to find uses for this mechanism myself, before being able to comment.

/Tommy

See CActiveRecordBehavior implementation. It only attaches the additional event handlers from the behavior to the model. So the parent implementation of beforeSave can not be overriden. Instead both will be called.