How to modify a field in afterFind()

Showing 1-8 of 8 messages
How to modify a field in afterFind() martin.w...@gmail.com 10/22/08 8:16 AM
Hi,
I thought I'd ask this here. (see why below)
How do I write afterFind() to modify a field.

For example just something simple like this (just an example):

function afterFind($data) {
    foreach ($data as $key => $val) {
        if ( isset($val[$this->alias]['name']) ) {
            $data[$key][$this->alias]['name2'] = $val[$this->alias]
['name'];
        }
    }
    debug($data);
    return $data;
}


What I want to know is how to pick out the field from the passed data
array. There are so many different ways the data is formatted that I
end up with a quite messy series of for's and if's and I still don't
fell 100% sure I got them all. I feel there must be some sure-fire way
to write these.

The Cookbook is not complete compared to what I get.
http://book.cakephp.org/view/681/afterFind

The API does not mention much about this.

I did not find any test in the core that helped me.

I did not find anything on Google that dealt with anything but basic
"primary" data.

I noticed that sometimes afterFind() is called more than once with
different data-structure each time. I asked about that here:
http://groups.google.com/group/cake-php/browse_thread/thread/c83e5f40ac861caa/1d88bdb31fa3d12f

I'd love some clarification of this callback. Thans in advance.
/Martin
Re: How to modify a field in afterFind() Martin Westin 10/23/08 12:58 AM
I compiled a list of the variations I have encountered in different
associations. I have not checked how behaviors are called.


When primary is set this is the structure I get just as in the
Cookbook:
array(
    '0' => array(
        'Model' => array(
            'id' => 1
        )
    )
)

When primary is not set I get a subset of these for each association:

hasOne
array(
    'id' => 1
)

habtm
array(
    '0' => array(
        'id' => 1
    )
)

hasOne, hasMany, belongsTo
array(
    '0' => array(
        'Model' => array(
            'id' => 1
        )
    )
)

habtm, hasMany
array(
    '0' => array(
        'Model' => array(
            '0' => array(
                'id' => 1
            )
        )
    )
)


This makes the number of ifs and fors quite many in order the catch
them all. And since more than one is sometimes called for the same
record in the same request, you also have to check is you have already
manipulated your data. At least if you do something "destructive" to
it like encryption/decryption or serialization.

My orignal question still stands. What is the best way to write an
afterFind in order to: 1. not miss converting data in some queries 2.
not double-convert the data ?

regards,
/Martin


On Oct 22, 5:16 pm, "martin.westin...@gmail.com"
<martin.westin...@gmail.com> wrote:
> Hi,
> I thought I'd ask this here. (see why below)
> How do I write afterFind() to modify a field.
>
> For example just something simple like this (just an example):
>
> function afterFind($data) {
>     foreach ($data as $key => $val) {
>         if ( isset($val[$this->alias]['name']) ) {
>             $data[$key][$this->alias]['name2'] = $val[$this->alias]
> ['name'];
>         }
>     }
>     debug($data);
>     return $data;
>
> }
>
> What I want to know is how to pick out the field from the passed data
> array. There are so many different ways the data is formatted that I
> end up with a quite messy series of for's and if's and I still don't
> fell 100% sure I got them all. I feel there must be some sure-fire way
> to write these.
>
> The Cookbook is not complete compared to what I get.http://book.cakephp.org/view/681/afterFind
>
> The API does not mention much about this.
>
> I did not find any test in the core that helped me.
>
> I did not find anything on Google that dealt with anything but basic
> "primary" data.
>
> I noticed that sometimes afterFind() is called more than once with
> different data-structure each time. I asked about that here:http://groups.google.com/group/cake-php/browse_thread/thread/c83e5f40...
Re: How to modify a field in afterFind() Matt Huggins 12/16/08 3:55 PM
I'm having the same issue, and I have yet to find a solution.  The
Cake documentation is wrong and/or the implementation is incorrect.

On Oct 23, 1:58 am, "martin.westin...@gmail.com"
Re: How to modify a field in afterFind() Martin Westin 12/17/08 3:36 AM

Hi Matt,
I ended up creating a special set of functions for this.

afterFind() calls _afterFind()
_afterFind() locates the data and calls doAfterFind()

This works for what I use afterFind for.
I will only have one place to edit if the data-structires change or I
find I have missed something.
It makes my models a lot more readable.

The relevant code if you should find it useful:

in SomeModel:

// this just calls the "real" afterFind
function afterFind($data, $primary) {
    return $this->_afterFind($data, $primary);
}

// receives data as a flat array of fields, no Modelname or anything.
// run from _afterFind splits datetime-field sendat into senddate and
sendtime
function doAfterFind($data) {

    if ( !isset($data['senddate']) ) {
        $timestamp = strtotime($data['sendat']);
        $data['senddate'] = date('Y-m-d', $timestamp);
        $data['sendtime'] = date('H', $timestamp);
    }

    return $data;
}


// AppModel::_afterFind()
function _afterFind($data, $primary) {

    if ( $primary ) {

        foreach ( $data as $key => $val ) {
            if ( isset($val[$this->alias]) ) {
                $data[$key][$this->alias] = $this->doAfterFind( $data
[$key][$this->alias] );
            }
        }

    } else {

        if ( isset($data['id']) ) {
            $data = $this->doAfterFind( $data );
        } else {

            foreach ( $data as $key => $val ) {
                if ( isset($val[$this->alias]) ) {
                    if ( isset($val[$this->alias]['id']) ) {
                        $data[$key][$this->alias] = $this->doAfterFind
( $data[$key][$this->alias] );
                    } else {
                        foreach ( $data[$key][$this->alias] as $key2
=> $val2 ) {
                            $data[$key][$this->alias][$key2] = $this-
>doAfterFind( $data[$key][$this->alias][$key2] );
                        }
                    }
                }
            }
        }

    }
    return $data;
Re: How to modify a field in afterFind() Matt Huggins 12/17/08 8:17 AM
Thanks for sharing, I'll definitely try this out!  I really hope they
fix this before 1.2 is considered a final release.  The documentation
is completely wrong otherwise.

On Dec 17, 5:36 am, "martin.westin...@gmail.com"
Re: How to modify a field in afterFind() Matt Huggins 12/17/08 8:56 AM
Your solution worked flawlessly!  I ended up making two small
changes.  First, I changed your references for 'id' to $this-
>primaryKey so that it can work with any model.  Second, I put the
afterFind/_afterFind into app_model.php so that it will call the
doAfterFind of any model that implements it, as per the following:

class AppModel extends Model {
         /**
          * sigh... $primary doesn't work as designed in CakePHP RC2 :(
          * this hack will manually go through and tear shit up
          */
         public function afterFind($results, $primary = false) {
                if (method_exists($this, 'doAfterFind')) {
                          if ($primary) {
                                   foreach ($results as $key => $val) {
                                            if (isset($val[$this->alias])) {
                                                     $results[$key][$this->alias] = $this->doAfterFind
($results[$key][$this->alias]);
                                                }
                                      }
                          } else {
                                  if (isset($results[$this->primaryKey])) {
                                           $results = $this->doAfterFind($results);
                                   } else {
                                            foreach ($results as $key => $val) {
                                                     if (isset($val[$this->alias])) {
                                                              if (isset($val[$this->alias][$this->primaryKey]))
{
                                                                       $results[$key][$this->alias] = $this->doAfterFind
($results[$key][$this->alias]);
                                                              } else {
                                                                       foreach ($results[$key][$this->alias] as $key2
=> $val2) {
                                                                                $results[$key][$this->alias][$key2] = $this-
>doAfterFind($results[$key][$this->alias][$key2]);
                                                                              }
                                                                    }
                                                          }
                                                }
                                      }
                           }
                }
                 return $results;
         }
}

On Dec 17, 5:36 am, "martin.westin...@gmail.com"
<martin.westin...@gmail.com> wrote:
Re: How to modify a field in afterFind() Martin Westin 12/17/08 10:04 AM
thanks for the modifications. I am happy it works for you too.

/Martin
Re: How to modify a field in afterFind() Luke Sapan 4/19/13 1:52 PM
I just wanted to thank both of you for working through this and figuring it out. Nearly five years later, CakePHP still has this issue with version 2 and this code worked flawlessly. You guys rock!