#6276: afterFind() called twice / documentation does not match behaviour
--------------------------------------------------+-------------------------
    Reporter:  nemosoft                           |          Type:  RFC   
      Status:  new                                |      Priority:  Medium
   Milestone:  1.2.x.x                            |     Component:  Model 
     Version:  1.2 Final                          |      Severity:  Normal
    Keywords:  afterFind primary data corruption  |   Php_version:  PHP 5 
Cake_version:  8120                               |  
--------------------------------------------------+-------------------------
 SUMMARY
 [[BR]]
 * With hasMany relations, afterFind() is called twice on the same
 data.[[br]]
 * The example given in the manual for afterFind() will mess up your
 data.[[br]]
 [[BR]]

 DESCRIPTION[[BR]]

 I would like to have your opinion on the following problem I've
 encountered using Model::afterFind(). It is the combination of afterFind's
 behaviour and the documentation in the manual that causes corruption of
 the data that is being read with a hasMany relation.[[BR]]
 [[BR]]

 See the attached example of a simple Tutor/Pupil/Course database. A Tutor
 hasMany Pupils, and also hasMany Courses. When reading in a Tutor, Pupils
 and Courses are automatically read as well, and their data is passed
 through their respective afterFind() functions *twice*. Or rather,
 afterFind() is called once for each individual record (the first pass),
 and once more wtih all data combined (second pass). The problem is that
 the data layout differs subtly between the first and second pass. The
 second pass has the same data one layer deeper; if one follows blindly the
 example given in the manual (http://book.cakephp.org/view/681/afterFind)
 as I did, the data will be messed up during the second pass.[[BR]][[BR]]


 The first pass formats data like this:

 {{{
 $results = array { [0] { [Pupil] { [id], [name], [tutor_id] } } }
 }}}

 This is called for each record retrieved from the database.[[BR]]


 The second pass uses all data in a combined format:

 {{{
 $results = array { [0] { [Pupil] { [0] { [id] [name] [tutor_id] }, [1] {
 ... }, [2] { ... } } } }
 }}}

 In both cases, the $primary boolean is set to false.
 [[BR]]
 [[BR]]


 There are multiple issues with this behaviour:

 - Essentially the same data is passed twice through afterFind(); this is
 inefficient and can introduce side effects (see below)[[BR]]
 - It both cases $primary is false, but the data is in a different layout.
 Moreover, it does not match the documentation!
 [[BR]][[BR]]

 Others have noticed this anomolous behaviour, see for example
 [http://groups.google.com/group/cake-
 
php/browse_thread/thread/c83e5f40ac861caa/1d88bdb31fa3d12f?lnk=gst&q=afterFind#1d88bdb31fa3d12f
 this Google Groups post] and [http://groups.google.com/group/cake-
 
php/browse_thread/thread/4b10a1d5ccd468d9/fb225acbdac9109d?lnk=gst&q=afterFind#fb225acbdac9109d
 this one].
 [[br]]
 [[br]]



 Some people have suggested solutions, like [http://groups.google.com/group
 /cake-
 
php/browse_thread/thread/d1eea174c3143d83/6bada5f9fce6f714?lnk=gst&q=afterFind#6bada5f9fce6f714
 this one] but in that case the data is processed twice. This could lead to
 weird data behaviour, for example if one converts dates from database to
 human format by string manipulation, concatenates strings, adds numbers,
 etc. I've used a rot13() function in my example.
 [[br]]
 [[br]]

 I think the behaviour stems from fixes for another problem where
 afterFind() was not called at all for recursive retrievals (for example,
 see changeset 7347). Now it's being called too many times.[[br]][[br]]

 My questions are:[[br]]
 * Is the observed behaviour of calling afterFind() twice intended? If so,
 what's the reason behind passing different data layouts?[[br]]
 * Could somebody please update the documentation for afterFind()? Either
 mention that the function is called twice with hasMany relations, or fix
 the example so that it won't work on the second pass.[[br]][[br]]

 I'm of the opinion that the second call to afterFind() is an error. It
 would make sense to process each record as it is being retrieved, but not
 do it again on the combined data set. The former would also eliminate the
 distinction between primary and non-primary retrievals, since the data
 layout is the same now (my observation).[[br]][[br]]

 Of course, a workaround would be to ignore the second pass, but I don't
 think that burden should be placed on the developer...

-- 
Ticket URL: <https://trac.cakephp.org/ticket/6276>
CakePHP : The Rapid Development Framework for PHP <https://trac.cakephp.org/>
Cake is a rapid development framework for PHP which uses commonly known design 
patterns like ActiveRecord, Association Data Mapping, Front Controller and MVC. 
Our primary goal is to provide a structured framework that enables PHP users at 
all levels to rapidly develop robust web applications, without any loss to 
flexibility.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"tickets cakephp" group.
To post to this group, send email to tickets-cakephp@googlegroups.com
To unsubscribe from this group, send email to 
tickets-cakephp+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/tickets-cakephp?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to