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!
-
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 }
royrules22 : 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!dr Hannibal Lecter : No problem! Set class is one of the best things in cake, and somehow unknown to the general public. So, spread the word! :) -
I know this is an old question, but it seems like maybe the ACL component would be a good option here.
0 comments:
Post a Comment