Как я могу сделать это, не нарушая структуру MVC в CakePHP?

Извините за общее название. Я не лучший в титулах.

В любом случае, то, как Cake передает данные (в виде хэша), в значительной степени является причиной, по которой мне даже нужно задать этот вопрос. Если бы я передал переменную/объект из контроллера в представление, это был бы объект, которому я мог бы задавать вопросы (т.е. $duck->quack() ), а не массив/словарь (т.е. $duck[' Duck']['quack'] == true) это было бы легко.

В моем приложении есть список элементов, которыми пользователь x может владеть или не владеть. В некоторых представлениях я отображаю все элементы в базе данных/приложении (т. е. разбиваю на страницы), и для каждого элемента мне нужно знать, владеет ли он вошедшим в систему пользователем или нет. Чтобы ответить на этот вопрос, мне нужно выполнить запрос (из-за сложных отношений HABTM), который выполняется внутри модели. Другими словами, в моей модели Item есть функция isOwnedByUser($user_id, $item_id), которая истинна, если она принадлежит пользователю. Я хочу вызвать эту функцию из представления.

Естественно, это нарушает структуру MVC, но я не знаю, как еще это сделать. У меня было четыре идеи:

Идея 1:

Сделайте это внутри помощника:

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

и вызовите помощника из представления (и, конечно же, передайте $item_id и $user_id). Но это ДЕЙСТВИТЕЛЬНО нарушает структуру MVC.

Идея 2:

Создайте действие внутри item_controller.php и вызовите действие из представления, используя requestAction(). Но я слышал, что это было крайне неэффективно

Эти две идеи я нашел, когда был ищу решение моей проблемы, но, по их мнению, эти две идеи плохие, поэтому я придумал еще два решения:

Идея 3:

При возврате данных с разбивкой на страницы в представление я могу убедиться, что все элементы имеют ключ «user_id», чтобы я мог проверить ключ в представлении по идентификатору вошедшего в систему пользователя, чтобы узнать, владеет ли он/она элементом. Но для этого потребуется: а) мне переписать нумерацию страниц б) очень уродливые запросы, особенно для определенных представлений (поиск), в) общее уродство и медлительность. Поэтому я решил отказаться от этой идеи

Идея 4:

Каждый раз, когда представлению нужно знать, принадлежит ли элемент пользователю, я просто передаю другой массив из контроллера, который содержит ВСЕ элементы, которыми владеет пользователь, и в представлении вы можете просто использовать in_array(), чтобы проверить, принадлежит ли пользователь указанный пункт. Конечно, проблема очевидна: что, если у пользователя много элементов?

Короче говоря, я застрял в этом, и я понятия не имею, куда идти отсюда, и я был бы признателен за любую помощь! Спасибо!


person encee    schedule 11.05.2009    source источник
comment
Я также должен добавить, что то, как Cake обрабатывает отношения HABTM, также отстой. Действительно отстой. Но это не имеет отношения к данному вопросу.   -  person encee    schedule 11.05.2009


Ответы (2)


Я бы объединил 3 и 4.

В вашем действии после того, как вы получите все элементы с разбивкой на страницы:

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

Получите их идентификаторы и объедините их с идентификатором пользователя, чтобы получить все элементы пользователя.

$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')
        )
);

Теперь вы можете установить $usersItems в предпочитаемом вами формате и установить как его, так и $items для представления. Это приведет вас к вашему варианту 4 и in_array(), за исключением того, что это, вероятно, будет Set::extract() или Set::check().

Что-то вроде этого должно сделать это:

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

(Я написал это по памяти, так что... вы знаете, что делать, если не получится :))

Редактировать:

В качестве альтернативы вы можете сделать что-то вроде этого (это должно быть быстрее, чем указано выше, просто убедитесь, что Set::extract() выведено из цикла):

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

if (in_array($itemId, $usersItemIds))
{
    // user has the item
}
person dr Hannibal Lecter    schedule 11.05.2009
comment
Извините, что мне понадобился почти день, чтобы ответить, но это отлично работает! Я совершенно не знал об утилите Set, и теперь я могу рефакторить большой кусок кода, который был написан неэффективно! Спасибо еще раз! - person encee; 12.05.2009
comment
Без проблем! Set class — одна из лучших вещей в торте, и почему-то неизвестная широкой публике. Итак, распространяйте информацию! :) - person dr Hannibal Lecter; 12.05.2009

Я знаю, что это старый вопрос, но, похоже, компонент ACL был бы здесь хорошим вариантом.

person Abba Bryant    schedule 13.11.2009