Странное поведение CakePHP с beforeFilter: я не могу установить переменные в представление

Хорошо, это потребует некоторой настройки:

Я работаю над методом использования красивых заголовков сообщений в URL-адресах моего блога на cakePHP.

Например: /blog/post-title-here вместо /blog/view_post/123.

Поскольку я, очевидно, не собираюсь писать новый метод для каждого поста, я пытаюсь быть ловким и использовать обратные вызовы CakePHP, чтобы эмулировать поведение магического метода __call() в PHP 5. Для тех, кто не знает, диспетчер CakePHP проверяет, существует ли метод, и выдает ошибку cakePHP, прежде чем __call() можно будет вызвать в контроллере.

Что я сделал до сих пор:

В интересах полного раскрытия информации (потому что я понятия не имею, почему у меня возникла проблема), у меня есть два маршрута:

Router::connect('/blog/:action/*', array('controller' => 'blog_posts'));
Router::connect('/blog/*', array('controller' => 'blog_posts'));

Они устанавливают псевдоним для BlogPostsController, чтобы мой URL-адрес не выглядел как /blog_posts/action

Затем в BlogPostsController:

public function beforeFilter() {
    parent::beforeFilter();
    if (!in_array($this->params['action'], $this->methods)) {
        $this->setAction('single_post', $this->params['action']);
    }
}
public function single_post($slug = NULL) {
    $post = $this->BlogPost->get_post_by_slug($slug);
    $this->set('post', $post);
    //$this->render('single_post');
}

beforeFilter перехватывает несуществующие действия и передает их моему методу single_post. single_post получает данные из модели и устанавливает переменную $post для представления.

Также есть метод index, который отображает 10 последних сообщений.

Вот сбивающая с толку часть:

Вы заметите, что есть метод $this->render, закомментированный выше.

  1. Когда я не вызываю $this->render('single_post'), представление отображается один раз, но переменная $post не устанавливается.
  2. Когда я делаю вызов $this->render('single_post'), представление отображается с установленной переменной $post, а затем снова отображается с ее не заданной. Таким образом, я получаю два полных макета один за другим в одном и том же документе. Один с содержанием, другой без.

Я пробовал использовать метод с именем single_post и метод с именем __single_post, и у обоих одна и та же проблема. Я бы предпочел, чтобы конечным результатом был метод с именем __single_post, чтобы к нему нельзя было получить прямой доступ с помощью URL-адреса /blog/single_post.

Также

Я еще не закодировал обработку ошибок, когда сообщение не существует (так что, когда люди вводят случайные вещи в URL-адрес, они не получают представление single_post). Я планирую сделать это после того, как выясню эту проблему.


person Stephen    schedule 02.10.2010    source источник


Ответы (1)


Это явно не отвечает на ваш вопрос, но я бы просто отказался от всей сложности, решив проблему, используя только маршруты:

// Whitelist other public actions in BlogPostsController first,
// so they're not caught by the catch-all slug rule.
// This whitelists BlogPostsController::other() and ::actions(), so
// the URLs /blog/other/foo and /blog/actions/bar still work.
Router::connect('/blog/:action/*',
                array('controller' => 'blog_posts'),
                array('action' => 'other|actions'));

// Connect all URLs not matching the above, like /blog/my-frist-post,
// to BlogPostsController::single_post($slug). Optionally use RegEx to
// filter slug format.
Router::connect('/blog/:slug',
                array('controller' => 'blog_posts', 'action' => 'single_post'),
                array('pass' => array('slug') /*, 'slug' => 'regex for slug' */));

Обратите внимание, что вышеуказанные маршруты зависят от исправления ошибки, которое было недавно, на момент написания этой статьи, включено в Cake (см. http://cakephp.lighthouseapp.com/projects/42648/tickets/1197-routing-error-when-using-regex-on-action). Просмотрите историю изменений этого сообщения, чтобы узнать о более совместимом решении.

Что касается прямого доступа к методу single_post: я не буду. Поскольку маршрут /blog/:slug перехватывает все URL-адреса, начинающиеся с /blog/, он перехватывает /blog/single_post и вызывает BlogPostsController::single_post('single_post'). Затем вы попытаетесь найти сообщение с слагом «single_post», которого, вероятно, не будет. В этом случае вы можете выдать ошибку 404:

function single_post($slug) {
    $post = $this->BlogPost->get_post_by_slug($slug);
    if (!$post) {
        $this->cakeError('error404');
    }

    // business as usual here
}

Обработка ошибок: сделано.

person deceze♦    schedule 03.10.2010
comment
Хм... Кажется, это работает не совсем правильно. Я обновлю свой вопрос тем, как я пробовал ваше решение. - person Stephen; 03.10.2010
comment
@ Стивен Хм, только что попробовал сам, действительно странно. Это может быть ошибкой в ​​Cake, но я не уверен. Я ожидал, что это сработает. В любом случае, если вы определяете каждое другое действие отдельно как разные маршруты, все работает нормально, так что это возможный обходной путь. Обновлен мой пост, чтобы отразить это, также включая исправление ошибки в маршруте slug. - person deceze♦; 04.10.2010
comment
@Stephen Альтернативой является использование определенного формата слагов, который вы можете отфильтровать с помощью регулярного выражения, такого как \d+-.+ ('42-my-frist-post') или s-.+ ('s-my-frist-post') в маршруте слагов. Любой URL-адрес, не соответствующий этому регулярному выражению, автоматически попадет на маршрут по умолчанию /controller/action/, поэтому нет необходимости вносить обычные действия в белый список. В любом случае, рад, что это работает. - person deceze♦; 04.10.2010
comment
@Stephen Это была ошибка: cakephp.lighthouseapp.com/projects/42648/tickets/ - person deceze♦; 15.10.2010