Общее, всеобъемлющее действие в Zend Framework ... это можно сделать? - PullRequest
4 голосов
/ 28 января 2009

Эта ситуация возникает из-за того, что кто-то хочет создать свои собственные "страницы" на своем веб-сайте, не вступая в создание соответствующих действий.

Допустим, у них есть URL-адрес, такой как mysite.com/index/books ... они хотят иметь возможность создавать mysite.com/index/booksmore или mysite.com/index/pancakes, но не должны создавать какие-либо действия в Контроллер индекса. Они (не технический специалист, который может делать простые html) в основном хотят создать простую статическую страницу без использования действия.

Как будто в контроллере индекса будет какое-то общее действие, которое обрабатывает запросы на несуществующее действие. Как ты это делаешь или это вообще возможно?

edit: Одной из проблем использования __call является отсутствие файла представления. Отсутствие действия становится спорным, но теперь вам приходится иметь дело с отсутствующим файлом вида. Фреймворк сгенерирует исключение, если не сможет найти его (хотя, если бы был способ заставить его перенаправить на 404 отсутствующий файл представления, __call был бы выполним.)

Ответы [ 9 ]

10 голосов
/ 25 января 2012

Использование магического метода __call работает нормально, все, что вам нужно сделать, это проверить, существует ли файл представления, и выдать правильное исключение (или сделать что-нибудь еще), если нет.

public function __call($methodName, $params)
{
    // An action method is called
    if ('Action' == substr($methodName, -6)) {
        $action = substr($methodName, 0, -6);

        // We want to render scripts in the index directory, right?
        $script = 'index/' . $action . '.' . $this->viewSuffix;

        // Script file does not exist, throw exception that will render /error/error.phtml in 404 context
        if (false === $this->view->getScriptPath($script)) {
            require_once 'Zend/Controller/Action/Exception.php';
            throw new Zend_Controller_Action_Exception(
                sprintf('Page "%s" does not exist.', $action), 404);
        }

        $this->renderScript($script);
    }

    // no action is called? Let the parent __call handle things.
    else {
        parent::__call($methodName, $params);
    }
}
3 голосов
/ 28 января 2009

надо играть с роутером http://framework.zend.com/manual/en/zend.controller.router.html

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

new Zend_Controller_Router_Route('index/*', 
array('controller' => 'index', 'action' => 'custom', 'module'=>'index') 

В вашей функции customAction просто получите параметры и отобразите правильный блок. Я не пробовал, так что, возможно, вам придется немного взломать код

2 голосов
/ 25 февраля 2012

Я реализовал универсальный метод, переопределив метод отправки и обработав исключение, которое выдается, когда действие не найдено:

public function dispatch($action)
{
    try {
        parent::dispatch($action);
    }
    catch (Zend_Controller_Action_Exception $e) {
        $uristub = $this->getRequest()->getActionName();
        $this->getRequest()->setActionName('index');
        $this->getRequest()->setParam('uristub', $uristub);
        parent::dispatch('indexAction');
    }
}
2 голосов
/ 31 августа 2009

Мне нужно было, чтобы существующий модуль / контроллер / действия работали в приложении Zend Framework как обычно, но затем нужно было использовать универсальный маршрут, который отправлял что-либо неизвестное PageController, которое могло бы выбрать указанные пользователем URL-адреса из таблицы базы данных и отобразить страницу. , Я не хотел иметь имя контроллера перед указанными пользователем URL. Я хотел, чтобы / my / custom / url not / page / my / custom / url прошел через PageController. Поэтому ни одно из перечисленных выше решений не помогло мне.

Я закончил расширением Zend_Controller_Router_Route_Module: использовал почти все поведение по умолчанию и просто немного подправил имя контроллера, чтобы, если файл контроллера существовал, мы направлялись к нему как обычно. Если он не существует, то URL-адрес должен быть странным, поэтому он отправляется в PageController с целым URL-адресом в качестве параметра.

class UDC_Controller_Router_Route_Catchall extends Zend_Controller_Router_Route_Module
{
    private $_catchallController = 'page';
    private $_catchallAction     = 'index';
    private $_paramName          = 'name';

    //-------------------------------------------------------------------------
    /*! \brief takes most of the default behaviour from Zend_Controller_Router_Route_Module
        with the following changes:
        - if the path includes a valid module, then use it
        - if the path includes a valid controller (file_exists) then use that
            - otherwise use the catchall
    */
    public function match($path, $partial = false)
    {
        $this->_setRequestKeys();

        $values = array();
        $params = array();

        if (!$partial) {
            $path = trim($path, self::URI_DELIMITER);
        } else {
            $matchedPath = $path;
        }

        if ($path != '') {
            $path = explode(self::URI_DELIMITER, $path);

            if ($this->_dispatcher && $this->_dispatcher->isValidModule($path[0])) {
                $values[$this->_moduleKey] = array_shift($path);
                $this->_moduleValid = true;
            }

            if (count($path) && !empty($path[0])) {
                $module = $this->_moduleValid ? $values[$this->_moduleKey] : $this->_defaults[$this->_moduleKey];
                $file = $this->_dispatcher->getControllerDirectory( $module ) . '/' . $this->_dispatcher->formatControllerName( $path[0] ) . '.php'; 
                if (file_exists( $file ))
                {
                    $values[$this->_controllerKey] = array_shift($path);
                }
                else
                {
                    $values[$this->_controllerKey] = $this->_catchallController;
                    $values[$this->_actionKey] = $this->_catchallAction;
                    $params[$this->_paramName] = join( self::URI_DELIMITER, $path );
                    $path = array();
                }
            }

            if (count($path) && !empty($path[0])) {
                $values[$this->_actionKey] = array_shift($path);
            }

            if ($numSegs = count($path)) {
                for ($i = 0; $i < $numSegs; $i = $i + 2) {
                    $key = urldecode($path[$i]);
                    $val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null;
                    $params[$key] = (isset($params[$key]) ? (array_merge((array) $params[$key], array($val))): $val);
                }
            }
        }

        if ($partial) {
            $this->setMatchedPath($matchedPath);
        }

        $this->_values = $values + $params;

        return $this->_values + $this->_defaults;
    }
}

Так что мой MemberController будет нормально работать как / member / login, / member / Preferences и т. Д., И другие контроллеры могут быть добавлены по желанию. ErrorController по-прежнему необходим: он фиксирует недопустимые действия на существующих контроллерах.

2 голосов
/ 30 января 2009

Если вы хотите использовать метод _call () в gabriel1836, вы сможете отключить макет и просмотр, а затем отобразить все, что захотите.

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
1 голос
/ 09 июня 2009

Для дальнейшего использования, опираясь на мысли gabriel1836 и ejunker, я откопал вариант, который больше подходит к сути (и поддерживает парадигму MVC). Кроме того, имеет смысл читать «использовать специализированное представление», чем «не использовать любое представление».


// 1. Catch & process overloaded actions.
public function __call($name, $arguments)
{
    // 2. Provide an appropriate renderer.
    $this->_helper->viewRenderer->setRender('overload');
    // 3. Bonus: give your view script a clue about what "action" was requested.
    $this->view->action = $this->getFrontController()->getRequest()->getActionName();
}
1 голос
/ 29 января 2009

предложением Стунти было то, как я поступил с этим. Мое конкретное решение заключается в следующем (это использует indexAction () того контроллера, который вы укажете. В моем случае каждое действие использовало indexAction и извлекало содержимое из базы данных на основе URL):

  1. Получить экземпляр маршрутизатора (все в вашем файле начальной загрузки, кстати):

    $ router = $ frontController-> getRouter ();

  2. Создание пользовательского маршрута:

    $ router-> addRoute ('controllername', новый Zend_Controller_Router_Route ('controllername / *', массив ('controller' => 'controllername')));

  3. Передать новый маршрут на фронт-контроллер:

    $ frontController-> setRouter ($ маршрутизатор);

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

1 голос
/ 28 января 2009

Вы можете использовать волшебную функцию __ call () . Например:

public function __call($name, $arguments) 
{
     // Render Simple HTML View
}
0 голосов
/ 26 ноября 2010

@ Стив, как указано выше - ваше решение звучит идеально для меня, но я не уверен, как вы внесли его в бутстрап?

...