Section: Powerful significant url engine
« basic significant url engine | ^ jUrl: automatic urls |
− Table of content
Here is how you can configure the significant engine for the url system of Jelix.
The configuration principle of this engine is to indicate in a
var/config/urls.xml
file all the possible url forms in the application,
and the actions that will be associated to them. Here is an example of file:
<urls xmlns="http://jelix.org/ns/urls/1.0">
<entrypoint name="index" default="true">
<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>
<classicentrypoint name="foo/bar">
<url handler="urlsig" module="unittest" action="url4" />
</classicentrypoint>
<classicentrypoint name="news">
<url module="news" />
</classicentrypoint>
<entrypoint name="xmlrpc" default="true" type="xmlrpc"/>
<entrypoint name="jsonrpc" default="true" type="jsonrpc"/>
</urls>
Entrypoint tags ¶
The urls
root tag contains the same number of entrypoint
tags as
entrypoints available in your application.
Each of these tags has a name
attribute specifying the name of the entry
point (without the .php extension), or, precisely, its relative path to the
basePath
indicated into the main configuration. And you could set eventually
a default
attribute specifying if this entry point is the default one for
the actual type of request.
An entrypoint
tag should also have a type
attribute which indicates
the type of the entry point : classic
, xmlrpc
, soap
etc. If this
attribute is not present, the default value is classic
.
For compatibility reasons with older version of jelix, you can use other name
for entrypoint
elements. Their exact name gives the type of request they
are affected to :
classicentrypoint
for classical request,xmlrpcentrypoint
for xmlrpc- etc...
However this kind of names are now deprecated.
Url tag ¶
Each entry point defines one or more possible url forms, knowing that those that are not defined will be accepted on an entry point defined as the default one.
Specify a complete url form ¶
According to a pathinfo ¶
You want to indicate the module and the action to execute for a particular url
form. You will then indicate a pathinfo
attribute, specifying the model of
the pathinfo part the url must be like, and the module and the action in the
module
and action
attributes.
The pathinfo
attribute must then contain the value of a pathinfo. In this
example, every url with "/foo/bar" pathinfo will correspond to the module and
the action indicated.
<url pathinfo="/foo/bar" module="hello" action="world" />
You can set the attribute optionalTrailingSlash="true"
if you want that if
there is a trailing slash or not, it correspond to the same action, ie,
/foo/bar/
and /foo/bar
are the same action (by default, it is not the
case). You can set this attribute on the entrypoint
element, so all it is
activated for all defined urls.
According to a pathinfo with undefined parts ¶
It is possible to indicate "dynamic" parts in the pathinfo. They are defined as
two points (:) followed by a name. The retrieved value will then be placed in a
parameter with the same name. In the following example, the pathinfo contains
two dynamical parts: chapter
and id_art
.
<url pathinfo="/articles/:chapter/:id_art" module="cms" action="show" />
If we use the "/articles/planes/544" url, then the chapter
and id_art
parameters will be created and will be the "planes" and "544" value
respectively.
Be careful: to avoid the confusion with other urls, at least one static part is needed (here "/articles/" in the url to distinguish it from the others).
The typed and formatted undefined parts ¶
Another way to avoid resemblances is to specify the format or the type of each parameter. By default, the type is a classical string.
For this, you have to indicate <param>
tags for each parameter which you
want to specify the type/format of. They will have to contain a name
attribute indicating the parameter, and either a type
attribute or a
regexp
attribute, containing a regular expression of the format (without
delimiter). In our example, we want to specify that chapter
is a string, and
a regular expression for id_art
:
<url pathinfo="/articles/:chapter/:id_art" module="cms" action="show">
<param name="chapter" type="string" />
<param name="id_art" regexp="\d+" />
</url>
if the regular expression of the attribute regexp contains parentheses, it's necessary to say that this one should not be catched. Example:
<param name="type" regexp="(?:0|1|2){1}" />
When the parameter is of string type, it is not mandatory to specify a
param
tag. The available types are:
string | a string |
letter | one letter alone |
number | an integer number,equivalent to 'int' et 'integer' too |
digit | a digit |
date | a date according to the AAAA-MM-JJ format |
year | a year on four digits |
month | a month on two digits |
day | a day on two digits |
path | a sub-path of the url. this parameter must be the last one in the URL |
Notice: you must of course indicate the values of these parameters when calling
jUrl
.
Static parameters ¶
It can be sometimes necessary to add "static" parameters, awaited by the action
(which can be attributed to several different urls), but not present in the url.
For this you have to add <static>
elements, with value and name like in
this example:
<url pathinfo="/:category/:product" module="shop" action="view">
<param name="product" regexp="\d{2}" />
<static name="details" value="0" />
</url>
<url pathinfo="/:category/:product/details" module="shop" action="view">
<param name="product" regexp="\d{2}" />
<static name="details" value="1" />
</url>
Here, we use the same action for two different urls. The processing will be
partly different according to the details
parameter. In this case, we would
display the product of a catalogue, with general characteristics, and in the
other its general and detailed characteristics. This avoids to create to
different actions for so small difference.
We can use this mechanism to support language, for instance:
<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>
You should not forget to give the lang
parameter to jUrl.
jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale));
// or
jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>"en_US"));
<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>
Automatic parameters for language ¶
with static parameters ¶
We saw that we could declare static parameters containing the code of the language, to have different URLs pointing to the same action. However, it causes some issues:
- you have to give the parameter to
jUrl::get()
, and in most of case, you use the current language code (jApp::config()->locale
). - in the action you have to set yourself the language code in the configuration, with the given value in the parameter.
Jelix 1.4 brings some improvements about these issues. You just have to add the attribute type="locale"
or type="lang"
, and then, everything is automatic. You can define for example:
<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>
If you call simply jUrl::get('cms~page:view', array('page'=>'foo'))
, then jUrl
will generate /articles/english/foo
if the current language is "en_US", or
/articles/francais/foo
if it is "fr_FR". You don't have to indicate the language code
as before: jUrl::get('cms~page:view', array('page'=>'foo', 'lang'=>jApp::config()->locale))
.
However, you can still give it if you want to force the language code (one code different from the default one).
During the call of the page /articles/english/foo
or /articles/francais/foo
,
Jelix will configure automatically the locale. So when the user call the page
/articles/english/foo
, the current language will be "en_US".
You can also use type="lang"
. In this case, you just have to indicate the lang code ('en', 'fr'...),
either in the attribute value
, or to jUrl::get()
.
<static name="lang" value="fr" type="lang" />
With dynamic parameters ¶
Since Jelix 1.4, you can have parameters with the type "locale" or "lang".
<param name="lg" type="lang"/>
<!-- or -->
<param name="lg" type="locale"/>
It allows you to have a language code into the "pathinfo" of the URL, without indicating
it to jUrl::get()
, but alos to configure automatically the current language during the
execution of the action.
Then, the previous example:
<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>
becomes:
<url pathinfo="/articles/:lang/:page" module="cms" action="page:view">
<param name="page"/>
<param name="lang" type="lang" />
</url>
If you just call jUrl::get('cms~page:view', array('page'=>'foo'))
,
then jUrl will generate /articles/en/foo
if the locale is "en_US",
or /articles/fr/foo
if the locale is "fr_FR".
And if a browser calls /articles/en/foo
, the current locale will be
automatically "en_US".
Contrary to Jelix 1.3 and lower, you then don't have to duplicate URL declaration for each lang.
Same principle with type="locale"
, except that jUrl will generate /articles/en_US/foo
.
Using an handler ¶
We saw how to use a system to analyse the content of the pathinfo. However, sometimes it is not enough, and we need a more complex parsing. For example, parts of a pathinfo can be some data stored in a database, and we need to search in the database this parts to get some ids or whatever..
So in some case, we want to create our own parser, called an "handler" (an other solution is to create a new url engine but it is more complicated).
To create an handler, you create a specific class:
class myHandlerUrlsHandler implements jIUrlSignificantHandler {
function parse($url){
if(preg_match("/^\/(.*)$/",$url->pathInfo,$match)){
$urlact = new jUrlAction($url->params);
$urlact->setParam('page',jUrl::unescape($match[1]));
return $urlact;
}else
return false;
}
function create($urlact, $url){
$p=jUrl::escape($url->getParam('page'));
$url->pathInfo = "/$f";
$url->delParam('page');
}
}
The class name should end by "UrlsHandler", and the name of the handler is the
prefix (here "myHandler"). Then the class should be stored in the classes/
directory of a module. The file should be named like prefix
.urlhandler.php.
In our example, it should be myHandler.urlhandler.php
.
The method parse()
should parse the url given as argument (a jUrl
object). If your handler recognize the url, it should return a jUrlAction
object, else it should return false.
The method create()
is called each time the application ask the url
corresponding to an action. This method receive a jUrlAction
object and a
jUrl
object. $urlaction
contains the parameters of the action. This
parameters are already stored in the $url
object. Then you should modify the
$url
object in order to generate the corresponding true url. So in fact, you
should generate the pathinfo, and/or delete some parameters...
Note that you can use jUrl::escape()
and jUrl::unescape()
, to
cleanup strings (by removing some special characters for example).
Then, in the urls.xml
file, you have to specify the handler:
<classicentrypoint name="wiki">
<url handler="myHandler" module="unittest" action="url4" />
</classicentrypoint>
Of course, the handler can be stored in any module:
<classicentrypoint name="wiki">
<url handler="othermodule~myHandler" module="unittest" action="url4" />
</classicentrypoint>
The same URL for several possible actions ¶
Let's imagine that we have a url of the following pattern, /article/54-title
, and
this displays the article number 54 with a view action associated for example:
<url pathinfo="/article/:id_art-:title" module="cms" action="view" />
We want to able to indicate other actions in some case without changing the url, with an action parameter:
/article/54-title?action=edit
/article/54-title?action=delete
Notice: we could also do /article/54-title/edit
or
/article/54-title/delete
, with thus several <url>
tags, which would
avoid what follows. But this would not be very good when the url is called by a
form for example.
To specify the authorized alternative actions, we add an actionoverride
attribute, containing the list of the actions separated by a space or a comma:
<url pathinfo="/article/:id_art-:title" module="cms" action="view" actionoverride="edit,delete" />
Specify that an entry point is dedicated to a particular module ¶
You not always want to indicate a significant url for the actions of particular module. However, you have created an entry point dedicated to this module. All these actions will pass through this entry point. You just then have to do it like this:
<classicentrypoint name="news">
<url module="news" />
</classicentrypoint>
You can specify several module like that on a same entry point.
Specify some secured urls (https) for specific actions ¶
For some actions, you could want to access them through an https access. So you
should add a https
attribute (with "true" as a value) on the <url>
elements you want. If you want to specify https on all actions of an entrypoint,
then you can put the https
attribute on the entrypoint element instead of
each url element.
Including urls files of modules ¶
You can define URLs in separate files, in each module, and then
declare those files into the main urls.xml
file. It avoid to have a big main
urls.xml
file, and more important, to avoid to declare or rewrite all url
of a module in this file, when you install a module made by someone.
To do it, you shoud create a file, urls.xml
(or with an other name), in
the directory of the module. This file should have a suburls
element as
root element, instead of urls
, and have all needed url
element to
declare all urls mapped to each action of the module. You should not use
entrypoint
elements of course. Here is the urls.xml file of the jauth
module:
<?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>
And in the main urls.xml file, in one of your entrypoint
element, you indicate:
<entrypoint name="index">
...
<url pathinfo="/auth" module="jauth" include="urls.xml" />
</entrypoint>
It means: urls declared in the urls.xml
file of the module jauth should be
included, and should be prefixed by the given pathinfo "/auth/". So the url of
the "jauth~login:in" action will be "/auth/dologin".
You notice that URLS defined in a module file are not "full" URL. It allow the developer to choose the first part of URLs, and it avoid to have conflict with other URLS declared in other modules.
In module files, you can declare URL with a static pathinfo, with a pathinfo
with parameters, with an handler, with static values. However you have to
declare an url for all actions. And you cannot of course use the module
attribute.
You have also to create a url file for each type of entry point, if needed.
Mod_rewrite and just removing the name of the entry point in the url ¶
Perhaps you would prefer that the parsing of the urls will be done by the web server, by using for example the rewrite mod in Apache.
Full use of mod_rewrite ¶
You can use mod_rewrite to parse the urls and so to rewrite urls to simple urls (index.php?module=...&action=...).
For that, you should inform jelix that it doesn't have to parse the urls:
[urlengine]
..
enableParser = off
However you should create a urls.xml file, so jelix can generate significant urls when your application need it.
In your .htaccess
file, you should write all rewrite rules (see the apache
manual).
If you want to define urls which don't contain the entry point (index.php for
example), you should tell to Jelix that it musn't insert the entry point into
generated urls. So you you have to add noentrypoint="true"
on the
<url>
element or on the <entrypoint>
element.
Just remove the entry point ¶
Perhaps you would like to remove the name of the entry point in the urls generated and parsed by Jelix. But to do it, you have to use and configure the mod_rewrite module of apache.
For example, you want to have
http://monsite.com/news/2017-02-08-il-neige-a-lille.html
instead of
http://monsite.com/index.php/news/2017-02-08-il-neige-a-lille.html
First, in the urls.xml
file, put the attribute noentrypoint="true"
on the <url>
element or on the <entrypoint>
element.
Second, you should create a .htaccess
file in the www/
directory,
which should contains this rules:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
</IfModule>
- second line: you don't need it if you write these instructions into an
.htaccess
in the www directory of your app. However, in an other.htaccess
or in the virtual host configuration, you have to write it, and you must indicate the right path to the www directory:/
if the documentRoot is the www directory, or the path to access this www directory from the documentRoot of the website (basePath). Example:RewriteBase /myapp/www/
if you access to your application withhttp://localhost.local/myapp/www/index.php
- third and forth lines: url will be rewritten only if doesn't correspond to a real file or a real directory. (for css files, images etc..).
- fifth line: we get all the content of the url and put it after index.php/, so the url become the pathinfo of the new url.
In some apache configuration, you could have an apache error "Error: No input
file specified" (this is the case for example when PHP is executed in cgi mode
and cgi.fix_pathinfo=0
in php.ini).
There is a solution. You should change the last rule to:
RewriteRule ^(.*)$ index.php?jpathinfo=/$1 [L,QSA]
So the url is not put as the pathinfo, but as a query parameters. Of course, you
should inform jelix of this parameter. You have to add the
pathInfoInQueryParameter
parameter in the urlengine section in the jelix
configuration file. pathInfoInQueryParameter
should contains the name of the
parameter (here in the example, jpathinfo):
[urlengine]
...
pathInfoInQueryParameter = jpathinfo