首頁 > php教程 > PHP开发 > Zend Framework教學之路由功能Zend_Controller_Router詳解

Zend Framework教學之路由功能Zend_Controller_Router詳解

高洛峰
發布: 2017-01-03 13:10:20
原創
1261 人瀏覽過

本文實例講述了Zend Framework教學之路由功能Zend_Controller_Router用法。分享給大家供大家參考,具體如下:

Zend Framework的路由提供了兩個主要功能路由和建立路由。

Zend_Controller_Router的Route類別和對應Route目錄下的類別定義常見的路由操作。

介面Zend_Controller_Router_Interface,類別Zend_Controller_Router_Abstract和Zend_Controller_Router_Rewrite完成了基本的路由,建立路由,刪除路由的功能。

└── Router
    ├── Abstract.php
    ├── Exception.php
    ├── Inter  │   ├── Abstract.php
    │   ├ ── Chain.php
    │   ├── Hostname.php
    │   ├── Interface.php
    │  ├── Interface.php
    │   └── Static.php
    └── Route .php

Zend_Controller_Router路由功能的實作

Zend_Controller_Router_Interface

<?php
interface Zend_Controller_Router_Interface
{
  /**
   * Processes a request and sets its controller and action. If
   * no route was possible, an exception is thrown.
   *
   * @param Zend_Controller_Request_Abstract
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Request_Abstract|boolean
   */
  public function route(Zend_Controller_Request_Abstract $dispatcher);
  /**
   * Generates a URL path that can be used in URL creation, redirection, etc.
   *
   * May be passed user params to override ones from URI, Request or even defaults.
   * If passed parameter has a value of null, it&#39;s URL variable will be reset to
   * default.
   *
   * If null is passed as a route name assemble will use the current Route or &#39;default&#39;
   * if current is not yet set.
   *
   * Reset is used to signal that all parameters should be reset to it&#39;s defaults.
   * Ignoring all URL specified values. User specified params still get precedence.
   *
   * Encode tells to url encode resulting path parts.
   *
   * @param array $userParams Options passed by a user used to override parameters
   * @param mixed $name The name of a Route to use
   * @param bool $reset Whether to reset to the route defaults ignoring URL params
   * @param bool $encode Tells to encode URL parts on output
   * @throws Zend_Controller_Router_Exception
   * @return string Resulting URL path
   */
  public function assemble($userParams, $name = null, $reset = false, $encode = true);
  /**
   * Retrieve Front Controller
   *
   * @return Zend_Controller_Front
   */
  public function getFrontController();
  /**
   * Set Front Controller
   *
   * @param Zend_Controller_Front $controller
   * @return Zend_Controller_Router_Interface
   */
  public function setFrontController(Zend_Controller_Front $controller);
  /**
   * Add or modify a parameter with which to instantiate any helper objects
   *
   * @param string $name
   * @param mixed $param
   * @return Zend_Controller_Router_Interface
   */
  public function setParam($name, $value);
  /**
   * Set an array of a parameters to pass to helper object constructors
   *
   * @param array $params
   * @return Zend_Controller_Router_Interface
   */
  public function setParams(array $params);
  /**
   * Retrieve a single parameter from the controller parameter stack
   *
   * @param string $name
   * @return mixed
   */
  public function getParam($name);
  /**
   * Retrieve the parameters to pass to helper object constructors
   *
   * @return array
   */
  public function getParams();
  /**
   * Clear the controller parameter stack
   *
   * By default, clears all parameters. If a parameter name is given, clears
   * only that parameter; if an array of parameter names is provided, clears
   * each.
   *
   * @param null|string|array single key or array of keys for params to clear
   * @return Zend_Controller_Router_Interface
   */
  public function clearParams($name = null);
}
登入後複製

   

🎇 _Router_Rewrite

<?php
/** Zend_Controller_Router_Interface */
require_once &#39;Zend/Controller/Router/Interface.php&#39;;
abstract class Zend_Controller_Router_Abstract implements Zend_Controller_Router_Interface
{
  /**
   * URI delimiter
   */
  const URI_DELIMITER = &#39;/&#39;;
  /**
   * Front controller instance
   * @var Zend_Controller_Front
   */
  protected $_frontController;
  /**
   * Array of invocation parameters to use when instantiating action
   * controllers
   * @var array
   */
  protected $_invokeParams = array();
  /**
   * Constructor
   *
   * @param array $params
   * @return void
   */
  public function __construct(array $params = array())
  {
    $this->setParams($params);
  }
  /**
   * Add or modify a parameter to use when instantiating an action controller
   *
   * @param string $name
   * @param mixed $value
   * @return Zend_Controller_Router
   */
  public function setParam($name, $value)
  {
    $name = (string) $name;
    $this->_invokeParams[$name] = $value;
    return $this;
  }
  /**
   * Set parameters to pass to action controller constructors
   *
   * @param array $params
   * @return Zend_Controller_Router
   */
  public function setParams(array $params)
  {
    $this->_invokeParams = array_merge($this->_invokeParams, $params);
    return $this;
  }
  /**
   * Retrieve a single parameter from the controller parameter stack
   *
   * @param string $name
   * @return mixed
   */
  public function getParam($name)
  {
    if(isset($this->_invokeParams[$name])) {
      return $this->_invokeParams[$name];
    }
    return null;
  }
  /**
   * Retrieve action controller instantiation parameters
   *
   * @return array
   */
  public function getParams()
  {
    return $this->_invokeParams;
  }
  /**
   * Clear the controller parameter stack
   *
   * By default, clears all parameters. If a parameter name is given, clears
   * only that parameter; if an array of parameter names is provided, clears
   * each.
   *
   * @param null|string|array single key or array of keys for params to clear
   * @return Zend_Controller_Router
   */
  public function clearParams($name = null)
  {
    if (null === $name) {
      $this->_invokeParams = array();
    } elseif (is_string($name) && isset($this->_invokeParams[$name])) {
      unset($this->_invokeParams[$name]);
    } elseif (is_array($name)) {
      foreach ($name as $key) {
        if (is_string($key) && isset($this->_invokeParams[$key])) {
          unset($this->_invokeParams[$key]);
        }
      }
    }
    return $this;
  }
  /**
   * Retrieve Front Controller
   *
   * @return Zend_Controller_Front
   */
  public function getFrontController()
  {
    // Used cache version if found
    if (null !== $this->_frontController) {
      return $this->_frontController;
    }
    require_once &#39;Zend/Controller/Front.php&#39;;
    $this->_frontController = Zend_Controller_Front::getInstance();
    return $this->_frontController;
  }
  /**
   * Set Front Controller
   *
   * @param Zend_Controller_Front $controller
   * @return Zend_Controller_Router_Interface
   */
  public function setFrontController(Zend_Controller_Front $controller)
  {
    $this->_frontController = $controller;
    return $this;
  }
}
登入後複製

   

新增路由的操作方法

public function addRoute($name, Zend_Controller_Router_Route_Interface $route)

public function addRoutes($routes)

<?php
/** Zend_Controller_Router_Abstract */
require_once &#39;Zend/Controller/Router/Abstract.php&#39;;
/** Zend_Controller_Router_Route */
require_once &#39;Zend/Controller/Router/Route.php&#39;;
class Zend_Controller_Router_Rewrite extends Zend_Controller_Router_Abstract
{
  /**
   * Whether or not to use default routes
   *
   * @var boolean
   */
  protected $_useDefaultRoutes = true;
  /**
   * Array of routes to match against
   *
   * @var array
   */
  protected $_routes = array();
  /**
   * Currently matched route
   *
   * @var Zend_Controller_Router_Route_Interface
   */
  protected $_currentRoute = null;
  /**
   * Global parameters given to all routes
   *
   * @var array
   */
  protected $_globalParams = array();
  /**
   * Separator to use with chain names
   *
   * @var string
   */
  protected $_chainNameSeparator = &#39;-&#39;;
  /**
   * Determines if request parameters should be used as global parameters
   * inside this router.
   *
   * @var boolean
   */
  protected $_useCurrentParamsAsGlobal = false;
  /**
   * Add default routes which are used to mimic basic router behaviour
   *
   * @return Zend_Controller_Router_Rewrite
   */
  public function addDefaultRoutes()
  {
    if (!$this->hasRoute(&#39;default&#39;)) {
      $dispatcher = $this->getFrontController()->getDispatcher();
      $request = $this->getFrontController()->getRequest();
      require_once &#39;Zend/Controller/Router/Route/Module.php&#39;;
      $compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request);
      $this->_routes = array(&#39;default&#39; => $compat) + $this->_routes;
    }
    return $this;
  }
  /**
   * Add route to the route chain
   *
   * If route contains method setRequest(), it is initialized with a request object
   *
   * @param string                 $name    Name of the route
   * @param Zend_Controller_Router_Route_Interface $route   Instance of the route
   * @return Zend_Controller_Router_Rewrite
   */
  public function addRoute($name, Zend_Controller_Router_Route_Interface $route)
  {
    if (method_exists($route, &#39;setRequest&#39;)) {
      $route->setRequest($this->getFrontController()->getRequest());
    }
    $this->_routes[$name] = $route;
    return $this;
  }
  /**
   * Add routes to the route chain
   *
   * @param array $routes Array of routes with names as keys and routes as values
   * @return Zend_Controller_Router_Rewrite
   */
  public function addRoutes($routes) {
    foreach ($routes as $name => $route) {
      $this->addRoute($name, $route);
    }
    return $this;
  }
  /**
   * Create routes out of Zend_Config configuration
   *
   * Example INI:
   * routes.archive.route = "archive/:year/*"
   * routes.archive.defaults.controller = archive
   * routes.archive.defaults.action = show
   * routes.archive.defaults.year = 2000
   * routes.archive.reqs.year = "\d+"
   *
   * routes.news.type = "Zend_Controller_Router_Route_Static"
   * routes.news.route = "news"
   * routes.news.defaults.controller = "news"
   * routes.news.defaults.action = "list"
   *
   * And finally after you have created a Zend_Config with above ini:
   * $router = new Zend_Controller_Router_Rewrite();
   * $router->addConfig($config, &#39;routes&#39;);
   *
   * @param Zend_Config $config Configuration object
   * @param string   $section Name of the config section containing route&#39;s definitions
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Rewrite
   */
  public function addConfig(Zend_Config $config, $section = null)
  {
    if ($section !== null) {
      if ($config->{$section} === null) {
        require_once &#39;Zend/Controller/Router/Exception.php&#39;;
        throw new Zend_Controller_Router_Exception("No route configuration in section &#39;{$section}&#39;");
      }
      $config = $config->{$section};
    }
    foreach ($config as $name => $info) {
      $route = $this->_getRouteFromConfig($info);
      if ($route instanceof Zend_Controller_Router_Route_Chain) {
        if (!isset($info->chain)) {
          require_once &#39;Zend/Controller/Router/Exception.php&#39;;
          throw new Zend_Controller_Router_Exception("No chain defined");
        }
        if ($info->chain instanceof Zend_Config) {
          $childRouteNames = $info->chain;
        } else {
          $childRouteNames = explode(&#39;,&#39;, $info->chain);
        }
        foreach ($childRouteNames as $childRouteName) {
          $childRoute = $this->getRoute(trim($childRouteName));
          $route->chain($childRoute);
        }
        $this->addRoute($name, $route);
      } elseif (isset($info->chains) && $info->chains instanceof Zend_Config) {
        $this->_addChainRoutesFromConfig($name, $route, $info->chains);
      } else {
        $this->addRoute($name, $route);
      }
    }
    return $this;
  }
  /**
   * Get a route frm a config instance
   *
   * @param Zend_Config $info
   * @return Zend_Controller_Router_Route_Interface
   */
  protected function _getRouteFromConfig(Zend_Config $info)
  {
    $class = (isset($info->type)) ? $info->type : &#39;Zend_Controller_Router_Route&#39;;
    if (!class_exists($class)) {
      require_once &#39;Zend/Loader.php&#39;;
      Zend_Loader::loadClass($class);
    }
    $route = call_user_func(array($class, &#39;getInstance&#39;), $info);
    if (isset($info->abstract) && $info->abstract && method_exists($route, &#39;isAbstract&#39;)) {
      $route->isAbstract(true);
    }
    return $route;
  }
  /**
   * Add chain routes from a config route
   *
   * @param string                 $name
   * @param Zend_Controller_Router_Route_Interface $route
   * @param Zend_Config              $childRoutesInfo
   * @return void
   */
  protected function _addChainRoutesFromConfig($name,
                         Zend_Controller_Router_Route_Interface $route,
                         Zend_Config $childRoutesInfo)
  {
    foreach ($childRoutesInfo as $childRouteName => $childRouteInfo) {
      if (is_string($childRouteInfo)) {
        $childRouteName = $childRouteInfo;
        $childRoute   = $this->getRoute($childRouteName);
      } else {
        $childRoute = $this->_getRouteFromConfig($childRouteInfo);
      }
      if ($route instanceof Zend_Controller_Router_Route_Chain) {
        $chainRoute = clone $route;
        $chainRoute->chain($childRoute);
      } else {
        $chainRoute = $route->chain($childRoute);
      }
      $chainName = $name . $this->_chainNameSeparator . $childRouteName;
      if (isset($childRouteInfo->chains)) {
        $this->_addChainRoutesFromConfig($chainName, $chainRoute, $childRouteInfo->chains);
      } else {
        $this->addRoute($chainName, $chainRoute);
      }
    }
  }
  /**
   * Remove a route from the route chain
   *
   * @param string $name Name of the route
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Rewrite
   */
  public function removeRoute($name)
  {
    if (!isset($this->_routes[$name])) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception("Route $name is not defined");
    }
    unset($this->_routes[$name]);
    return $this;
  }
  /**
   * Remove all standard default routes
   *
   * @param Zend_Controller_Router_Route_Interface Route
   * @return Zend_Controller_Router_Rewrite
   */
  public function removeDefaultRoutes()
  {
    $this->_useDefaultRoutes = false;
    return $this;
  }
  /**
   * Check if named route exists
   *
   * @param string $name Name of the route
   * @return boolean
   */
  public function hasRoute($name)
  {
    return isset($this->_routes[$name]);
  }
  /**
   * Retrieve a named route
   *
   * @param string $name Name of the route
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getRoute($name)
  {
    if (!isset($this->_routes[$name])) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception("Route $name is not defined");
    }
    return $this->_routes[$name];
  }
  /**
   * Retrieve a currently matched route
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getCurrentRoute()
  {
    if (!isset($this->_currentRoute)) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception("Current route is not defined");
    }
    return $this->getRoute($this->_currentRoute);
  }
  /**
   * Retrieve a name of currently matched route
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getCurrentRouteName()
  {
    if (!isset($this->_currentRoute)) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception("Current route is not defined");
    }
    return $this->_currentRoute;
  }
  /**
   * Retrieve an array of routes added to the route chain
   *
   * @return array All of the defined routes
   */
  public function getRoutes()
  {
    return $this->_routes;
  }
  /**
   * Find a matching route to the current PATH_INFO and inject
   * returning values to the Request object.
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Request_Abstract Request object
   */
  public function route(Zend_Controller_Request_Abstract $request)
  {
    if (!$request instanceof Zend_Controller_Request_Http) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception(&#39;Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object&#39;);
    }
    if ($this->_useDefaultRoutes) {
      $this->addDefaultRoutes();
    }
    // Find the matching route
    $routeMatched = false;
    foreach (array_reverse($this->_routes, true) as $name => $route) {
      // TODO: Should be an interface method. Hack for 1.0 BC
      if (method_exists($route, &#39;isAbstract&#39;) && $route->isAbstract()) {
        continue;
      }
      // TODO: Should be an interface method. Hack for 1.0 BC
      if (!method_exists($route, &#39;getVersion&#39;) || $route->getVersion() == 1) {
        $match = $request->getPathInfo();
      } else {
        $match = $request;
      }
      if ($params = $route->match($match)) {
        $this->_setRequestParams($request, $params);
        $this->_currentRoute = $name;
        $routeMatched    = true;
        break;
      }
    }
     if (!$routeMatched) {
       require_once &#39;Zend/Controller/Router/Exception.php&#39;;
       throw new Zend_Controller_Router_Exception(&#39;No route matched the request&#39;, 404);
     }
    if($this->_useCurrentParamsAsGlobal) {
      $params = $request->getParams();
      foreach($params as $param => $value) {
        $this->setGlobalParam($param, $value);
      }
    }
    return $request;
  }
  protected function _setRequestParams($request, $params)
  {
    foreach ($params as $param => $value) {
      $request->setParam($param, $value);
      if ($param === $request->getModuleKey()) {
        $request->setModuleName($value);
      }
      if ($param === $request->getControllerKey()) {
        $request->setControllerName($value);
      }
      if ($param === $request->getActionKey()) {
        $request->setActionName($value);
      }
    }
  }
  /**
   * Generates a URL path that can be used in URL creation, redirection, etc.
   *
   * @param array $userParams Options passed by a user used to override parameters
   * @param mixed $name The name of a Route to use
   * @param bool $reset Whether to reset to the route defaults ignoring URL params
   * @param bool $encode Tells to encode URL parts on output
   * @throws Zend_Controller_Router_Exception
   * @return string Resulting absolute URL path
   */
  public function assemble($userParams, $name = null, $reset = false, $encode = true)
  {
    if (!is_array($userParams)) {
      require_once &#39;Zend/Controller/Router/Exception.php&#39;;
      throw new Zend_Controller_Router_Exception(&#39;userParams must be an array&#39;);
    }
    if ($name == null) {
      try {
        $name = $this->getCurrentRouteName();
      } catch (Zend_Controller_Router_Exception $e) {
        $name = &#39;default&#39;;
      }
    }
    // Use UNION (+) in order to preserve numeric keys
    $params = $userParams + $this->_globalParams;
    $route = $this->getRoute($name);
    $url  = $route->assemble($params, $reset, $encode);
    if (!preg_match(&#39;|^[a-z]+://|&#39;, $url)) {
      $url = rtrim($this->getFrontController()->getBaseUrl(), self::URI_DELIMITER) . self::URI_DELIMITER . $url;
    }
    return $url;
  }
  /**
   * Set a global parameter
   *
   * @param string $name
   * @param mixed $value
   * @return Zend_Controller_Router_Rewrite
   */
  public function setGlobalParam($name, $value)
  {
    $this->_globalParams[$name] = $value;
    return $this;
  }
  /**
   * Set the separator to use with chain names
   *
   * @param string $separator The separator to use
   * @return Zend_Controller_Router_Rewrite
   */
  public function setChainNameSeparator($separator) {
    $this->_chainNameSeparator = $separator;
    return $this;
  }
  /**
   * Get the separator to use for chain names
   *
   * @return string
   */
  public function getChainNameSeparator() {
    return $this->_chainNameSeparator;
  }
  /**
   * Determines/returns whether to use the request parameters as global parameters.
   *
   * @param boolean|null $use
   *      Null/unset when you want to retrieve the current state.
   *      True when request parameters should be global, false otherwise
   * @return boolean|Zend_Controller_Router_Rewrite
   *       Returns a boolean if first param isn&#39;t set, returns an
   *       instance of Zend_Controller_Router_Rewrite otherwise.
   *
   */
  public function useRequestParametersAsGlobal($use = null) {
    if($use === null) {
      return $this->_useCurrentParamsAsGlobal;
    }
    $this->_useCurrentParamsAsGlobal = (bool) $use;
    return $this;
  }
}
登入後複製

   

addRoute的第一個參數是路由名稱。第二個參數是路由自己。路由名最普通的用法是透過Zend_View_Url助手的方法:

$router = $ctrl->getRouter(); // returns a rewrite router by default
$router->addRoute(&#39;user&#39;,
         new Zend_Controller_Router_Route(&#39;user/:username&#39;));
登入後複製

   


它將導致在href: user/martel.

路由是一個簡單的過程,這個過程通過所有提供的路由和匹配它的目前請求的URI定義來迭代。當一個正匹配被發現,變數值從路由實例返回並注入到Zend_Controller_Request物件以備將來在派遣器和使用者建立的控制器中使用。如果是負匹配,在鏈中的下個路由被檢查。

Note: 倒序匹配

用倒序來匹配路由確保最通用的路由被先定義。

Note: 傳回的值

從路由傳回的值來自於URL參數或用於定義的缺省值。這些變數以後可透過Zend_Controller_Request::getParam() 或 Zend_Controller_Action::_getParam() 方法來存取。

有三個特殊的變數可用於你的路由-'module'、 'controller' 和 'action'。這些特殊的變數被Zend_Controller_Dispatcher用來找出控制器和動作然後派遣過去。

Note: 特殊變數

如果你選擇透過 setControllerKey 和 setActionKey方法的方式來改變預設值,這些特殊變數的名字可能會不同。

缺省路由

Zend_Controller_Router_Rewrite 和預設路由一起預先配置,它將以controller/action的形式匹配URIs。另外,模組名稱可以被指定為第一個路徑參數,允許這種module/controller/action形式的URIs。最後,它也將缺省地匹配任何另外的追加到URI的參數-controller/action/var1/value1/var2/value2。

一些路由如何匹配的例子:

"<?= $this->url(array(&#39;username&#39; => &#39;martel&#39;), &#39;user&#39;) ?>">Martel</a>
登入後複製

   

缺省路由是儲存在RewriteRouter名(index)為'default'的簡單的Zend_Controller_Router_Route_Module物件。它被創建多多少少像下面這樣:

// Assuming the following:
$ctrl->setControllerDirectory(
  array(
    &#39;default&#39; => &#39;/path/to/default/controllers&#39;,
    &#39;news&#39;  => &#39;/path/to/news/controllers&#39;,
    &#39;blog&#39;  => &#39;/path/to/blog/controllers&#39;
  )
);
Module only:
http://example/news
  module == news
Invalid module maps to controller name:
http://example/foo
  controller == foo
Module + controller:
http://example/blog/archive
  module   == blog
  controller == archive
Module + controller + action:
http://example/blog/archive/list
  module   == blog
  controller == archive
  action   == list
Module + controller + action + params:
http://example/blog/archive/list/sort/alpha/date/desc
  module   == blog
  controller == archive
  action   == list
  sort    == alpha
  date    == desc
登入後複製

   

如果你不想這個特別的缺省路由在你的路由計劃中,你可以重寫你自己的'缺省'路由(例如,把它儲存在'default'名下)或用removeDefaultRoutes()完全清除它:

$compat = new Zend_Controller_Router_Route_Module(array(),
                         $dispatcher,
                         $request);
$this->addRoute(&#39;default&#39;, $compat);
登入後複製

   

為了增加路由的靈活性,方便自訂新的路由類型,Zend_Controller_RouterupTurfaced,Interface_Interface_Router_Ro的類別方法即可定義路由類型,為開發提供了便利。

Zend_Controller_Router的路由類型

Zend_Controller_Router預設提供了以下路由類型,分別為:

Zend_Controller_Router_Route

Zend_Controller_Router_Roost_Staticic;

Zend_Controller_Router_Route_Module

Zend_Controller_Router_Route_Chain

Zend_Controller_Router_Route

Zend_Controller_Router_Route是標準的框架路由。它結合了靈活路由定義的易用性。每個路由包含了基本的URL映射(靜態的和動態的部分(變數))並且可以被預設地初始化,也可以根據不同的要求初始化。

让我们想象一下我们假设的应用程序将需要一些广域内容作者的信息页面。我们想能够把浏览器指向http://domain.com/author/martel去看一个叫"martel"的信息。有这样功能的路由看起来是这样的:

$route = new Zend_Controller_Router_Route(
  &#39;author/:username&#39;,
  array(
    &#39;controller&#39; => &#39;profile&#39;,
    &#39;action&#39;   => &#39;userinfo&#39;
  )
);
$router->addRoute(&#39;user&#39;, $route);
登入後複製

在Zend_Controller_Router_Route里构造函数的第一个参数是路由的定义,它将匹配一个URL。路由定义包含静态的和动态部分,它们由正斜杠('/')符分开。静态部分只是简单的字符:author。动态部分,被叫做变量,用预设的冒号来标记变量名::username。

Note: 字符的的用法

当前的实现允许你使用任何字符(正斜杠除外)作为变量标识符,但强烈建议只使用PHP使用的变量标识符。将来的实现也许会改变这个行为,它可能会导致在你的代码里有隐藏的bugs。

当你把浏览器指向http://domain.com/author/martel这个例子的路由应该被匹配,它所有的变量将被注入到Zend_Controller_Request对象并在ProfileController可访问。由这个例子返回的变量可能会被表示为如下键和值配对的数组:

$values = array(
  &#39;username&#39;  => &#39;martel&#39;,
  &#39;controller&#39; => &#39;profile&#39;,
  &#39;action&#39;   => &#39;userinfo&#39;
);
登入後複製

稍后,基于这些值,Zend_Controller_Dispatcher_Standard应该调用ProfileController类(在缺省模块中)中的userinfoAction()方法。你将依靠Zend_Controller_Action::_getParam()或者Zend_Controller_Request::getParam()方法能够访问所有的变量:

public function userinfoAction()
{
  $request = $this->getRequest();
  $username = $request->getParam(&#39;username&#39;);
  $username = $this->_getParam(&#39;username&#39;);
}
登入後複製

路由定义可以包一个额外的特别字符-通配符-表示为'*'号。它被用来取得参数,和缺省模块路由类似(在URI中定义的var=>value)。下面的路由多多少少地模仿了模块路由的行为:

$route = new Zend_Controller_Router_Route(
  &#39;:module/:controller/:action/*&#39;,
  array(&#39;module&#39; => &#39;default&#39;)
);
$router->addRoute(&#39;default&#39;, $route);
登入後複製

变量缺省

在路由中每个变量可以有一个缺省值,这就是Zend_Controller_Router_Route中构造函数使用的第二个变量。这个参数是一个数组,在数组中键表示变量名,值就是期望的缺省值:

$route = new Zend_Controller_Router_Route(
  &#39;archive/:year&#39;,
  array(&#39;year&#39; => 2006)
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

上述路由将匹配象http://domain.com/archive/2005和http://example.com/archive的URLs。对于后者变量year将有一个初始的缺省值为2006。

这个例子将导致注入一个year变量给请求对象。应为没有路由信息出现(没有控制器和动作参数被定义),应用程序将被派遣给缺省的控制器和动作方法(它们都在Zend_Controller_Dispatcher_Abstract被定义)。为使它更可用,你必须提供一个有效的控制器和动作作为路由的缺省值:

$route = new Zend_Controller_Router_Route(
  &#39;archive/:year&#39;,
  array(
    &#39;year&#39;    => 2006,
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39;   => &#39;show&#39;
  )
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

这个路由将导致派遣给ArchiveController类的showAction()方法。

变量请求

当变量请求被设定,第三个参数可以加给Zend_Controller_Router_Route的构造函数。这些被定义为正则表达式的一部分:

$route = new Zend_Controller_Router_Route(
  &#39;archive/:year&#39;,
  array(
    &#39;year&#39;    => 2006,
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39;   => &#39;show&#39;
  ),
  array(&#39;year&#39; => &#39;\d+&#39;)
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

用上述定义的路由,路由器仅当year变量包含数字数据将匹配它,例如http://domain.com/archive/2345。象http://example.com/archive/test这样的URL将不被匹配并且控制将被传递给在链中的下一个路由。

主机名路由

你也可以使用主机名做路由匹配。对简单的匹配使用静态主机名选项:

$route = new Zend_Controller_Router_Route(
  array(
    &#39;host&#39; => &#39;blog.mysite.com&#39;,
    &#39;path&#39; => &#39;archive&#39;
  ),
  array(
    &#39;module&#39;   => &#39;blog&#39;,
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39;   => &#39;index&#39;
  )
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

如果你想匹配参数在主机名里,使用 regex 选项。在下面例子中,子域为动作控制器被用作用户名参数。 当组装路由时,你可以给出用户名为参数,就像你用其它路径参数一样:

$route = new Zend_Controller_Router_Route(
  array(
    &#39;host&#39; => array(
      &#39;regex&#39;  => &#39;([a-z]+).mysite.com&#39;,
      &#39;reverse&#39; => &#39;%s.mysite.com&#39;
      &#39;params&#39; => array(
        1 => &#39;username&#39;
      )
    ),
    &#39;path&#39; => &#39;&#39;
  ),
  array(
    &#39;module&#39;   => &#39;users&#39;,
    &#39;controller&#39; => &#39;profile&#39;,
    &#39;action&#39;   => &#39;index&#39;
  )
);
登入後複製

$router->addRoute('profile', $route);

Zend_Controller_Router_Route_Static

设置固定不变的路由:

$route = new Zend_Controller_Router_Route_Static(
  &#39;login&#39;,
  array(&#39;controller&#39; => &#39;auth&#39;, &#39;action&#39; => &#39;login&#39;)
);
$router->addRoute(&#39;login&#39;, $route);
登入後複製

上面的路由将匹配http://domain.com/login的URL,并分派到 AuthController::loginAction().

Zend_Controller_Router_Route_Regex

除了缺省的和静态的路由类型外,正则表达式路由类型也可用。这个路由比其它路由更强更灵活,只是稍微有点复杂。同时,它应该比标准路由快。

象标准路由一样,这个路由必须用路由定义和一些缺省条件来初始化。让我们创建一个archive路由作为例子,和先前定义的类似,这次只是用了Regex:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)&#39;,
  array(
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39;   => &#39;show&#39;
  )
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

每个定义的regex子模式将被注入到请求对象里。同上述的例子,再成功匹配http://domain.com/archive/2006之后,结果值的数组看起来象这样:

$values = array(
  1      => &#39;2006&#39;,
  &#39;controller&#39; => &#39;archive&#39;,
  &#39;action&#39;   => &#39;show&#39;
);
登入後複製

Note: 在匹配之前,开头和结尾的斜杠从路由器里的URL中去除掉了。结果,匹配http://domain.com/foo/bar/,需要foo/bar这样的regex,而不是/foo/bar。

Note: 行开头和行结尾符号(分别为'^' 和 '$')被自动预先追加到所有表达式。这样,你不需要在你的正则表达式里用它们,你应该匹配整个字符串。

Note: 这个路由类使用#符作为分隔符。这意味着你将需要避免哈希符('#')但不是正斜杠('/')在你的路由定义里。因为'#'符(名称为锚)很少被传给webserver,你将几乎不需要在你的regex里使用它。

你可以用通常的办法获得已定义的子模式的内容:

public function showAction()
{
  $request = $this->getRequest();
  $year  = $request->getParam(1); // $year = &#39;2006&#39;;
}
登入後複製

Note: 注意这个键是整数(1) 而不是字符串('1')。

因为'year'的缺省没有设置,这个路由将和它的标准路由副本不是非常精确地相同。即使我们为'year'声明一个缺省并使子模式可选,也不清楚是否会在拖尾斜杠(trailing slash)上还将有问题。方案是使整个'year'部分和斜杠一起可选但只抓取数字部分:(这段比较绕口,请校对者仔细看看,谢谢 Jason Qi)

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive(?:/(\d+))?&#39;,
  array(
    1      => &#39;2006&#39;,
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39;   => &#39;show&#39;
  )
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

让我们看看你可能注意到的问题。 给参数使用基于整数的键不是容易管理的办法,今后可能会有问题。这就是为什么有第三个参数。这是个联合数组表示一个regex子模式到参数名键的映射。我们来看看一个简单的例子:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)&#39;,
  array(
    &#39;controller&#39; => &#39;archive&#39;,
    &#39;action&#39; => &#39;show&#39;
  ),
  array(
    1 => &#39;year&#39;
  )
);
$router->addRoute(&#39;archive&#39;, $route);
登入後複製

这将导致下面的值被注入到请求:

$values = array(
  &#39;year&#39;    => &#39;2006&#39;,
  &#39;controller&#39; => &#39;archive&#39;,
  &#39;action&#39;   => &#39;show&#39;
);
登入後複製

这个映射被任何目录来定义使它能工作于任何环境。键可以包含变量名或子模式索引:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)&#39;,
  array( ... ),
  array(1 => &#39;year&#39;)
);
// OR
$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)&#39;,
  array( ... ),
  array(&#39;year&#39; => 1)
);
登入後複製

Note: 子模式键必须用整数表示。

注意在请求值中的数字索引不见了,代替的是一个名字变量。当然如果你愿意可以把数字和名字变量混合使用:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)/page/(\d+)&#39;,
  array( ... ),
  array(&#39;year&#39; => 1)
);
登入後複製

这将导致在请求中有混合的值可用。例如:URLhttp://domain.com/archive/2006/page/10将在下列结果中:

$values = array(
  &#39;year&#39;    => &#39;2006&#39;,
  2      => 10,
  &#39;controller&#39; => &#39;archive&#39;,
  &#39;action&#39;   => &#39;show&#39;
);
登入後複製

因为regex模型不容易颠倒,如果你想用URL助手或这个类中的 assemble方法,你需要准备一个颠倒的URL。这个颠倒的路径用可由sprintf()解析的字符串来表示并定义为第四个构造参数:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;archive/(\d+)&#39;,
  array( ... ),
  array(&#39;year&#39; => 1),
  &#39;archive/%s&#39;
);
登入後複製

所有这些都已经可能由标准路由对象完成,那么使用Regex路由的好处在哪里?首先,它允许你不受限制地描述任何类型的URL。想象一下你有一个博客并希望创建象http://domain.com/blog/archive/01-Using_the_Regex_Router.html这样的URLs,还有把解析它路径元素中的最后部分,01-Using_the_Regex_Router.html,到一个文章的ID和文章的标题/描述;这不可能由标准路由完成。用Regex路由,你可以做象下面的方案:

$route = new Zend_Controller_Router_Route_Regex(
  &#39;blog/archive/(\d+)-(.+)\.html&#39;,
  array(
    &#39;controller&#39; => &#39;blog&#39;,
    &#39;action&#39;   => &#39;view&#39;
  ),
  array(
    1 => &#39;id&#39;,
    2 => &#39;description&#39;
  ),
  &#39;blog/archive/%d-%s.html&#39;
);
$router->addRoute(&#39;blogArchive&#39;, $route);
登入後複製

正如你所看到的,这个在标准路由上添加了巨大的灵活性。

通过配置文件定义路由规则

例如

[production]
routes.archive.route = "archive/:year/*"
routes.archive.defaults.controller = archive
routes.archive.defaults.action = show
routes.archive.defaults.year = 2000
routes.archive.reqs.year = "\d+"
routes.news.type = "Zend_Controller_Router_Route_Static"
routes.news.route = "news"
routes.news.defaults.controller = "news"
routes.news.defaults.action = "list"
routes.archive.type = "Zend_Controller_Router_Route_Regex"
routes.archive.route = "archive/(\d+)"
routes.archive.defaults.controller = "archive"
routes.archive.defaults.action = "show"
routes.archive.map.1 = "year"
; OR: routes.archive.map.year = 1
登入後複製

上述的INI文件可以被读进Zend_Config对象:

$config = new Zend_Config_Ini(&#39;/path/to/config.ini&#39;, &#39;production&#39;);
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, &#39;routes&#39;);
登入後複製

在上面的例子中,我们告诉路由器去使用INI文件'routes'一节给它的路由。每个在这个节下的顶级键将用来定义路由名;上述例子定义了路由'archive'和'news'。每个路由接着要求,至少,一个'route'条目和一个或更多'defaults'条目;可选地,一个或更多'reqs'('required'的简写)可能要求提供。总之,这些相对应的三个参数提供给Zend_Controller_Router_Route_Interface对象。一个选项键,'type',可用来指定路由类的类型给特殊的路由;缺省地,它使用Zend_Controller_Router_Route。在上述例子中,'news'路由被定义来使用Zend_Controller_Router_Route_Static。

自定义路由类型

标准的rewrite路由器应当最大限度提供你所需的功能;大多时候,为了通过已知的路由提供新的或修改的功能,你将只需要创建一个新的路由类型

那就是说,你可能想要用不同的路由范例。接口Zend_Controller_Router_Interface提供了需要最少的信息来创建路由器,并包含一个单个的方法。

interface Zend_Controller_Router_Interface
{
 /**
  * @param Zend_Controller_Request_Abstract $request
  * @throws Zend_Controller_Router_Exception
  * @return Zend_Controller_Request_Abstract
  */
 public function route(Zend_Controller_Request_Abstract $request);
}
登入後複製

路由只发生一次:当请求第一次接收到系统。路由器的意图是基于请求的环境决定控制器、动作和可选的参数,并把它们发给请求。请求对象接着传递给派遣器。如果不可能映射一个路由到一个派遣令牌,路由器对请求对象就什么也不做。

希望本文所述对大家PHP程序设计有所帮助。

更多Zend Framework教程之路由功能Zend_Controller_Router详解相关文章请关注PHP中文网!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門推薦
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板