Drupal中使用URL别名在SEO以及网站用户体验方面非常重要,通常我们使用如下几个模块, - path(核心模块)
- pathauto
- path_redirect
- global_redirect
一般情况下,给一个URL设置一个别名,全站的所有URL都会更新用这个别名来代替原来的URL。 比如: /user/1 —> /robbin-zhao
这样设置的URL会被保存在url_alias表中。 这里有两个术语: 1. outbound URL 输出URL,或者显示/打印的URL。 2. inbound URL 请求URL,可以理解为进来的URL。 了解了术语之后,我们理解一下Drupal处理URL别名的方式,
1). 输出别名 在输出URL的时候,核心函数是URL function url($path = NULL, $options = array()) {
" r, l7 ~6 ^0 Z& h3 [ // Merge in defaults.. S F( x) p1 ?, O4 L# D
$options += array(% Z( n$ ^( d& M+ i1 z
'fragment' => '',
( y$ F9 J8 O# p+ X2 Z2 X2 ]) C 'query' => '',' }2 J( c$ h! }1 x" _/ L0 `8 c
'absolute' => FALSE,
7 H3 d( S$ Z8 Y2 ^/ f2 V 'alias' => FALSE,! _# Z; y0 e/ i k/ [0 s8 F0 |
'prefix' => '',2 Q1 n7 ^. k- w- G, p: ^. ]2 L
);
1 d3 ~. ~2 K2 M: S7 h" C0 |) G W 2 M/ B( k* K! j- ]/ X0 k& p( i
...$ h5 S: u9 F/ i$ ~1 [- i
$ D8 z9 p5 _, v/ D0 n& J4 Q elseif (!empty($path) && !$options['alias']) {
. [6 K: `) a7 k* M $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
- @+ ~* g2 `% v0 x }
. c( A- L# j) L5 S. a% ~ 5 I# D7 V/ A. ?& v o2 K! u
if (function_exists('custom_url_rewrite_outbound')) {5 L, Y( }' h6 }( }, @
// Modules may alter outbound links by reference.
& |; O; Y. w/ E# k custom_url_rewrite_outbound($path, $options, $original_path);
# @4 e. M1 ^3 ?% s" z5 h } 我们重点看下面的两个调用 drupal_get_path_alias 和 custom_url_rewrite_outbound。Drupal通过查询url_alias表,把要显示的URL更新为对应的alias就实现了别名的替换。 2). 处理别名的HTTP请求 Drupal在启动所有模块之前,先初始化URL,调用如下函数: function drupal_init_path() {- n. {, f/ [! ] N' E3 y1 ?
if (!empty($_GET['q'])) {* ]" B I/ D. w1 X, O8 T' w
$_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));
/ e. P6 w9 K. p& X7 D f }; ]1 D; b" a# \; Y7 J! A1 E
else {
/ i) C7 N' B! Y# p# a2 W9 D: V/ P* t $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));" q$ f' l9 @6 I- X' C0 g& ]( W
}# W3 |1 a' q4 w) t9 K
}% H* ^3 v0 |. w
! i% m2 u$ G2 |2 k+ Y7 B. u
function drupal_get_normal_path($path, $path_language = '') {
1 r5 v; F6 f2 ], R $result = $path;0 T! Y* e5 P, N* ]! w
if ($src = drupal_lookup_path('source', $path, $path_language)) {7 e# r: L* }4 k
$result = $src;
( K1 Z4 y `" P: ? }5 k! C# f1 A% ?( H/ N" F. z
if (function_exists('custom_url_rewrite_inbound')) {4 b6 e4 a, z" J5 X: h
// Modules may alter the inbound request path by reference.
$ J" A4 v$ l' Y1 k: q3 k custom_url_rewrite_inbound($result, $path, $path_language);
& |* d2 x& R. {/ Q. d# V- h$ G }1 H/ Q4 G* V5 d6 p5 ? ?
return $result;
) `2 a) b. J6 U4 z} 函数 drupal_get_normal_path 主要是查询url_alias表,得到当前URL的实际地址,比如 user/1, 然后把这个URL赋给 $_GET['q']来实现具体的URL重写功能。 在有些情况下,我们需要批量修改一些URL的别名,如果我们用drupal默认的url_alias, 但又有一些问题,首先,更新所有的URL脚本比较繁琐,数据量大的情况需要batch,操作数据不方便。其次,如果用户量大,会产生严重的Drupal性能问题,因此,可以考虑到不用url_alias,举个例子,比如我们希望更新user下面的所有tab url, 如:user/1/info, user/1/blog, user/1/message,user/1/mail … 每个用户有多个URL需要更新,如果有1百万用户,那么就会有上百万、千万的alias数据,对于维护、性能都是很大问题。 自定义函数实现URL重写 通过查看Drupal的URL流程,可以发现,Drupal在处理输出URL的时候,会调用一个自定义函数:custom_url_rewrite_outbound,在处理HTTP请求的URL时, 也会调用一个自定义函数:custom_url_rewrite_inbound,所以我们可以实现这两个函数来实现URL重写。 注意,由于这是单个函数而不是hook,如果每个函数都实现,很容易相互冲突,比如fb模块(facebook),url_alter(用于自定义代码来实现URL重写,主要实现了上面的两个函数)。但是由于这两个函数容易冲突(不是hook),其次,url_alter对inbound URL处理有问题,因为Drupal在调用custom_url_rewrite_inbound这个自定义函数的时候,是在加载所有模块之前,所以把这个函数写在module文件里面,根本掉用不到,这里提供一个目前较为合理的解决方案: - 写一个inc文件,放到(任意)自定义模块下面,比如 my-core/my-core.rewrite.inc
- 修改settings文件,include这个文件。比如 include “sites/all/modules/custom/my-core/my-core.rewrite.inc”;
- 在该文件中加入inbound和outbound这两个函数。
具体代码如下: /**% \8 C) k8 M, V6 S e F
* Define custom_url_rewrite_inbound()4 T# X! a$ _5 j! ? {/ N3 M
* @author robbin8 t7 r4 n0 Z0 d; m0 x
*/0 e* m7 }5 R9 r. i7 e* {9 d3 g7 x
if (!function_exists('custom_url_rewrite_inbound')) {
( s2 W" Q m3 k% ]+ L function custom_url_rewrite_inbound(&$result, $path, $path_language) {& ]: i0 x \9 H O! ^, W) K
{fun_1}_url_inbound_alter($result, $path, $path_language);
" v/ D+ j6 H3 z0 E; u i" f }
/ I, B0 _+ m& W- x}
: C8 h/ @5 {* ^# e; Z
6 `& _, N* Y0 o0 q% t/** b9 X1 q* \1 P% W
* Define custom_url_rewrite_outbound()
' ]% {! R i; M# w * @author robbin
9 i4 X1 H; \8 p7 h, Y */
9 e- [6 U9 l- |6 x8 uif (!function_exists('custom_url_rewrite_outbound')) {8 Q7 \% ]1 P) X% w1 |6 i, {, _- ]8 }0 h
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
% J- Z k2 B; e1 z: i1 R$ v! g( w {fun_1}_url_outbound_alter($path, $options, $original_path);
* }0 c" f( y) H5 g# E }: A0 ~6 x8 X% W* ~0 r4 }9 z/ y4 v6 ?
} 其中 {fun_1}_url_inbound_alter、{fun_1}_url_outbound_alter 表示一组处理inbound/outbound的函数,命名规则最好按照如上方式,因为一些第三方模块以及hook都是这个规则,容易理解。 可以添加多个函数,比如{fun_2}_url_inbound_alter等等,每添加一个,在上面的位置调用函数,以做到每组不通功能的函数分开。如果第三方模块,也需要实现重写,一般情况下,这些模块会实现类似 {module}_url_inbound_alter这样的函数,直接把这个函数加到上面对应的位置来调用即可,比如(facebook模块的fb_url_inbound_alter等)。这里给出函数的简要说明: //修改result的值为最终实际的URL $result是引用传值 hook_url_inbound_alter(&$result, $path, $path_language); //修改$path的值为想要的别名的URL $path是引用传值 hook_url_outbound_alter(&$path, $options, $original_path);
最后,还有一点要注意,自定义inbound函数,有时可能会和global_redirect冲突,(没用这个模块,写了类似的函数,也会冲突),因为redirect模块会检查当前的真是url(从outbound中获取)和当前请求的URL不一样,比如真实url是user/1,而当前的请求是 robbin-zhao,它会自动跳转,导致一个无限循环跳转的bug。 解决办法就是在inbound函数里面设置一个全局变量,阻止继续调转。设置 $_REQUEST['q'] = $result; 的值为最终实际URL的值,而不是别名。 示例代码 function my_redirect_url_inbound_alter (&$result, $path, $path_language) {
% R- P' e @2 \ t& S' q6 E/ B 1 `. U0 g" O+ h$ g- x. [
$arg0 = arg(0); //should be user-name* }6 @7 t" ?! X0 h
$arg1 = arg(1); //should be connections/media/...* E% w" v% H5 [) f! o
# y/ z/ n- R* C# D
if ($arg1) {
- Z* R0 O% z4 Q6 Q8 n1 b $user_url = drupal_lookup_path('source', $arg0);2 x/ w5 k8 T5 X0 q+ z9 t" r
if ($user_url != $arg0 && preg_match('{user/(\d+)}i', $user_url, $matches)) {
: [1 L2 H6 v! P% K" C $user_id = $matches[1];
( k3 x L a: A; p" L5 z $result = "user/$user_id/$arg1";
% j7 z% \) p( T8 y+ G. z* q9 w' i) W) i8 Q - z" V8 G7 J$ H0 |& r% k, ?
//add this to tell global_redirect not to redirect this url again
: [ c* u3 @5 C) I $_REQUEST['q'] = $result;2 R+ n! @" @6 N7 g" g1 R. Q
}0 M+ g# Z; u* b5 D2 l& A
}* }/ H1 W5 t) t) f% B; M& r
}5 u/ A( ]0 y6 `1 }! T
; Q0 H: V# o2 s1 O3 `& u) e$ m7 K
function my_redirect_url_outbound_alter (&$path, $options, $original_path) {# A4 s& Y) L) g7 G# {6 g
//rewrite user's sub tab url to seo-friendly url
% K+ e% H) q; Z/ P' l$ l# f8 r //such as, user/1/media --> robbin-zhao/media! }9 S9 m/ V- C0 b- u; E
if (preg_match('{user/(\d+)/(\w+)}i', $path, $matches)) {7 M* I- S% q! y4 d+ a
$uid = $matches[1];
* ^% x% D# s2 |$ L6 M8 R $tab = $matches[2];5 A5 W, m4 p4 [
7 ]; T1 _6 b4 T" r $alias = drupal_lookup_path('alias', "user/$uid");% H0 u" ]% |7 l) q
' E! p# i8 F# S if ($alias != $path) {6 J& |8 x; {4 c- T, a) d
$path = "$alias/$tab";4 O. W8 I: S# C0 P& V
}# H' G8 j% P$ a8 G: L( k" Y7 k8 f
//$path = ''
+ r6 J; g4 K3 i2 e; U }
' Z3 K4 a1 m& E+ O( a3 p* s} 优化过的代码已经提交到Drupal官方网站,并且已经是一个第三方模块,大家可以下载使用。 模块地址:http://drupal.org/project/rewrite_sub_link
声明: 本站所有文章欢迎转载,所有文章未说明,均属于原创,转载均请注明出处。 本文有效链接: http://www.drupal001.com/2011/12/drupal-custom-url-alias/ 版权所有: Drupal与高性能网站架构 http://www.drupal001.com |