Complete URL management for a Web application involves two aspects. First, when a user request comes in terms of a URL, the application needs to parse it into understandable parameters. Second, the application needs to provide a way of creating URLs so that the created URLs can be understood by the application. For an Yii application, these are accomplished with the help of CUrlManager.
Although URLs can be hardcoded in controller views, it is often more flexible to create them dynamically:
$url=$this->createUrl($route,$params);
where $this
refers to the controller instance; $route
specifies the
route of the request; and $params
is a list of GET
parameters to be appended to the URL.
By default, URLs created by createUrl is in the
so-called get
format. For example, given $route='post/read'
and
$params=array('id'=>100)
, we would obtain the following URL:
/index.php?r=post/read&id=100
where parameters appear in the query string as a list of Name=Value
concatenated with the ampersand characters, and the r
parameter specifies
the request route. This URL format
is not very user-friendly because it requires several non-word characters.
We could make the above URL look cleaner and more self-explanatory by
using the so-called path
format which eliminates the query string and
puts the GET parameters into the path info part of URL:
/index.php/post/read/id/100
To change the URL format, we should configure the urlManager application component so that createUrl can automatically switch to the new format and the application can properly understand the new URLs:
array(
......
'components'=>array(
......
'urlManager'=>array(
'urlFormat'=>'path',
),
),
);
Note that we do not need to specify the class of the urlManager component because it is pre-declared as CUrlManager in CWebApplication.
Tip: The URL generated by the createUrl method is a relative one. In order to get an absolute URL, we can prefix it with
Yii::app()->hostInfo
, or call createAbsoluteUrl.
When path
is used as the URL format, we can specify some URL rules to
make our URLs even more user-friendly. For example, we can generate a URL
as short as /post/100
, instead of the lengthy
/index.php/post/read/id/100
. URL rules are used by CUrlManager for both
URL creation and parsing purposes.
To specify URL rules, we need to configure the rules property of the urlManager application component:
array(
......
'components'=>array(
......
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'pattern1'=>'route1',
'pattern2'=>'route2',
'pattern3'=>'route3',
),
),
),
);
The rules are specified as an array of pattern-route pairs, each corresponding to a single rule. The pattern of a rule is a string used to match the path info part of URLs. And the route of a rule should refer to a valid controller route.
Info: Starting from version 1.0.6, a rule may be further customized by setting its
urlSuffix
andcaseSensitive
options. And starting from version 1.0.8, a rule may also havedefaultParams
which represents a list of name-value pairs to be merged into$_GET
. To customize a rule with these options, we should specify the route part of the rule as an array, like the following:'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
A rule can be associated with a few GET parameters. These GET parameters appear in the rule's pattern as special tokens in the following format:
<ParamName:ParamPattern>
where ParamName
specifies the name of a GET parameter, and the optional
ParamPattern
specifies the regular expression that should be used to
match the value of the GET parameter. In case when ParamPattern
is omitted,
it means the parameter should match any characters except the slash /
.
When creating a URL, these parameter
tokens will be replaced with the corresponding parameter values; when
parsing a URL, the corresponding GET parameters will be populated with the
parsed results.
Let's use some examples to explain how URL rules work. We assume that our rule set consists of three rules:
array(
'posts'=>'post/list',
'post/<id:\d+>'=>'post/read',
'post/<year:\d{4}>/<title>'=>'post/read',
)
Calling $this->createUrl('post/list')
generates /index.php/posts
.
The first rule is applied.
Calling $this->createUrl('post/read',array('id'=>100))
generates
/index.php/post/100
. The second rule is applied.
Calling $this->createUrl('post/read',array('year'=>2008,'title'=>'a
sample post'))
generates /index.php/post/2008/a%20sample%20post
. The
third rule is applied.
Calling $this->createUrl('post/read')
generates
/index.php/post/read
. None of the rules is applied.
In summary, when using createUrl to generate a URL, the route and the GET parameters passed to the method are used to decide which URL rule to be applied. If every parameter associated with a rule can be found in the GET parameters passed to createUrl, and if the route of the rule also matches the route parameter, the rule will be used to generate the URL.
If the GET parameters passed to createUrl are
more than those required by a rule, the additional parameters will appear
in the query string. For example, if we call
$this->createUrl('post/read',array('id'=>100,'year'=>2008))
, we would
obtain /index.php/post/100?year=2008
. In order to make these additional
parameters appear in the path info part, we should append /*
to the rule.
Therefore, with the rule post/<id:\d+>/*
, we can obtain the URL as
/index.php/post/100/year/2008
.
As we mentioned, the other purpose of URL rules is to parse the requesting
URLs. Naturally, this is an inverse process of URL creation. For example,
when a user requests for /index.php/post/100
, the second rule in the
above example will apply, which resolves in the route post/read
and the
GET parameter array('id'=>100)
(accessible via $_GET
).
Note: Using URL rules will degrade application performance. This is because when parsing the request URL, CUrlManager will attempt to match it with each rule until one can be applied. The more the number of rules, the more the performance impact. Therefore, a high-traffic Web application should minimize its use of URL rules.
Starting from version 1.0.5, we may reference named parameters in the route part of a rule. This allows a rule to be applied to multiple routes based on matching criteria. It may also help reduce the number of rules needed for an application, and thus improve the overall performance.
We use the following example rules to illustrate how to parameterize routes with named parameters:
array(
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
'<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
'<_c:(post|comment)>s' => '<_c>/list',
)
In the above, we use two named parameters in the route part of the rules:
_c
and _a
. The former matches a controller ID to be either post
or comment
,
while the latter matches an action ID to be create
, update
or delete
.
You may name the parameters differently as long as they do not conflict with
GET parameters that may appear in URLs.
Using the aboving rules, the URL /index.php/post/123/create
would be parsed as the route post/create
with GET parameter id=123
.
And given the route comment/list
and GET parameter page=2
, we can create a URL
/index.php/comments?page=2
.
Starting from version 1.0.11, it is also possible to include hostname into the rules
for parsing and creating URLs. One may extract part of the hostname to be a GET parameter.
For example, the URL http://admin.example.com/en/profile
may be parsed into GET parameters
user=admin
and lang=en
. On the other hand, rules with hostname may also be used to
create URLs with paratermized hostnames.
In order to use parameterized hostnames, simply declare URL rules with host info, e.g.:
array(
'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
)
The above example says that the first segment in the hostname should be treated as user
parameter while the first segment in the path info should be lang
parameter. The rule
corresponds to the user/profile
route.
Note that CUrlManager::showScriptName will not take effect when a URL is being created using a rule with parameterized hostname.
Also note that if the application is under a sub-folder of the Web root, then the sub-folder
should be removed from the rule. For example, if the application is under
http://www.example.com/sandbox/blog
, then we should still use the same URL rule as described
above without the sub-folder sandbox/blog
.
index.php
There is one more thing that we can do to further clean our URLs, i.e.,
hiding the entry script index.php
in the URL. This requires us to
configure the Web server as well as the
urlManager application component.
We first need to configure the Web server so that a URL without the entry
script can still be handled by the entry script. For Apache HTTP
server, this can be done by turning on the URL
rewriting engine and specifying some rewriting rules. We can create
the file /wwwroot/blog/.htaccess
with the following content.
Note that the same content can also be put in the Apache configuration
file within the Directory
element for /wwwroot/blog
.
Options +FollowSymLinks IndexIgnore */* RewriteEngine on # if a directory or a file exists, use it directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # otherwise forward it to index.php RewriteRule . index.php
We then configure the showScriptName
property of the urlManager component to be
false
.
Now if we call $this->createUrl('post/read',array('id'=>100))
, we would
obtain the URL /post/100
. More importantly, this URL can be properly
recognized by our Web application.
We may also add some suffix to our URLs. For example, we can have
/post/100.html
instead of /post/100
. This makes it look more like a URL
to a static Web page. To do so, simply configure the
urlManager component by setting its
urlSuffix property to the suffix you like.
Signup or Login in order to comment.