views:

278

answers:

2

Sorry for what is a generic title. I'm not the best at titles.

Anyway the way Cake passes around data (as a hash) is pretty much the reason why I even need to ask this question. If when I passed a variable/obj from the controller to the view, it was an object that I can ask questions to (i.e. $duck->quack() ) rather than having it be an array/dictionary (i.e. $duck['Duck']['quack'] == true) this would be easy.

What I have in my app is a list of items that a user x can own or not. In certain views I display all the items in the database/app (i.e. paginate) and for each item I need to know if the logged in user owns it or not. To answer this question I need to do a query (due to a complicated HABTM relationship), which is done inside the Model. In other words my Item model has a function isOwnedByUser($user_id, $item_id) which is true if it is owned by user. I want to call this function from the view.

Naturally this is violating the MVC framework, but I'm not sure how else to do it. I had four ideas:

Idea 1:

Do this inside a helper:

App:Import('Model','Item');
$item = new Item();
$item->isOwnedByUser($user_id,$item_id);

and call the helper from the view (and pass in the $item_id and $user_id of course). But this REALLY violates the MVC framework.

Idea 2:

Create a action inside item_controller.php and call the action from the view using requestAction(). But I heard that this was extremeley inefficent

Now these two ideas I found when I was looking for a solution to my problem but according to them those two ideas are bad so I came up with two more solutions:

Idea 3:

When returning the paginated data to the view, I can make sure that all items have a 'user_id' key so that I can check the key in the view against the logged in user's id to see if he/she owns item. But this would require a) me to re-write pagination b) very ugly queries especially for certain views (search), c) overall ugliness and slowness. So I decided to abandon this idea

Idea 4:

Every time a view needs to know if an item is owned by user, I'll just pass along another array from the controller that contains ALL the items a user owns and in the view you can just use in_array() to check if user owns said item. Of course the problem to this is obvious: what if the user has lots of items?

In short I'm stuck at this and I have no clue where to go from here and I'd appreciate all help! Thanks!

+3  A: 

I'd combine 3 and 4.

In your action, after you get all the paginated items:

$items = $this->paginate('Item');

Get their IDs and combine that with the user ID to fetch all the user's items.

$itemIds = Set::extract('/Item/id', $items);

$usersItems = $this->Item->User->find
    (
     'all',
     array
     (
      'conditions' => array
      (
       'User.id' = $userId,
       'Item.id' => $itemIds
      ),
      'fields' => array('User.id', 'Item.id')
     )
);

Now you can set the $usersItems in a format you prefer and set both that and $items for the view. That would bring you to your option 4 and in_array(), except it would probably be Set::extract() or Set::check().

Something like this should do it:

if (Set::extract(sprintf('/Item[id=%s]', $itemId), $usersItems))
{
    // user has the item
}

(I wrote this from my memory so... you know what to do if it fails :))

Edit:

Alternatively, you can do something like this (it should be faster than the above, just make sure to move Set::extract() out of the loop):

$usersItemIds = Set::extract('/Item/id', $usersItems);

if (in_array($itemId, $usersItemIds))
{
    // user has the item
}
dr Hannibal Lecter
Sorry it took me nearly a day to reply but this works great! I totally didn't know about the Set utility and now I can refactor a large bunch of code that was inefficiently written! Thanks again!
royrules22
No problem! Set class is one of the best things in cake, and somehow unknown to the general public. So, spread the word! :)
dr Hannibal Lecter
A: 

I know this is an old question, but it seems like maybe the ACL component would be a good option here.

Abba Bryant