An improvement to unbindModel on model side

By Mariano Iglesias (mariano)
Not long ago I saw Tom OReilly's great tutorial entitled "Keeping bindModel and unbindModel out of your Controllers." While he showed us some great tips I was not so comfortable having to define my model relations in a different way, but I still wanted the possibility to only specify what relations I want to get when querying a model.
For those of you who didn't, I recommend you read Tom OReilly's Keeping bindModel and unbindModel out of your Controllers. He makes a good argument why it is better to take care of the details of ubinding models on the model itself, and just let the controller specify which models it's expecting to get.

The problem with that solution is that it requires you to change the way you define your model relations. While it may make sense on some specific cases, I am not a fan of changing the way things are done within CakePHP. Rather try to change your code to suit your needs, and let CakePHP do what it does best: act as a framework.

Also it approaches model associations in a different way: model relations get loaded *when* you call expects(), while I wanted more a way to have my associations defined on each model the CakePHP way, and then specify which relations I'm interested in getting back when querying a model. So models behave the way models are supposed: they define the data within and associations to other models, and controllers behave the way controllers do: they query the models and optionally specify what model information they are insterested in.

Furthermore, I wanted to change it in a way that if I don't use expects() I get the standard result CakePHP brings: a model with its related data. With Tom's way, you have to call expects() for model associations to become useful. With this modification, you call expects() when you want to limit the amount of information you get. Makes sense, doesn't it?

UPDATE December 16, 2006: Now we have two ways to call expects. The version 1 way (array of models to include on response) or the new way which is a variable number of arguments including the models, and inner models, that should be returned when querying. Please refer to the section included at the bottom of this article. Also note that those bakers who downloaded the code prior to this update may update your expects() function and yet no change on your code is necessary.

UPDATE February 26, 2007: Two reported issues have been solved: "when defining multiple relations to the same Model, expects() would not work as expected", and "when unattaching inner model relationships through expects, such as by calling expects('InnerModelA', 'InnerModelB.InnerModelB') inner model relations are not restored after find()." This has change the code to the latest version you see below. IMPORTANT: as you see the new version includes a re-definition of afterFind() at the AppModel level. So if you re-define this function on your models, make sure to always call parent::afterFind() (which you should've been doing anyway)

UPDATE March 24, 2007: An issue with afterFind() being executed for inner model relationships (and thus re-binding relationships before main query was executed) has been fixed.

So here's the code. The first thing we will need is to add the following code to our AppModel class:

LATEST CODE UPDATE: March 24, 2007

Model Class:

Download code <?php class AppModel extends Model
{
    function 
afterFind($results
    { 
        if (isset(
$this->__runResetExpects) && $this->__runResetExpects)
        {
            
$this->__resetExpects();
            unset(
$this->__runResetExpects);
        }
        
        return 
parent::afterFind($results);
    }
    
    
/**
     * Unbinds all relations from a model except the specified ones. Calling this function without
     * parameters unbinds all related models.
     * 
     * @access public
     * @since 1.0
     */
    
function expects() 
    { 
        
$models = array();
        
$arguments func_get_args();
        
$innerCall false;

        if (!empty(
$arguments) && is_bool($arguments[0]))
        {
            
$innerCall $arguments[0];
        }
        
        foreach(
$arguments as $index => $argument
        { 
            if (
is_array($argument)) 
            { 
                if (
count($argument) > 0
                { 
                    
$arguments am($arguments$argument); 
                } 

                unset(
$arguments[$index]); 
            }
        }
        
        foreach(
$arguments as $index => $argument)
        {
            if (!
is_string($argument))
            {
                unset(
$arguments[$index]);
            }
        }

        if (
count($arguments) == 0
        { 
            
$models[$this->name] = array(); 
        } 
        else 
        { 
            foreach(
$arguments as $argument
            { 
                if (
strpos($argument'.') !== false
                { 
                    
$model substr($argument0strpos($argument'.')); 
                    
$child substr($argumentstrpos($argument'.') + 1); 

                    if (
$child == $model
                    {
                        
$models[$model] = array(); 
                    } 
                    else 
                    { 
                        
$models[$model][] = $child
                    } 
                }
                else 
                { 
                    
$models[$this->name][] = $argument
                } 
            } 
        }
        
        
$relationTypes = array ('belongsTo''hasOne''hasMany''hasAndBelongsToMany');

        foreach(
$models as $bindingName => $children
        {
            
$model null;
            
            foreach(
$relationTypes as $relationType
            { 
                
$currentRelation = (isset($this->$relationType) ? $this->$relationType null);
                
                if (isset(
$currentRelation) && isset($currentRelation[$bindingName]) && is_array($currentRelation[$bindingName]) && isset($currentRelation[$bindingName]['className'])) 
                {
                    
$model $currentRelation[$bindingName]['className'];
                    break;
                }
            }
            
            if (!isset(
$model))
            {
                
$model $bindingName;
            }
            
            if (isset(
$model) && $model != $this->name && isset($this->$model)) 
            {
                if (!isset(
$this->__backInnerAssociation))
                {
                    
$this->__backInnerAssociation = array();
                } 
                
                
$this->__backInnerAssociation[] = $model;
                
                
$this->$model->expects(true$children);
            } 
        }
        
        if (isset(
$models[$this->name])) 
        { 
            foreach(
$models as $model => $children
            { 
                if (
$model != $this->name
                { 
                    
$models[$this->name][] = $model
                } 
            } 
    
            
$models array_unique($models[$this->name]);
            
$unbind = array(); 
    
            foreach(
$relationTypes as $relation
            { 
                if (isset(
$this->$relation)) 
                { 
                    foreach(
$this->$relation as $bindingName => $bindingData)
                    { 
                        if (!
in_array($bindingName$models))
                        { 
                            
$unbind[$relation][] = $bindingName
                        } 
                    } 
                } 
            } 
    
            if (
count($unbind) > 0
            { 
                
$this->unbindModel($unbind); 
            }
        }

        if (!
$innerCall)
        {
            
$this->__runResetExpects true;
        }
    }
    
    
/**
     * Resets all relations and inner model relations after calling expects() and find().
     * 
     * @access private
     * @since 1.1
     */
    
function __resetExpects()
    {
        if (isset(
$this->__backAssociation))
        {
            
$this->__resetAssociations();
        }
        
        if (isset(
$this->__backInnerAssociation))
        {
            foreach(
$this->__backInnerAssociation as $model)
            {
                
$this->$model->__resetExpects();
            }
            
            unset(
$this->__backInnerAssociation);
        }
    }
}
?>


You don't need to define another variable on your model, just set your relations as you normally do on Cake. For example, let's take Tom's Title example but let's build it the Cake way:

Model Class:

Download code <?php class Title extends AppModel
{
    var 
$belongsTo = array (
        
'Book' => array (
            
'className' => 'Book',
            
'foreignKey' => 'collection_id'
        
),
        
'Album' => array (
            
'className' => 'Album',
            
'foreignKey' => 'collection_id'
        
)
    );
    
    var 
$hasOne = array (
        
'Story' => array (
            
'className' => 'Story'
        
),
        
'Photo' => array (
            
'className' => 'Photo'
        
)
    );
    
    var 
$hasMany = array (
        
'Post' => array (
            
'className' => 'Post',
            
'order' => 'Post.id DESC'
        
)
    );
}
?>


Following his example, we now want to query this model and only return its associations with Story and Post, disregarding the rest:

Controller Class:

Download code <?php class TitlesController extends AppController 

    function list(
$id
    { 
        
// establish necessary associations 
        
        
$this->Title->expects(array('Story''Post')); 
        
$this->Title->Post->expects(array('User')); 
        
        
$this->Title->recursive 2
        
        
$results $this->Title->read(null$id); 
    } 

?>


As you can see you use the expects() function the same way, but you don't need to change the way associations are defined in CakePHP. Furthermore, we make clean calls to CakePHP's bult in unbindModel() function in the model class, so we are safe for any further CakePHP upgrades. Also, there's an easy way to do an unbindAll() as Tom was requested, just call expects() with no parameters:

Controller Class:

Download code <?php class TitlesController extends AppController 

    function list(
$id
    { 
        
$this->Title->expects(); 
        
        
$results $this->Title->read(null$id); 
    } 

?>


Making multiple expects() in one call


As noted earlier, on December 16, 2006 I added a new version of the code to allow an easier way to do multiple expects() calls. Let's take this code:

Controller Class:

Download code <?php 
$this
->Post->Author->expects();
$this->Post->Category->expects();
$this->Post->PostDetail->expects(array('PostExtendedDetail''PostAttachment'));
?>


We are here not limiting the Post model, but its related models. You can achieve the same result by using the new method of call:

Controller Class:

Download code <?php 
$this
->Post->expects('Author.Author''Category.Category'
    
'PostDetail.PostExtendedDetail''PostDetail.PostAttachment');
?>


As you can see in just one call we can provide the necessary restrictions. Note the form of specifying an inner restriction: Model.InnerModel. If you wish to obtain the same effect as: $this->Model->InnerModel->expects() then the inner restriction is of the form: Model.Model

Let's look at another example. On the old form we do:

Controller Class:

Download code <?php 
$this
->Title->expects(array('Story''Post')); 
$this->Title->Post->expects(array('User'));
?>


On the new form we would do:

Controller Class:

Download code <?php 
$this
->Title->expects('Story''Post''Post.User');
?>


Or better yet:

Controller Class:

Download code <?php 
$this
->Title->expects('Story''Post.User');
?>


A final yet simpler example:

Controller Class:

Download code <?php 
$this
->Title->expects(array('Story''Post'));
?>


can be also obtained by doing:

Controller Class:

Download code <?php 
$this
->Title->expects('Story''Post');
?>


Once again I must alert that the previous form of method calling (through array of models) is still valid and will work as expected. This was just a handy modification to further improve the way you use this functionality from your controllers.

 

Comments 185

CakePHP Team Comments Author Comments
 

Comment

1 AKA unbindAllExcept()

I would like to call this functionality as unbindAllExcept() rather than expect.

I would still like to use Tom's way when there are associations which I don't often bind to and your way when there are associations which I don't often unbind. So depending on the the context both the things make equal sense for me
Posted Dec 13, 2006 by Dr. Tarique Sani
 

Bug

2 Association Name vs. Classname

Thanks Mariano for this nice little piece of code. This helps a lot!

I came across an issue when using different association names than classnames, ie:

var belongsTo = array('FromUser'=>array('className'=>'User'), 'ToUser'=>array('className'=>'User'));

I changed your code to use the index instead of the classname of the association and it worked.
Posted Jan 5, 2007 by Timo Derstappen
 

Comment

3 Recursive

There are some questions I have. If I have a situation where I need to go recursive=3 then I get a lot of data, so expects is very helpful. But if those data is recursively getting to the same model (remember it could have a different role as in my comment above) and i which to include that object but not all its associations. Then all the associations get cut in the first place.

let me try to explain this with an example:
i have a user model which has ratings and the ratings have a ByUser and an AboutUser. so if i need recursive=3 because of other associations with the user model

$this->User->expects('User', 'Rating.ByUser');

results in user plus ratings plus byuser plus all associations to byuser. if i want to cut these associations by using

$this->User->expects('User', 'Rating.ByUser.ByUser');

unbinding affects the user model itself and i get only the user tuple without any associations.

i know that this is a general problem with unbindModel. but maybe someone has any conclusion or hint how to solve this elegantly.

you can solve it by getting the data more manually. for instance you can exclude the ratings in $this->User->read() and make a $this->User->Rating->findAll() with the correct expects. this is okay for me, but you can't even merge this data cause the first results in array('User'..., 'Rating'=>array(0=>..., 1=>)) and the second is array(0=>array('Rating'=>....),1=>...). But this is another problem with cake :) , there are arrays instead of objects (i know there are plans to solve this issue).

sorry if this came up like a rant. just want to get a little discussion about this. if you don't care what i'm talking about, ignore this ;)

Posted Jan 5, 2007 by Timo Derstappen
 

Comment

4 Unbind issues

In the above given example

<?php
$this->Post->expects('Author.Author', 'Category.Category',
'PostDetail.PostExtendedDetail', 'PostDetail.PostAttachment');
?>

after running a find query on Post model, all models which were unbinded from 'Post' will be re-binded. But the models which were unbinded from 'PostDetail' will not be re-binded to 'PostDetail'.

So if we run a find query on 'PostDetail' we will not get the associated data. We will have to specifically bind the unbinded models to 'PostDetail' after running the find on 'Post'. This is not a problem if 'PostDetail' is associated with a few models but what if it is associated with quite a handful of? It will be unwise to bind all those again manually.

Perhaps we need a bindAll method.
Posted Jan 19, 2007 by Abbas Ali
 

Bug

5 Association Name vs. Classname

Hi,

I am having the same problem as reported by Timo Derstappen (Comment 2). But I am not sure where to make appropriate changes. Please let me know the changes that needs to be made.
Posted Dec 31, 1969 by Ritesh Agrawal
 

Comment

6 Re 5 Association Name vs Classname

@Ritesh

Not sure if this is 100% correct all the time, but it seemed to work ok for me.

$relations = array ('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');

foreach($relations as $relation) {
if (isset($this->$relation)) {
foreach($this->$relation as $association => $currentModel) {
if (!in_array($association, $models)) {
$unbind[$relation][] = $association;
}
}
}
}
Posted Dec 31, 1969 by Neil Crookes
 

Comment

7 Thanks

@ Neil Crookes - Thanks for sharing the code. It worked for me.

@ Mariano Iglesias - Thanks for the great extension

Ritesh
Posted Dec 31, 1969 by Ritesh Agrawal
 

Comment

8 New Version

Guys, take a look at the new version as it solves the issues reported here. Thanks Neil!
Posted Dec 31, 1969 by Mariano Iglesias
 

Comment

9 Thanks

Thanks for this very helpful function. It saved me a lot of headache
Posted Dec 31, 1969 by Sava Rados
 

Comment

10 Tom OReillys solution is superior (in most cases)

"With Tom's way, you have to call expects() for model associations to become useful. With this modification, you call expects() when you want to limit the amount of information you get. Makes sense, doesn't it?"

The reason why calling expects() to activate associations is actual a better idea than calling it to deactivate assocations is because the former is more resistant to future extensions of your models (and associations).

A specification of what you need is a definition that will remain constant, even after you added a zillion new associations to corresponding models later. A specification of what you do not need though, is a definition relative to the current definition of a model (its associations) and therefore depends on this model definition. As soon as this model definition changes (you add associations), the result of the unbind will change as well. In my experience, in most cases this is not what you want.
Posted Mar 14, 2007 by tawm
 

Bug

11 Addition to afterFind() method

I needed to replace the afterFind in the example code with the following to get it to work...


  function afterFind($results) {
      $this->__resetExpects();
      return $results;
  }


...this replicated the afterFind method in cake's Model class. Without it, the results of my find calls were empty!
Posted Mar 15, 2007 by Neil Crookes
 

Comment

12 Responses

@tawm: that's your opinion, as a matter of fact model relationships are *integral* to CakePHP. The most recommended way to deal with models on controllers is not to use $uses, but instead to use model associations (so if you are on a Users controller and you need to fetch all profiles, recommended way of doing so is $this->User->Profile->findAll()). Having to load up the relations *on demand*, when this is the recommended way to navigate associated models, just seem like a waste of effort to me. I'm not saying Tom's work is bad (which is not the case, at all), just that under CakePHP's best coding practices it is not the recommended way to go.

@Neil: Thanks for the heads up! My working copy had the version as you see now on the code, but somehow it didn't make it to the article :)
Posted Mar 16, 2007 by Mariano Iglesias
 

Question

13 1.2 compatible

Does this need tweaking for 1.2? I tried plugging it in, and some of this worked, like the first level stuff, but couldn't get the second level to work (Comment.Person).

Also, does the recursive variable need to be set for this to look below the first level of associations? If so, it would be great if (Comment.Person) would automatically trigger expects() to set the recursive variable.

Thanks, Mariano!
Posted Apr 7, 2007 by Joel Stein
 

Comment

14 expects Test cases for CakePHP 1.2

@Joel: I have created a set of test cases for expects() to run on CakePHP 1.2 and all tests succeed. I have not encountered any issues running expects() on 1.2 and don't expect to find any.

Regarding $recursive: it takes precedence over your bindings. This is normal CakePHP behavior, not something that expects() changes (once again I must clarify that expects() relies on stable CakePHP core elements and does not make any hacks to work), so if you set recursive to 1 but you have a third level relationship you wish to obtain, you will have to set recursive to a higher value.

For lack of a better place here's the test case for CakePHP 1.2:

http://bin.cakephp.org/view/1443836304
Posted Apr 7, 2007 by Mariano Iglesias
 

Question

15 calling expects() before paginate()

Hey
is it possible to make expect() more permament ( something like $reset in unbindModel() ) - it would be usefull for $this->paginate().

Cause now when you call expects() before paginate() it only works for the first findCount that cake makes. So the actuall find will have all the models.
greets,
Marcin Domanski aka kabturek
Posted Jun 11, 2007 by Marcin Domanski
 

Comment

16 re calling expects() before paginate()

Although it's not mentioned in the article, if you pass `true` as the first argument to expects() then the associations will not be reset after the call.
Posted Jun 22, 2007 by JP Mortiboys
 

Comment

17 re calling expects() before paginate()

thx JP Mortiboys i didn't notice this when i looked at the code :)

greets,
Marcin Domanski aka kabturek
Posted Jun 27, 2007 by Marcin Domanski
 

Comment

18 My response

@tawm: thanks! I think so too, but then again I'm biased ;)

I know one thing for sure, Mariano and I agree that having dynamic control of your associations is a great and powerful feature. Model relationships *are* integral to CakePHP. I'm only responding (and a little late I might add, sorry :) ) because some of the things written implies we may disagree.

I am always the first to argue that $uses should not be used when there is a "better" association way. In fact that's part of the reason I wrote the tutorial in the first place, and as you can see it doesn't use $uses but instead loads standard cake definitions and even keeps the same definitions format. As close to the 'cake way' as possible and still get the functionality. What would be even more 'cakey' would be to put this into a behavior in some way, but I'm happy as it is.

I also wouldn't suggest having *all* of your associations defined this way. Only the rare ones. The idea being not to hide the definition embedded in a bindModel() string somewhere but right on top of the model where it should be. Calling expects() when you need to load the rare definition is better than the alternative.

I only wish Mariano used different method names, so that they could coexist in harmony - there's really no reason why you wouldn't want to sometimes add, sometimes tweak. Can't we all just get along? ;)

But for those keeping score: my method: approx. 10 lines of code, 0 need for bug fixes. Sorry Mariano ;)
Posted Aug 7, 2007 by Tom OReilly
 

Question

19 hasAndBelongsToMany Problem

My User Model:
--------------

var $hasOne = array (
'Avatar' => array (
'className' => 'Avatar',
'conditions' => '',
'order' => '',
'dependent' => true,
'foreignKey' => 'user_id'
)
);

var $hasAndBelongsToMany = array (
'Friend' => array (
'className' => 'User',
'joinTable' => 'friends',
'foreignKey' => 'user_id',
'associationForeignKey' => 'friend_id'
)
);



My Avatar Model:
----------------
var $belongsTo = array (
'User' => array (
'className' => 'User',
'conditions' => '',
'order' => '',
'foreignKey' => 'user_id'
)
);


In my User Controller I use this:

$this->User->expects('User', 'Friend', 'Avatar');
$results = $this->User->read(null, $id);

I Get The User with the $id and his Avatar and his Friends.

The PROBLEM Is That I Dont Get The Avatar From The Friends :(


So please help me when somebody has an idea....

Thank u very much!
Posted Oct 4, 2007 by Maidi
 

Comment

20 Answer to your problem

I Get The User with the $id and his Avatar and his Friends.

The PROBLEM Is That I Dont Get The Avatar From The Friends :(


So please help me when somebody has an idea....

Thank u very much!

The answer lies in recursion. Since User->Friend->Avatar is one layer deeper than User->Avatar and User->Friend, you simply need to set your recursive value a little higher.
Posted Oct 4, 2007 by Tom OReilly
 

Question

21 Still Problems With hasAndBelongsToMany

$this->User->expects('User.Avatar', 'User.Friend');
$results = $this->User->read(null, '$id);

When I Do This Than The Results Are Ok But Without The Avatar For The Friends.

When I Start To Set The Recursion From $this->User->Friend->recursive = 2; The Problem Is That The Friends Table Is A hasAndBelongsToMany Relationship That Points Back To The User Table. So The Recursion Is Set For The User Table And I get To Many Results For The User And The Friends.




Without Recursion:
------------------

Array
(
[User] => Array
(
[id] => 122
[firstname] => xxx
[lastname] => yyy
[birthday] => 1985-04-04
[created] => 2007-05-25 01:18:08
[modified] => 2007-10-05 05:46:08
)

[Avatar] => Array
(
[id] => 10
[user_id] => 122
[avatarlink] => avatars/46561d30c9c12/Foto 16_2.jpg
)

[Friend] => Array
(
[0] => Array
(
[id] => 129
[firstname] => aaa
[lastname] => bbb
[birthday] => 1979-07-28
[created] => 2007-05-25 16:49:50
[modified] => 2007-07-06 11:57:23
)))


With Recursion ($this->User->Friend->recursive = 2;)
----------------------------------------------------

Array
(
[User] => Array
(
[id] => 122
[firstname] => xxx
[lastname] => yyy
[birthday] => 1985-04-04
[created] => 2007-05-25 01:18:08
[modified] => 2007-10-05 05:53:04
)

[Avatar] => Array
(
[id] => 10
[user_id] => 122
[avatarlink] => avatars/46561d30c9c12/Foto 16_2.jpg
[User] => Array
(
[id] => 122
[firstname] => xxx
[lastname] => yyy
[birthday] => 1985-04-04
[created] => 2007-05-25 01:18:08
[modified] => 2007-10-05 05:53:04
)

)

[Friend] => Array
(
[0] => Array
(
[id] => 129
[firstname] => aaa
[lastname] => bbb
[birthday] => 1979-07-28
[created] => 2007-05-25 16:49:50
[modified] => 2007-07-06 11:57:23
[0] => Array
(
[id] => 119
[firstname] => Maidi
[lastname] => Rocks
[birthday] =>
[created] => 2007-05-25 00:44:05
[modified] => 2007-06-14 00:29:04
)

[Avatar] => Array
(
[id] => 13
[user_id] => 129
[avatarlink] => avatars/myavatar/wandern.jpg
)

So It Will Be Great When You Could Help Me To Solve The Problem.

- Thanks -

Best regards,
Maidi
Posted Oct 4, 2007 by Maidi
 

Comment

22 Still Problems With hasAndBelongsToMany

@Maidi: try this:

PHP Snippet:

<?php $this->User->expects('Avatar''Friend.Friend');
$results $this->User->read(null$id);?>


To everyone else: I will soon be releasing ExpectsBehavior, the 1.2 version of expects with performance improvement, better test coverage, clearer notation and many features.
Posted Nov 2, 2007 by Mariano Iglesias
 

Comment

23 Bindable Behavior released

@all: I've just released the first beta version of Bindable behavior, the Expects version for CakePHP 1.2. It is not only a port for CakePHP 1.2 but also includes a lot of new and exciting features, check it out at:

Bindable Behavior: control your model bindings
Posted Nov 23, 2007 by Mariano Iglesias
 

Question

24 Multiple aliases for same table break behaviour

Mariano,

Excellent work - I've just started with Cake and this code is perfect (personally I think it should be part of the core...)

I have a problem - the following are my associations:
Location belongsTo Dataset // Dataset aliased to MaskDataset
Location hasMany DisplayDatasets
DisplayDataset belongsTo Dataset
Dataset hasOne NamedDataset

I do the following:
$this->Location->recursive = 3;
$this->Location->expects(
'Location.Location',
// 'MaskDataset.MaskDataset',
'DisplayDatasets.DisplayDatasets',
'DisplayDatasets.Dataset',
'DisplayDatasets.Dataset.NamedDataset'
);

This works mostly as required. If I uncomment the commented out line though (retrieving MaskDataset and none of its associations), it prevents any of DisplayDatasets.Dataset's associations - i.e. it doesn't return the NamedDataset.

Am I doing something wrong here or is this broken behaviour?
I'd love to check out Bindable behaviour but can't do anything until 1.2 is stable :(
Posted Dec 12, 2007 by Rob
 

Comment

25 similar troubles

I've been having some issues as well.. seems to be quite close to some of the others posted here.

Faq has many FaqTopic (FaqTopic belongs to Faq)
FaqTopic has many FaqItem (FaqItem belongs to FaqTopic)

doing a findAll with resursive = 2 pulls just what I need.
$this->Faq->recursive = 2;
$this->Faq->findAll();

When I try to do a findAll with expects, I get the oddness:

(i've tried just about every variation I can think of)

$this->Faq->expects('FaqTopic', 'FaqItem');
$this->Faq->expects('FaqTopic.FaqItem');
$this->Faq->expects('FaqTopic', 'FaqTopic.FaqItem')

and so on.. also tried setting
$this->Faq->FaqTopic->expects('FaqItem');
along with everything else.

For now I'll just revert to recursive var, but would love to see if I'm doing something wrong (or if there is a fix) for this lovely 1.1 version of expects();



Posted Feb 7, 2008 by ryan morris