Passing ActiveRecord attributes to its behavior

Hi,

I want to pass an attribute of a model to its behavior. But it appears like the parameters of the behavior-call are bound before the model is saved. Does this theory sound valid (the code snippet underneath shows what I’m talking about)? If so, is there a workaround?




<?php


class TestModel extends CActiveRecord {


	...

	public function behaviors() {

	return array(

		'idPrint'=>array(

			'class'=>'ext.behaviors.TestBehavior'

			'a'=>$this->id

			'b'=>$this //This would work, but I don't want it

		)

	);

	}


}

?>


<?php

class TestBehavior extends CActiveRecordBehavior {

	public $a = '';

        public $b = '';


	public function afterSave($event) {

		echo $this->a; //null

		echo $this->b->id; //Works!

	}


}

?>






//inside the behavior:

$this->owner->id;



$this->owner is the model this behavior is attached to, thus you have full access to it without doing anything special.

Thanks for the quick reply!

I should have clarified the following:

In order for my behavior to be as generic as possible, I don’t want to directly access the model’s attribute but instead pass it over.

If I understand you correctly you simply want to configure the behavior by telling it what attribute, in the model it is attached to, that it should do something with?

If so, just use $this->owner->{$this->attributeName} inside the behavior’s afterSave(), assuming you gave it ‘attributeName’=>‘foo’ in the config in behaviors() and the model it is attached to has an attribute named ‘foo’.

I’m basing this understanding of what you want on the pastebin you showed me in the #yii channel on the IRC/freenode, perhaps it’s wrong (hard to tell by this new example you posted :)

You don’t have what to pass because it is automatically passed to the owner property.

If you want to pass another model BUT NOT the one the behavior is attached to, then you would do as you did with your $b property.

Hey,

glad to see that you followed the link to my thread :) thanks

I should have explained it more, sorry guys. I will try again:

The behavior is called in different models. The parameters varies from model to model, in some it is a fixed string, in some a combination of string and attribute, and so on. Ergo I will need the value of the parameter to be an already evaluated string.

I hope this reacts to all your answers

Please give an example of what evaluation you mean. I’m not sure I see why you don’t “evaluate” it in the afterSave(), whatever it is you need to do.

Can you add a method to all the models that you use this behavior on that just returns this parameter? That would seem to be the most straightforward way to handle this, given your requirements.







<?php


class TestModel extends CActiveRecord {


        ...

        public function behaviors() {

        return array(

                'idPrint'=>array(

                        'class'=>'ext.behaviors.TestBehavior'

                )

        );

        }


        public function getTestParameter() {

             return "Hello ".$this->FirstName." ".$this->LastName;

        }


}

?>




<?php

class TestBehavior extends CActiveRecordBehavior {

        public $a = '';


        public function afterSave($event) {

                echo $this->owner->TestParameter;

        }


}

?>

Alternatively, if it had to be a behavior parameter you could do this:




<?php


class TestModel extends CActiveRecord {


	...

	public function behaviors() {

	return array(

		'idPrint'=>array(

			'class'=>'ext.behaviors.TestBehavior',

			'fn'=>create_function('$model', 'return "Hello ".$model->Firstname."!";'),

                        // if you have php 5.3  

                        // 'fn'=> function($model) { return "Hello ".$model->Firstname."!"; },     

		)

	);

	}


}

?>


<?php

class TestBehavior extends CActiveRecordBehavior {

	public $fn = null;


	public function afterSave($event) {

		echo $this->fn($this->owner); 

	}


}

?>



Hey,

thanks for all the answers. Great community!

I couldn’t reply since there was is a post limit of 3 for the first day and I wasn’t around at the weekend.

Anyway, btt:

@rAWTAZ: The behavior is getting a parameter for the path in which a file should be saved and most of the time the path consists of some variation of the model’s attributes.

@zilles: Thank you for your two ideas. I think I will go with your 2nd one which almost perfectly fits my scenario.

Thank you and everyone else for helping!

Edit 1: I just found out that you can’t pass functions in PHP. Too bad, nice idea though :-/

I think I will try your first idea now, if noone else nor me comes up with a better idea.

Edit 2: It’s not really generic if I require every model that uses the behavior to implement the functions needed for the parameters. It sure is more generic then the other ideas but if someone else has a new idea about this, I would be glad to read it!

Edit 3: Okay, it gets worse:

With the functions implemented in the model I var_dumped the return values in the behavior. And what did I get? Empty values. So the same output I got when I just passed that model’s attributes. :unsure:

Edit 4 (Hopefully the last):

It works when I not just return the attributes but connect them to an empty string:


return $model->id . '';

For now I will try to work with this version!

Edit 5 (Nope!):

This worked for the filename thing, but I just felt what losing generecity feels like… There is one ex-parameter, now function, in one of the models which needs to have different parameters at different times(the behavior is called twice in that model). So this solution would only work out with way too many dirty little fixes.

I’m still searching for that perfect one :)

Anyone?

What’s wrong with passing functions? You can try this:




class TestModel extends CActiveRecord {


        ...

        public function behaviors() {

        return array(

                'idPrint'=>array(

                        'class'=>'ext.behaviors.TestBehavior'

                        'pathCallback'=>array($this, 'generatePath'),

                )

        );

        }


        public function generatePath()

        {

            return MY_UPLOADS.'/uploads/foo/bar/'.$this->date.'/'.$this->id;

        }


}


class TestBehavior extends CActiveRecordBehavior {


        public $pathCallback;


        public function afterSave($event) {

            $path = $this->getPath();

        }


        private function getPath() {

            return call_user_func($this->pathCallback);

        }


}



I pass functions all the time in php.

Thank you guys, I didn’t try it with call user func!

Problem kinda solved :)