Маршрут Zend Framework: неизвестное количество параметров - PullRequest
6 голосов
/ 22 марта 2011

Я пытаюсь написать маршрут для уровня категории N глубины. Поэтому обычный URL-адрес категории будет выглядеть так:

http://website/my-category/my-subcategory/my-subcategory-level3/my-subcategory-level4

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

$routeCategory = new Zend_Controller_Router_Route_Regex(
    '(([a-z0-9-]+)/?){1,}',
        array(
            'module' => 'default',
            'controller' => 'index',
            'action' => 'index'
        ),
        array( 1 => 'path'),
        '%s'
);
$router->addRoute('category', $routeCategory);

Я не могу найти способ отправить параметры, соответствующие маршруту, в контроллер. Если у вас есть лучшее решение, я открыт для предложений!

Ответы [ 3 ]

4 голосов
/ 22 марта 2011

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

Проблема:

  • нужен собственный маршрут для категорий уровня Nкак category/subcategory/subsubcategory/...
  • пользовательский маршрут для N категорий + объект как category/subcategory/../page.html
  • сохранить маршрутизацию по умолчанию Zend Framework (для других модулей, например admin)
  • URLсборка с URL-помощником

Решение:

  • создать пользовательский класс маршрута (я использовал Zend_Controller_Router_Route_Regex в качестве отправной точкипоэтому я могу извлечь выгоду из assemble() метода)

Фактический код:

<?php

class App_Controller_Router_Route_Category extends Zend_Controller_Router_Route_Regex
{
    public function match($path, $partial = false)
    {
        if (!$partial) {
            $path = trim(urldecode($path), '/');
        }

        $values = explode('/', $path);
        $res = (count($values) > 0) ? 1 : 0;
        if ($res === 0) {
            return false;
        }

        /**
         * Check if first param is an actual module
         * If it's a module, let the default routing take place
         */
        $modules = array();
        $frontController = Zend_Controller_Front::getInstance();
        foreach ($frontController->getControllerDirectory() as $module => $path) {
            array_push($modules, $module);
        }

        if(in_array($values[0], $modules)) {
            return false;
        }

        if ($partial) {
            $this->setMatchedPath($values[0]);
        }

        $myValues = array();
        $myValues['cmsCategory'] = array();

        // array_filter_key()? Why isn't this in a standard PHP function set yet? :)
        foreach ($values as $i => $value) {
            if (!is_int($i)) {
                unset($values[$i]);
            } else {
                if(preg_match('/.html/', $value)) {
                    $myValues['cmsObject'] = $value;
                } else {
                    array_push($myValues['cmsCategory'], $value);
                }
            }
        }

        $values = $myValues;
        $this->_values = $values;

        $values   = $this->_getMappedValues($values);
        $defaults = $this->_getMappedValues($this->_defaults, false, true);

        $return   = $values + $defaults;

        return $return;
    }

    public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
    {
        if ($this->_reverse === null) {
            require_once 'Zend/Controller/Router/Exception.php';
            throw new Zend_Controller_Router_Exception('Cannot assemble. Reversed route is not specified.');
        }

        $defaultValuesMapped  = $this->_getMappedValues($this->_defaults, true, false);
        $matchedValuesMapped  = $this->_getMappedValues($this->_values, true, false);
        $dataValuesMapped     = $this->_getMappedValues($data, true, false);

        // handle resets, if so requested (By null value) to do so
        if (($resetKeys = array_search(null, $dataValuesMapped, true)) !== false) {
            foreach ((array) $resetKeys as $resetKey) {
                if (isset($matchedValuesMapped[$resetKey])) {
                    unset($matchedValuesMapped[$resetKey]);
                    unset($dataValuesMapped[$resetKey]);
                }
            }
        }

        // merge all the data together, first defaults, then values matched, then supplied
        $mergedData = $defaultValuesMapped;
        $mergedData = $this->_arrayMergeNumericKeys($mergedData, $matchedValuesMapped);
        $mergedData = $this->_arrayMergeNumericKeys($mergedData, $dataValuesMapped);

        /**
         * Default Zend_Controller_Router_Route_Regex foreach insufficient
         * I need to urlencode values if I bump into an array
         */
        if ($encode) {
            foreach ($mergedData as $key => &$value) {
                if(is_array($value)) {
                    foreach($value as $myKey => &$myValue) {
                        $myValue = urlencode($myValue);
                    }
                } else {
                    $value = urlencode($value);
                }
            }
        }

        ksort($mergedData);

        $reverse = array();
        for($i = 0; $i < count($mergedData['cmsCategory']); $i++) {
            array_push($reverse, "%s");
        }
        if(!empty($mergedData['cmsObject'])) {
            array_push($reverse, "%s");
            $mergedData['cmsCategory'][] = $mergedData['cmsObject'];
        }

        $reverse = implode("/", $reverse);
        $return = @vsprintf($reverse, $mergedData['cmsCategory']);

        if ($return === false) {
            require_once 'Zend/Controller/Router/Exception.php';
            throw new Zend_Controller_Router_Exception('Cannot assemble. Too few arguments?');
        }

        return $return;

    }
}

Использование:

Маршрут:

$routeCategory = new App_Controller_Router_Route_Category(
        '',
        array(
            'module' => 'default',
            'controller' => 'index',
            'action' => 'index'
        ),
        array(),
        '%s'
);
$router->addRoute('category', $routeCategory);

URL Helper:

echo "<br>Url: " . $this->_helper->url->url(array(
                            'module' => 'default',
                            'controller' => 'index',
                            'action' => 'index',
                            'cmsCategory' => array(
                                'first-category',
                                'subcategory',
                                'subsubcategory')
                            ), 'category');

Пример вывода в контроллере с помощью getAllParams ()

["cmsCategory"]=>
  array(3) {
    [0]=>
    string(15) "first-category"
    [1]=>
    string(16) "subcategory"
    [2]=>
    string(17) "subsubcategory"
  }
  ["cmsObject"]=>
  string(15) "my-page.html"
  ["module"]=>
  string(7) "default"
  ["controller"]=>
  string(5) "index"
  ["action"]=>
  string(5) "index"
  • Обратите внимание, что cmsObject устанавливается только тогда, когда URL содержит что-то вроде category/subcategory/subsubcategory/my-page.html
2 голосов
/ 22 марта 2011

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

Маршрут:

resources.router.routes.catalog-display.route = /catalog/item/:id
resources.router.routes.catalog-display.defaults.module = catalog
resources.router.routes.catalog-display.defaults.controller = item
resources.router.routes.catalog-display.defaults.action = display

как пример: я использую это для каталога, затем в itemController в displayAction, который я проверяю для $ this-> getRequest () -> getParams (), дело в том, что вы можете (но я думаю, что вы это знаете) прочитать всепараметры, передаваемые способом ключ / значение, например: "site.com/catalog/item/15/kind/hat/color/red/size/M" будет создавать массив в виде: $params['controller'=>'catalog','action'=>'display','id'=>'15','kind'=>'hat','color'=>'red','size'=>'M'];

1 голос
/ 08 февраля 2019

Для любого, кто сталкивается с этим вопросом, используя Zend Framework 2 или Zend Framework 3, существует тип маршрута регулярного выражения , который может (и, вероятно, должен) использоваться для маршрута ОП, в котором существует неизвестное числопараметров, зависящих от количества дочерних категорий.Чтобы использовать, добавьте следующую строку в начало конфигурации вашего роутера:

use Zend\Router\Http\Regex;

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

'categories' => [
    'type' => Regex::class,
        'options' => [
        'regex'    => '/categories(?<sequence>(/[\w\-]+)+)',
        'defaults' => [
            'controller' => ApplicationController\Categories::class,
            'action'     => 'view',
        ],  
        'spec' => '%sequence',
    ],  
],  

Приведенный выше маршрут будет соответствовать следующим маршрутам:

/categories/parent-cat
/categories/parent-cat/child-cat
/categories/parent-cat/sub-child-cat
/categories/parent-cat/sub-sub-child-cat
/categories/parent-cat-2
/categories/parent-cat-2/child-cat

... и т. Д.Последовательность категорий передается контроллеру в параметре sequence.Вы можете обработать этот параметр по своему усмотрению в вашем контроллере.

...