How to use CActiveRecord.beforeFind() ?

Hi,

I’m in a need to use this method but the documentation on it is slim.

How do I alter the ‘find’ that is about to take place inside beforeFind()? Messing with ‘$this’ attributes is not useful since its not even populated, which is a little surprise.

I’ve seen the documentation mentions a “hidden CDbCriteria parameter” but I just couldn’t guess how to use it… .

Any help will be appreciated.

Thanks,

Boaz.

Check out the documentation for CActiveRecord

If you just want to modify the CDbCriteria attached to the CActiveRecord, you can do something like this:




public function beforeFind()

{

	$this->getDbCriteria()->mergeWith(array(

		'condition'=>"id!=6",

	));

	//var_dump($this->dbCriteria);

	parent::beforeFind();

}



Thanks for the reply!

Yep, that I know.

What I need to do is rather simple, I’ll state it here: I’ve got a table column for storing IP addresses. The most efficient design from scalability perspective, is to use a VARBINARY(16) data type for the column. See for example this SO question page (and answers) on this.

So, the cleanest solution would be to have beforeFind(), afterFind() and beforeSave() work transparently for the users. In the code stack, the IP addresses would be the normal dotted-quad and in the DB level, its whatever that goes into the field after utilizing PHP’s inet_pton() method in those after/before hook methods.

It was supposed to be cool. and it is cool - with afterFind() and beforeSave(), where I have the ip_address attribute of the object at hand, at the mercy of my uber-manipulation powers.

Here’s the point, and the need: thing is, I don’t know how to achieve that on beforeFind(). I cannot do a blind mergeWith() as I need to check if ip_address attribute is part of the original criteria, and that I don’t know how to do.

Many thanks!

Boaz.

I see why it would be convenient for beforeSave() and afterFind(), since in both cases you will have a populated CActiveRecord object.

What exactly are you trying to accomplish with beforeFind() though? Generally my use case for beforeFind() would be something like this (Drawing it out for clarity):




$model = User::model();

// Here is your empty User model to be used for performing the find. No IP to convert.

$user = $model->findByPk(5);

// $user has been found, afterFind() kicked in and converted the IP.



Are you trying to use beforeFind() as a means of doing something like this?




$user = User::model()->findByIP('192.168.0.1');

// Found the user by IP



[font="Arial"]What I try to achieve is as follows, written in pseudocode/algorithm style:[/font]

[font="Courier New"][color="#0000ff"]method beforeFind

[/color][/font][indent][font=“Courier New”][color="#0000ff"]if current condition includes an ip_address field[/color][/font][/indent][indent][indent][font=“Courier New”][color="#0000ff"]/* this address field is in human readable form. e.g. “123.22.33.44”. we need to transform it to VARBINARY format /[/color][/font][/indent][/indent][indent][indent][font=“Courier New”][color="#0000ff"]transformed_value = php’s inet_pton(current_value)[/color][/font][/indent][/indent][indent][indent][font=“Courier New”][color="#0000ff"]apply/modify query criteria/condition to use transformed_value instead of current_value[/color][/font][/indent][/indent][indent][font=“Courier New”][color="#0000ff"]else[/color][/font][/indent][indent][indent][font=“Courier New”][color="#0000ff"]/ well, actually do nothing - the search criteria does not even mention an ip. I’m not here and you didn’t notice that beforeFind() was called! :) */[/color][/font][/indent][/indent][indent][font=“Courier New”][color="#0000ff"]end if[/color][/font][/indent][font=“Courier New”][color="#0000ff"]end method[/color][/font]

[font="Arial"]

I think it clears it, isn’t it?[/font]

Do you have an example of your model having an IP address in it’s condition already?

well, sort of - I’m using the following in the code. God knows what/how exactly Yii handles it:


$bookeeping_record = PageViewBookeeping::model()->findByAttributes(array('ip_address' => $this->_clientIpAddress, 'result_id' => $this->statsRecord->id));



Does it help? (I hope this reply was what you meant)

I would recommend overriding findByAttributes() in that case:




public function findByAttributes($attributes,$condition='',$params=array())

{

	foreach($attributes as $attribute=>$value)

	{

		if($attribute === 'ip_address')

			// converting the IP

			$attributes[$attribute] = inet_pton($value);

	}

	return parent::findByAttributes($attributes, $condition, $params);

}



From what I can see the attributes never get assigned to the actual model using any of the findBy methods, so trying to alter them in beforeFind() would not do much good.

Oh, but of course. How could I not think of this?.. :( (so obvious - just override parent method and do the trick there).

About your last message - that’s exactly what I bumped onto. There’s that ‘hint’ in the documentation (here) that talks about “a hidden CDbCriteria parameter” but leaves it as that level… :)

Anyway - onto finishing that extension, armed with your suggestion, which is much more elegant than "remembering" to do a inet_pton() (and its opposite function) on every usage of the ip_address attribute… . You could see the results here: http://www.yiiframew…pcviewscounter/

Thanks for the prompt assistance!

Boaz.

Aaah, I see what you meant about the hidden parameter now.

Glad that solution works for you.