− Table des matières
Le principe de configuration est d'indiquer dans un fichier
app/system/urls.xml
la correspondance entre des urls et les actions
jelix. Voici un exemple de fichier :
<urls xmlns="http://jelix.org/ns/urls/1.0">
<entrypoint name="index" default="true">
<url pathinfo="/" module="main" action="default:index" />
<url module="main" />
<url pathinfo="/news/:annee/:mois/:id-:title" module="news" action="view">
<param name="annee" type="year"/>
<param name="mois" type="month" />
<param name="id" type="number" />
<param name="title" escape="true" />
</url>
<url pathinfo="/articles/:rubrique/:id_art" module="cms" action="show">
<param name="id_art" regexp="\d+"/>
</url>
</entrypoint>
<entrypoint name="shop" type="classic">
<url pathinfo="/:category/:product" module="unittest" action="url2">
<param name="product" regexp="\d{2}" />
<static name="mystatic" value="valeur statique" />
</url>
</entrypoint>
<entrypoint name="foo/bar">
<url handler="urlsig" module="unittest" action="url4" />
</entrypoint>
<entrypoint name="news">
<url module="news" />
</entrypoint>
<entrypoint name="xmlrpc" default="true" type="xmlrpc" />
<entrypoint name="jsonrpc" default="true" type="jsonrpc"/>
</urls>
Balises entrypoint ¶
La balise racine urls
contient autant de balises entrypoint
que de
points d'entrée disponibles dans votre application.
Chacune de ces balises a un attribut name
indiquant le nom du point
d'entrée (sans l'extension .php), ou plus exactement, son chemin, relatif à la
valeur du basePath
dans la configuration générale. Et vous ajouterez
éventuellement un attribut default
indiquant si ce point d'entrée est
celui par défaut pour le type de requête en question.
Il doit également y avoir un attribut type
, qui indique le type de point
d'entrée, et vaut donc classic
, xmlrpc
, jsonrpc
etc.. Si cet
attribut n'est pas présent, la valeur par défaut est classic
.
Pour des raisons de compatibilité avec les versions précédentes de jelix, les
balises entrypoint
peuvent avoir d'autres noms. Leur nom exact précise le
type de requête auquel ils sont affectés :
classicentrypoint
pour les requêtes classiquesxmlrpcentrypoint
pour du xmlrpc- etc ...
Mais cet usage est dorénavant obsolète.
Balise url ¶
Chaque module est accessible à un point d'entrée spécifique, donc il faut déclarer les urls correspondantes à ces modules dans les balises de point d'entrée attitrées.
Spécifier l'action par défaut, la page d'accueil ¶
Toute application a une page par défaut qui s'affiche quand il n'y a pas de pathinfo.
Il y a donc au moins une url à définir pour chaque point d'entrée de type "classic", en indiquant le module et l'action correspondants :
<urls xmlns="http://jelix.org/ns/urls/1.0">
<entrypoint name="index" default="true">
<url pathinfo="/" module="main" action="default:index" />
</entrypoint>
</url>
Ici donc, l'url index.php/
correspond à la méthode index
du contrôleur
default
du module main
.
Note : avant Jelix 1.7, il fallait l'action par défaut au moyen des paramètres
startModule
et startAction
dans le fichier de configuration ini du point d'entrée.
des URLs automatiques pour les modules ¶
Comme vous le verrez un peu plus loin, vous pouvez définir des urls pour
chaque action (une balise url d'action
). Mais ce n'est pas une obligation.
Par défaut, les urls sont de la forme : <entrypoint>.php/<module>/<controleur>/<methode>
.
Pour cela, il faut indiquer sur quel point d'entrée le module est accessible, et donc
spécifier une balise URL de module
, c'est à dire une balise <url>
indiquant juste le
nom du module.
<entrypoint name="index" default="true">
<url pathinfo="/" module="main" action="default:index" />
<url module="main" />
</entrypoint>
<entrypoint name="actualites">
<url module="news" />
</entrypoint>
Ici toutes les urls du module news
seront de la forme actualites.php/news/<controleur>/<methode>
.
Vous pouvez aussi indiquer un pathinfo : il remplacera le nom du module dans l'url. Avec cet exemple :
<entrypoint name="index" default="true">
<url pathinfo="/" module="main" action="default:index" />
<url pathinfo="/supere/actu/" module="news" />
</entrypoint>
les urls du module news seront de la forme index.php/supere/actu/<controleur>/<methode>
.
Si il n'y a pas de balise url de module
pour un module donné, seul les actions du module
qui sont spécifiées par une balise <url>
d'action, seront accessible depuis le web.
Ainsi, dans l'exemple précédant, il n'y a qu'une balise url d'action pour le
module main, donc uniquement l'action default:index
sera accessible (et
qui se trouve ici être la page d'accueil). Pour que les autres actions du module
main puissent être accessible, soit on déclare une balise url d'action pour
chaque action, soit on ajoute <url module="main" />
.
Si aucune balise url (de module ou d'action) n'est définie pour un module donné, alors
le module sera accessible par le point d'entrée qui est celui par défaut
(<entrypoint default="true">
).
des URLs automatiques pour les contrôleurs ¶
Il est possible de définir un pathinfo pour un contrôleur en particulier, tout en laissant ajouter le nom de l'action dans l'url.
Pour cela, il faut indiquer le module, le pathinfo et le contrôleur dans un
attribut controller
.
Exemple :
<entrypoint name="index" default="true">
<!-- ... -->
<url pathinfo="/cool" module="main" controller="system" />
</entrypoint>
Les urls des actions du contrôleur system
dans l'exemple, seront de la forme
index.php/cool/<methode>
.
Spécifier les urls pour des actions ¶
Selon un pathinfo ¶
Vous voulez indiquer le module et l'action à exécuter pour une forme
particulière d'URL. Vous indiquerez alors un attribut pathinfo
, indiquant
le "modèle" de la partie pathinfo auquel l'URL doit ressembler, ainsi que le
module et l'action dans des attributs module
et action
.
Dans cet exemple, toute URL qui aura pour pathinfo la valeur /foo/bar
correspondra au module hello
et l'action default:world
.
<url pathinfo="/foo/bar" module="hello" action="default:world" />
Vous pouvez mettre l'attribut optionalTrailingSlash="true"
si vous voulez
indiquer que l'éventuelle présence/absence d'un slash à la fin correspond à la
même action ( /foo/bar/
et /foo/bar
serait donc équivalent, ce qui n'est
pas le cas par défaut). On peut mettre cet attribut sur la balise
entrypoint
pour que ce soit pris en compte pour toutes les urls définies.
Selon un pathinfo contenant des parties indéfinies ¶
Il est possible d'indiquer des parties "dynamiques" dans le pathinfo. Elles sont
à définir par un deux-points suivi d'un nom. La valeur trouvée sera alors
stockée dans un paramètre de même nom pour le controleur. Dans l'exemple qui suit,
le pathinfo contient deux parties dynamiques : rubrique
et id_art
.
<url pathinfo="/articles/:rubrique/:id_art" module="cms" action="default:show" />
Si on appelle l'URL "/articles/aviation/544", alors les paramètres rubrique
et id_art
seront crées et auront pour valeurs respectives "aviation"
et
"544"
.
Attention : pour éviter les ressemblances avec d'autres URLs, il faut au moins
une partie fixe (ici /articles/
) dans l'URL pour la distinguer des
autres.
Des parties indéfinies typées ou formatées ¶
Un autre moyen d'éviter ces ressemblances est de spécifier le format ou le type de chaque paramètre. Par défaut le type est une chaîne classique.
Pour cela il faut indiquer des balises <param>
pour chacun des paramètres
dont on veut spécifier le type/format. Elles devront contenir un attribut
name
indiquant le paramètre et, soit un attribut type
, soit un
attribut regexp
, contenant une expression régulière du format (sans
délimiteur). Dans notre exemple, nous voulons spécifier que rubrique
est une
chaîne et une expression régulière pour id_art
:
<url pathinfo="/articles/:rubrique/:id_art" module="cms" action="show">
<param name="rubrique" type="string" />
<param name="id_art" regexp="\d+" />
</url>
Si l'expression régulière de l'attribut regexp
contient des parenthèses, il est
nécessaire d'indiquer que celles-ci ne doivent pas être capturées. Exemple :
<param name="type" regexp="(?:0|1|2){1}" />
Quand le paramètre est de type string il n'est pas obligatoire de mettre une
balise param
. Les types disponibles sont :
string | une chaîne |
letter | une seule lettre |
number | un nombre entier, équivalent aussi à 'int' et 'integer' |
digit | un chiffre |
date | une date au format AAAA-MM-JJ |
year | une année sur 4 chiffres |
month | un mois sur deux chiffres |
day | un jour sur deux chiffres |
path | un sous chemin. dans ce cas ce paramètre doit être le dernier dans l'url |
lang | un code langue (2 ou 3 lettres), voir section plus bas sur les paramètres automatiques de langue |
locale | un code de locale (code langue + code pays), voir section plus bas sur les paramètres automatiques de langue |
Vous devrez bien entendu indiquer les valeurs de ces paramètres lors de l'appel à jUrl, afin de générer la bonne URL.
Des paramètres statiques ¶
Il peut être nécessaire parfois de rajouter des paramètres "statiques",
attendues par l'action (celle-ci pouvant être attribuées à plusieurs URLs
différentes), mais non présent dans l'URL. Pour cela il faut ajouter une balise
<static>
, avec nom et valeur, comme dans cet exemple :
<url pathinfo="/:category/:product" module="shop" action="product:view">
<param name="product" regexp="\d{2}" />
<static name="details" value="0" />
</url>
<url pathinfo="/:category/:product/details" module="shop" action="product:view">
<param name="product" regexp="\d{2}" />
<static name="details" value="1" />
</url>
Ici on utilise la même action pour deux URLs différentes. Son traitement
différera en partie selon le paramètre details
, qu'il ne faut donc pas
oublier de donner à jUrl::get()
. Dans un cas on afficherait le produit
d'un catalogue avec ses caractéristiques générales, et dans l'autre avec ses
caractéristiques générales et détaillées. Cela évite donc de créer deux
actions différentes pour si peu de différences.
On peut aussi utiliser ce mécanisme pour supporter plusieurs langues :
<url pathinfo="/articles/en/:page" module="cms" action="page:view">
<param name="page"/>
<static name="lang" value="en_US" />
</url>
<url pathinfo="/articles/fr/:page" module="cms" action="page:view">
<param name="page"/>
<static name="lang" value="fr_FR" />
</url>
Là encore, il ne faut pas oublier de donner le paramètre lang
à jUrl pour
que ce dernier puisse savoir quelle url il doit renvoyer.
jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale));
// ou
jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>"en"));
<a href="{jurl 'cms~page:view', array('page'=>'foo', 'lang'=>$j_locale}">my link</a>
<a href="{jurl 'cms~page:view', array('page'=>'foo', 'lang'=>'fr_FR'}">my link</a>
Paramètres automatiques de langues ¶
Avec les paramètres statiques ¶
Nous avons vu que l'on pouvait déclarer des paramètres statiques contenant le code d'une langue, pour différencier deux URLS pointant vers une même action. Cependant, cette solution, telle que décrite, a deux inconvénients :
- il faut donner ce paramètre à
jUrl::get()
, et en général, on indique la valeur de la langue courante - Dans l'action, il faut tenir compte de ce paramètre, et en général, il s'agit de changer la configuration à la volée (le plugin autolocale n'étant pas forcément utilisé)
Le moteur d'URL apporte une solution : il suffit d'ajouter l'attribut type="locale"
sur la balise <static>
, pour que tout soit automatique. Vous définissez par exemple :
<url pathinfo="/articles/english/:page" module="cms" action="page:view">
<param name="page"/>
<static name="lang" value="en_US" type="locale"/>
</url>
<url pathinfo="/articles/francais/:page" module="cms" action="page:view">
<param name="page"/>
<static name="lang" value="fr_FR" type="locale" />
</url>
Si vous faites simplement jUrl::get('cms~page:view', array('page'=>'foo'))
,
alors jUrl générera /articles/english/foo
si la langue courante est "en_US",
ou /articles/francais/foo
si la langue courante est "fr_FR". Vous n'avez pas
besoin d'indiquer le code langue comme avant : jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale))
.
Cependant, vous pouvez toujours le faire pour forcer le code langue.
À l'inverse, lors de la demande de la page /articles/english/foo
ou
/articles/francais/foo
, Jelix configurera automatiquement la locale, le temps
de l'execution de l'action. Ainsi, si l'utilisateur appelle la page /articles/english/foo
,
la langue courante deviendra "en_US".
Vous pouvez aussi utiliser type="lang"
. Dans ce cas, vous pouvez indiquer juste
le code langue ('en', 'fr'...), que ce soit dans l'attribut value
, ou à jUrl::get()
.
<static name="lang" value="fr" type="lang" />
Avec les paramètres dynamiques ¶
Si il y a plus de 2-3 langues, l'utilisation des paramètres statiques peut devenir fastidieuses : il faut autant d'URL que de langues.
Heureusement, vous pouvez avoir des paramètres dynamiques de type "locale" ou "lang".
<param name="lg" type="lang"/>
<!-- ou -->
<param name="lg" type="locale"/>
Notez qu'il s'agit d'une balise <param>
et non plus <static>
.
Cela permet d'avoir dans le "pathinfo" de l'url l'information de la langue, sans même avoir à
l'indiquer à jUrl::get()
. Cela permet aussi de configurer la langue courante
automatiquement pendant l'éxecution de l'action lors de l'appel de l'URL en
question.
Ainsi, l'exemple utilisé plus haut :
<url pathinfo="/articles/en/:page" module="cms" action="page:view">
<param name="page" />
<static name="lang" value="en_US" />
</url>
<url pathinfo="/articles/fr/:page" module="cms" action="page:view">
<param name="page" />
<static name="lang" value="fr_FR" />
</url>
peut en fait se résumer à :
<url pathinfo="/articles/:lang/:page" module="cms" action="page:view">
<param name="page"/>
<param name="lang" type="lang" />
</url>
Si vous faites juste jUrl::get('cms~page:view', array('page'=>'foo'))
,
alors jUrl générera /articles/en/foo
si la langue courante est "en_US",
ou /articles/fr/foo
si la langue courante est "fr_FR".
Et si un navigateur appelle l'url /articles/en/foo
, la langue courante
deviendra automatiquement "en_US".
Même principe avec type="locale"
, sauf que jUrl générera un code
de locale, c'est à dire code langue + code pays : /articles/en_US/foo
.
Utiliser un handler spécifique ¶
Jusqu'ici, nous avons vu un système d'analyse et de génération d'URL avec ses pathinfos et paramètres, assez générique. Cependant, pour certains usages, il peut être insuffisant en terme de flexibilité.
Imaginons par exemple que vous vouliez transformer une partie de l'information contenue dans le pathinfo, comme par exemple chercher dans une base de données un id correspondant à un titre contenu dans le pathinfo, et fournir ainsi aux actions, non pas un titre, mais un id (qui n'est pas contenu dans cette URL).
Ou encore que la partie pathinfo puisse être variable.
Aussi c'est à vous de développer l'analyse de l'URL et d'indiquer l'action à exécuter. Vous le ferez dans une classe spécialisée :
class myHandlerUrlsHandler implements jIUrlSignificantHandler {
function parse($url){
if (preg_match("/^\/(.*)$/",$url->pathInfo,$match)) {
// le pathinfo correspond, on traite
$urlact = new jUrlAction($url->params);
$urlact->setParam('page',jUrl::unescape($match[1]));
return $urlact;
}
else {
// le pathinfo ne correspond pas, on ne traite pas l'url
return false;
}
}
function create($urlact, $url){
$p = jUrl::escape($url->getParam('page'));
$url->pathInfo = "/$p";
$url->delParam('page');
}
}
Le nom de la classe doit commencer par un préfixe que vous voulez et se terminer
par UrlsHandler
. La classe sera stockée dans le répertoire classes/
du
module indiqué dans la balise url
, sous le nom <prefixe>.urlhandler.php
.
Pour notre exemple, cela sera myHandler.urlhandler.php
.
La méthode parse()
est chargée d'analyser une URL (objet jUrl
que
vous recevez en paramètre), en l'occurence, l'URL demandée dans l'application.
Si votre handler reconnaît l'URL, parse()
doit renvoyer un objet
jUrlAction
qui contient toutes les données pour l'action. Sinon il doit
renvoyer false
.
La méthode create()
est appelée à chaque fois que l'application demande la
création d'une URL à partir de paramètres d'action. Elle reçoit donc un objet de
type jUrlAction
, et un objet de type jUrl
. $urlaction contient
donc les paramètres de l'action. Ces paramètres ont déjà été inclus dans l'objet
$url. Vous devez donc modifier $url de façon à produire l'URL
correspondante à l'action demandée. Dans l'exemple, on récupère le paramètre
page, que l'on incorpore dans le pathinfo. Et comme il est dans le pathinfo, on
le supprime alors des paramètres.
Notez l'usage de jUrl::escape()
et jUrl::unescape()
, pour "nettoyer"
les chaînes à inclure dans un pathinfo (accents enlevés par ex).
Enfin dans le fichier urls.xml
, vous devez indiquer l'utilisation de ce
handler :
<entrypoint name="wiki">
<url handler="myHandler" module="unittest" action="url4" />
</entrypoint>
Vous pouvez bien sûr indiquer un handler d'un autre module. Exemple :
<entrypoint name="wiki">
<url handler="autremodule~myHandler" module="unittest" action="url4" />
</entrypoint>
Une même URL pour plusieurs actions possibles ¶
Imaginons que nous ayons une URL de ce type /article/54-titre
et par défaut,
cela affiche l'article 54 avec une action nommée view, par exemple :
<url pathinfo="/article/:id_art-:title" module="cms" action="view" />
On veut pouvoir, sans changer l'url, indiquer d'autres actions dans certains cas, avec un paramètre action :
/article/54-titre?action=edit
/article/54-titre?action=delete
Note : On pourrait faire aussi /article/54-titre/edit ou
/article/54-titre/delete , avec donc plusieurs balises urls
, ce qui nous
éviterait ce qui suit. Mais ce n'est pas très pratique quand l'URL est appelée
suite à un formulaire par exemple.
Pour indiquer les actions alternatives autorisées, on ajoute un attribut
actionoverride
, contenant la liste des actions séparées par un espace ou
une virgule :
<url pathinfo="/article/:id_art-:title" module="cms" action="view" actionoverride="edit,delete" />
Spécifier des URLs en https à certaines actions ¶
Pour certaines actions vous voulez que l'accès soit uniquement en mode sécurisé
de http (SSL). Vous indiquez alors un attribut https
sur les balises
<url>
en question avec la valeur true. Si cela concerne toutes les
actions/URLs d'un point d'entrée, alors vous mettrez cet attribut sur la balise
<entrypoint>
correspondante.
Attention : pour le moment, Jelix ne vérifie pas lors de l'exécution d'une action que la requête a été faite en https ou non.
Inclure un fichier d'urls d'un module particulier ¶
Il est possible de déclarer les urls dans chaque module, donc dans des fichiers séparés. Cela évite d'avoir un fichier urls.xml volumineux, mais surtout évite d'avoir à définir ou à "recopier" les urls d'un module réalisé par un tiers.
Pour ce faire, vous devez créer un fichier urls.xml
dans le répertoire du
module (vous pouvez choisir un autre nom que urls.xml). Ce fichier ne contient
pas une balise racine urls
mais suburls
, et contient uniquement des
balises url
, et donc pas entrypoint
. Voici celui du module jauth comme
exemple :
<?xml version="1.0" encoding="utf-8"?>
<suburls xmlns="http://jelix.org/ns/suburls/1.0">
<url pathinfo="/dologin" action="login:in" />
<url pathinfo="/dologout" action="login:out" />
<url pathinfo="/login" action="login:form"/>
</suburls>
Et dans le fichier urls.xml principal, dans une des balises entrypoint
, vous indiquez
<entrypoint name="index">
...
<url pathinfo="/auth" module="jauth" include="urls.xml" />
</entrypoint>
Cela signifie : il faut inclure toutes les urls qui sont dans le fichier
urls.xml
du module jauth, et préfixer le pathinfo de ces urls par le
chemin "/auth/". Autrement dit, l'url pour l'action "jauth~login:in" sera
"/auth/dologin".
Cela signifie aussi que les urls que vous indiquez dans un fichier de module ne sont pas des urls complètes. Cela permet à l'utilisateur du module de choisir le chemin (en tout cas, le début du chemin) pour les urls du module, et éviter donc des doublons avec d'autres modules.
Dans un fichier URL de module, vous pouvez déclarer des urls avec un pathinfo
complet, avec un pathinfo avec variables (formatées ou non), avec des paramètres
statiques, avec un handler. Par contre, il est obligatoire de spécifier une url
pour chaque action. Notez aussi qu'il ne faut pas utiliser l'attribut
module
, cela n'aurait pas de sens.
Bien sûr, vous devez faire un fichier d'urls pour chaque type d'action : un fichier pour le point d'entrée classique, un fichier pour chaque autre type de point d'entrée etc.
Suppression du nom du point d'entrée dans l'URL ¶
Vous pourriez vouloir ne plus avoir le nom du point d'entrée dans les URLS.
Par exemple nos URLs sont de la forme :
http://monsite.com/index.php/news/2017-02-08-il-neige-a-lille.html
Et on souhaite qu'elles deviennent :
http://monsite.com/news/2017-02-08-il-neige-a-lille.html
Voyons comment faire.
Vous devez indiquer sur les balises <url>
ou <entrypoint>
l'attribut noentrypoint="true"
. Ainsi Jelix n'ajoutera pas le nom
du point d'entrée (index.php) dans les URLs concernées.
Ensuite il faut configurer correctement votre serveur web.
Pour Nginx, vous créez une configuration de ce type :
server {
location / {
try_files $uri $uri/ @mysite;
}
location @mysite {
fastcgi_split_path_info ^()(/.*)$;
set $path_info $fastcgi_path_info;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME "$document_root/index.php";
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED "$document_root/index.php";
fastcgi_param SCRIPT_NAME "/index.php";
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
}
Pour Apache, vous pouvez utiliser le module rewrite, d'une façon toute simple : le
chemin appelé sera rajouté après index.php. En clair il faut copier ceci dans un
fichier .htaccess
dans le dossier www/
de votre application (ou dans
la section virtualhost de la configuration apache):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
</IfModule>
Observez bien :
- 2e ligne : vous n'en avez pas besoin si vous écrivez ces instructions dans
un
.htaccess
dans le répertoire www de l'appli. Par contre, dans un autre.htaccess
ou dans un virtual host, vous devez la mettre, et adapter le chemin :/
si le www correspond au documentRoot, ou le chemin pour accéder au répertoire www à partir d'une url (basePath). exRewriteBase /monapp/www/
si vous accédez à l'application parhttp://localhost.local/monapp/www/index.php
- 3e et 4e ligne : Réécrire l'URL seulement si la requête ne correspond pas à un fichier déjà existant, ou un dossier déjà existant. Ainsi on préserve ses dossiers d'image, de feuille de style, etc. de la règle de réécriture.
- 5e ligne : prendre tout le contenu de l'url, et le mettre après index.php/
On retrouve ainsi notre point d'entrée index.php ainsi que notre pathinfo.
Il se peut que sur certaines configurations, vous ayez une erreur apache du type
"Error: No input file specified", en particulier quand PHP est exécuté en CGI
avec cgi.fix_pathinfo=0
dans php.ini. Mais il y a une solution. Il faut changer
la dernière rêgle en :
RewriteRule ^(.*)$ index.php?jpathinfo=/$1 [L,QSA]
Ainsi le but est de mettre le pathinfo dans un paramétre de la requête. Pour que
cela fonctionne pleinement, il faut ajouter dans la configuration de Jelix, dans
la section urlengine
, le paramètre pathInfoInQueryParameter
qui doit
contenir le nom du paramètre (ici dans l'exemple jpathinfo
) :
[urlengine]
...
pathInfoInQueryParameter = jpathinfo