2011-03-22 5 views
5

J'essaye d'écrire une route pour une profondeur de catégorie N de niveau. Donc une URL de catégorie habituelle ressemblerait à ceci:Zend Framework route: nombre inconnu de paramètres

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

Il a une profondeur inconnue et ma route doit correspondre à tous les niveaux possibles. J'ai fait un itinéraire pour cela, mais je ne peux pas obtenir tous les paramètres de mon contrôleur. Je ne parviens pas à trouver un moyen d'envoyer les paramètres correspondants à l'automate. Si vous avez une meilleure solution, je suis ouvert aux suggestions!

Répondre

4

J'ai trouvé une solution qui correspond à mes besoins. Je vais poster ici pour les gens qui finiront dans la même chose que moi.

Problème:

  • besoin itinéraire personnalisé pour les catégories de niveau N comme category/subcategory/subsubcategory/...
  • itinéraire personnalisé pour les catégories N + objet comme category/subcategory/../page.html
  • conserver le routage par défaut de Zend Framework (pour les autres modules, admin pour exemple)
  • URL d'assemblage avec URL helper

Solution:

  • Créer une classe d'itinéraire personnalisé (je Zend_Controller_Router_Route_Regex comme point de départ pour que je puisse bénéficier de la méthode assemble())

Code actuel:

<?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; 

    } 
} 

Utilisation:

Route:

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

Exemple de sortie dans le contrôleur avec 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" 
  • Notez le cmsObject est réglé uniquement lorsque l'URL contient quelque chose comme category/subcategory/subsubcategory/my-page.html
+1

c'est vraiment bon Bogdan :) – MiPnamic

+0

Merci, MiPnamic! J'ai vraiment fait un peu de réflexion pour celui-ci :) –

2

Je l'ai fait sans routes ... Je l'ai mis en déroute que le premier paramètre, puis acheminer les autres à obtenir tous les params à l'intérieur du contrôleur

Route:

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 

comme par exemple: Je l'utilise pour le catalogue, puis dans l'itemController dans le displayAction je vérifie pour $ this-> getRequest() -> getParams(), le point est que vous pouvez (mais je pense que vous le savez) lire tous les params passé dans la façon clé/valeur, par exemple: "site.com/catalog/item/15/kind/hat/color/red/size/M" produira un tableau comme: $params['controller'=>'catalog','action'=>'display','id'=>'15','kind'=>'hat','color'=>'red','size'=>'M'];

+0

Pouvez-vous donner un exemple de cela, s'il vous plaît? –

+0

Je ne suis pas si sûr que ce soit ce que je veux. De cette façon, je vais finir avec de nombreuses variables dans le contrôleur dispersés. Quoi qu'il en soit, j'ai trouvé une solution donc je l'afficherai ci-dessous. –

Questions connexes