2008-09-24 16 views
15

Je suis donc en train d'écrire un cadre sur lequel je veux baser quelques applications sur lesquelles je travaille (le framework est là, donc j'ai un environnement de travail, et un système qui va me permettre , par exemple, utilisez une connexion unique)Routage d'URL d'application PHP

Je souhaite créer ce cadre et les applications qu'il utilise comme architecture orientée ressources. Maintenant, je veux créer une classe de routage d'URL qui est extensible par les auteurs APP (et peut-être aussi par les utilisateurs de l'application CMS, mais c'est WAYYYY dans le futur) et j'essaie de trouver la meilleure façon de faire en regardant comment les autres applications le font.

Répondre

12

Je préfère utiliser reg ex plutôt que de créer mon propre format car il est de notoriété publique. J'ai écrit une petite classe que j'utilise, ce qui me permet d'imbriquer ces tables de routage. J'utilise quelque chose de similaire qui a été implémenté par héritage mais je n'ai pas besoin d'héritage donc je l'ai réécrit. Je fais un reg sur une clé et mappe à ma propre chaîne de contrôle. Prenez l'exemple ci-dessous.Je visite /api/related/joe et ma classe de routeur crée un nouvel objet ApiController et appelle sa méthode relatedDocuments(array('tags' => 'joe'));

// the 12 strips the subdirectory my app is running in 
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12)); 

Route::process($index, array(
    "#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags", 

    "#^thread/(.*)/post$#Di" => "ThreadController/post/title", 
    "#^thread/(.*)/reply$#Di" => "ThreadController/reply/title", 
    "#^thread/(.*)$#Di"   => "ThreadController/thread/title", 

    "#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags", 
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id", 
    "#^ajax/reply/(.*)$#Di"  => "ArticleController/newReply/id", 
    "#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle", 

    "#^$#Di"     => "HomeController", 
)); 

Afin de garder les erreurs vers le bas et la simplicité jusqu'à vous pouvez diviser votre table. De cette façon, vous pouvez placer la table de routage dans la classe qu'elle contrôle. En prenant l'exemple ci-dessus, vous pouvez combiner les trois appels de threads en un seul.

Route::process($index, array(
    "#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags", 

    "#^thread/(.*)$#Di"   => "ThreadController/route/uri", 

    "#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags", 
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id", 
    "#^ajax/reply/(.*)$#Di"  => "ArticleController/newReply/id", 
    "#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle", 

    "#^$#Di"     => "HomeController", 
)); 

Ensuite, vous définissez ThreadController :: route comme suit.

function route($args) { 
    Route::process($args['uri'], array(
     "#^(.*)/post$#Di" => "ThreadController/post/title", 
     "#^(.*)/reply$#Di" => "ThreadController/reply/title", 
     "#^(.*)$#Di"   => "ThreadController/thread/title", 
    )); 
} 

Vous pouvez également définir les valeurs par défaut que vous souhaitez pour votre chaîne de routage sur la droite. N'oubliez pas de les documenter ou vous allez confondre les gens. J'appelle actuellement l'index si vous n'incluez pas un nom de fonction sur la droite. Here est mon code actuel. Vous souhaiterez peut-être le modifier pour gérer les erreurs comme vous le souhaitez et/ou les actions par défaut.

+0

Le deuxième exemple est très bien ce que je pensais réellement faire ... (style RoR) - Je vais jeter un oeil à travers votre code. – Mez

+0

C'est très similaire à Django Python. – kta

1

Utilisez une liste de regexs pour correspondre à quel objet que je devrais utiliser

Par exemple

^/users/[\w-]+/bookmarks/(.+)/$ 
^/users/[\w-]+/bookmarks/$ 
^/users/[\w-]+/$ 

Plus: Nice et simple, me permet de définir des itinéraires directement Inconvénients: aurait à commander, ne rendant facile d'ajouter de nouvelles choses (très sujette aux erreurs)

Ceci est, autant que je sache, comment Django le fait

-4

Essayez de prendre oeil à 01 Motif.
Zend Framework, il utilise par exemple, mais aussi CakePHP, CodeIgniter, ...

moi n'aime pas personnellement le modèle MVC, mais il est la plupart du temps mis en œuvre en tant que composant « View pour le Web ».

La décision assez dépend beaucoup de préférence ...

+0

J'utilise une chose MVC. Je parle du côté technique ou du routage d'URL. Ceci n'est pas une réponse à ma question – Mez

0

Je pense que beaucoup de cadres utilisent une combinaison de mod_rewrite d'Apache et un contrôleur avant. Avec mod_rewrite, vous pouvez transformer une URL comme celle-ci:/people/get/3 en: index.php? Controller = gens & method = get & id = 3. Index.php implémenterait votre contrôleur frontal qui route la requête de page en fonction des paramètres donnés.

+0

Donc, en ignorant vos URLs non ROA, c'est la même chose que ma réponse ci-dessus, mais en utilisant mod_rewrite avec des règles spécifiques, et en faisant le "routage" par des paramètres spécifiques plutôt que par le code. – Mez

-1

cadre de MVC utilise par défaut une structure comme

/router/controller/action/key1/value1/key2/value2 

router est le fichier de routeur (mappé via mod_rewrite, controller est à partir d'un gestionnaire d'action de commande qui est définie par une classe qui dérive de Zend_Controller_Action et action référence une méthode dans le contrôleur, nommée actionAction Les paires clé/valeur peuvent aller dans n'importe quel ordre et sont disponibles pour la méthode d'action sous forme de tableau associatif

J'ai utilisé quelque chose de similaire dans le passé Dans mon propre code, et jusqu'à présent, ça a plutôt bien marché.

+0

actions ne s'appliquent pas à un système ROA, seulement à un système de style RPC – Mez

+0

Vous avez absolument raison ... J'ai mal lu la question. Cela, et ROA m'a jeté. Je l'ai toujours appelé REST. –

+0

Ce n'est pas du tout RESTFul cependant. Juste pour info. – DanMan

2

Encore un autre cadre? - de toute façon ...

L'astuce est avec le routage est de le passer partout à votre contrôleur de routage.

Vous voudriez probablement utiliser quelque chose de semblable à ce que j'ai documenté ici:

http://www.hm2k.com/posts/friendly-urls

La deuxième solution vous permet d'utiliser des URL similaires à Zend Framework.

+5

Construire un cadre de travail est un rite d'initiation. Tout programmeur php doit le faire. – user1709251

0

Comme vous pouvez vous y attendre, il y a beaucoup de façons de le faire.

Par exemple, dans Slim Framework, un exemple du moteur de routage peut être le folllowing (en fonction du modèle ${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})):

$app->get("/Home", function() { 
    print('Welcome to the home page'); 
} 

$app->get('/Profile/:memberName', function($memberName) { 
    print('I\'m viewing ' . $memberName . '\'s profile.'); 
} 

$app->post('/ContactUs', function() { 
    print('This action will be fired only if a POST request will occure'); 
} 

Ainsi, l'instance initialisés ($app) obtient une méthode par méthode de requête (par exemple, obtenir, publier, mettre, supprimer, etc.) et obtient une route en tant que premier paramètre et le rappel en tant que deuxième. L'itinéraire peut obtenir des jetons - qui sont "variables" qui changeront à l'exécution sur la base de certaines données (comme le nom du membre, l'ID de l'article, le nom de l'organisation ou autre).

Personnellement, j'aime bien, mais je ne pense pas que ce soit assez flexible pour un framework avancé.

Depuis que je travaille actuellement avec ZF et Yû, j'ai un exemple d'un routeur j'ai créé dans le cadre d'un cadre d'une entreprise pour laquelle je travaille:

Le moteur d'itinéraire est basé sur regex (similaire à celui de @ gradbot) mais a une conversation bidirectionnelle, donc si un de vos clients ne peut pas exécuter mod_rewrite (dans Apache) ou ajouter des règles de réécriture sur son serveur, il peut toujours utiliser le traditionnel URL avec chaîne de requête.

Le fichier contient un tableau, chacun de celui-ci, chaque élément est similaire à cet exemple:

$_FURLTEMPLATES['login'] = array(
    'i' => array(// Input - how the router parse an incomming path into query string params 
     'pattern' => '@Members/Login/[email protected]', 
     'matches' => array('Application' => 'Members', 'Module' => 'Login'), 
    ), 
    'o' => array(// Output - how the router parse a query string into a route 
     '@Application=Members(&|&)Module=Login/[email protected]' => 'Members/Login/' 
    ) 
); 

Vous pouvez également utiliser des combinaisons plus complexes, telles que:

$_FURLTEMPLATES['article'] = array(
    'i' => array(
     'pattern' => '@CMS/Articles/([\d]+)/[email protected]', 
     'matches' => array('Application' => "CMS", 
      'Module' => 'Articles', 
      'Sector' => 'showArticle', 
      'ArticleID' => '$1'), 
    ), 
    'o' => array(
    '@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/$4' 
    ) 
); 

La ligne de fond , comme je le pense, c'est que les possibilités sont infinies, cela dépend seulement de la complexité de votre cadre et de ce que vous voulez en faire. Par exemple, il s'agit simplement d'un service Web ou d'un simple wrapper de site Web - il suffit de suivre le style d'écriture de Slim framework - un code très simple et très esthétique. Cependant, si vous souhaitez développer des sites complexes en l'utilisant, je pense que regex est la solution.

Bonne chance! :)

0

Vous devriez vérifier Pux https://github.com/c9s/Pux

Voici le synopsis

<?php 
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class. 
use Pux\Executor; 

class ProductController { 
    public function listAction() { 
     return 'product list'; 
    } 
    public function itemAction($id) { 
     return "product $id"; 
    } 
} 
$mux = new Pux\Mux; 
$mux->any('/product', ['ProductController','listAction']); 
$mux->get('/product/:id', ['ProductController','itemAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$mux->post('/product/:id', ['ProductController','updateAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$route = $mux->dispatch('/product/1'); 
Executor::execute($route); 
+0

pourriez-vous ajouter l'itinéraire pour 404? –