CakePHP's Model::afterFind()
callback looks like:
afterFind(array $results, boolean $primary = false)
According to the documentation:
The
$primary
parameter indicates whether or not the current model was the model that the query originated on or whether or not this model was queried as an association. If a model is queried as an association the format of$results
can differ.
They can differ, but experimentation shows that they don't always differ. As far as I can tell, the $primary
parameter isn't actually all that useful. If it's set to false
you may or may not get a flattened data structure, so you may or may not wind up with the dreaded "cannot use string offset as an array" error message.
Although I haven't tried it yet, my thought based on the documentation was to ignore the $primary
flag altogether and just check the data:
public function afterFind($results, $primary = false) {
if (array_key_exists(0, $results) {
// operate on $results[0]['User']['fieldname']
} else {
// operate on $results['fieldname']
}
return $results;
}
This is hackish and I don't like it, but it seems likely to be more useful than $primary
.
Explicitly stated, my questions are:
$primary
flag actually useful for?$results
array, or have I missed something there?Indeed the $primary
parameter seems to only be useful in warning you of cases where the format of $results
is unpredictable. It is not useful in determining the format of $results
.
More information here: https://groups.google.com/forum/?fromgroups=#!topic/cake-php/Mqufi67UoFo
The solution offered there is to check !isset($results[$this->primaryKey])
to see what format $results
is. This is also a bit of a hack, but arguably better than checking for a key '0'.
The solution I ultimately came up with is to do something like this:
public function afterFind($results, $useless) {
// check for the primaryKey field
if(!isset($results[$this->primaryKey])) {
// standard format, use the array directly
$resultsArray =& $results;
} else {
// stupid format, create a dummy array
$resultsArray = array(array());
// and push a reference to the single value into our array
$resultsArray[0][$this->alias] =& $results;
}
// iterate through $resultsArray
foreach($resultsArray as &$result) {
// operate on $result[$this->alias]['fieldname']
// one piece of code for both cases. yay!
}
// return $results in whichever format it came in
// as but with the values modified by reference
return parent::afterFind($results, $useless);
}
This reduces code duplication because you don't have to write your field alteration logic twice (once for an array and once for non-array).
You may be able to avoid the references stuff altogether by just returning $resultsArray
at the end of the method, but I wasn't sure what issues that might cause if CakePHP (or some other parent class) expects $results
in the way it was passed in. Plus this way doesn't have the overhead of copying the $results
array.
If you can't always rely on primaryKey
being in the fields list AND you know the key you are looking for, you can get away with something a bit more simple. Here is an example:
/**
* Decrypt password
*
* @see Model::afterFind()
*/
public function afterFind($results, $primary = false) {
if (!empty($results['password'])) {
$results['password'] = Security::rijndael($results['password'], Configure::read('encrypt.key'), 'decrypt');
return $results;
}
foreach ($results as &$r) {
if (!empty($r[$this->alias]['password'])) {
$r[$this->alias]['password'] = Security::rijndael($r[$this->alias]['password'], Configure::read('encrypt.key'), 'decrypt');
}
}
return $results;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With