|
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()) {& w$ Y! ]7 f) B" P: g6 k
// Merge in defaults.
4 E8 A9 L7 l: G1 I* { $options += array(
: u1 \" v$ e) y9 O 'fragment' => '',* J5 f) i+ [$ w+ \
'query' => '',
. y x0 j0 W9 R- ]1 e: \% i6 m 'absolute' => FALSE,
9 d6 i4 J0 z2 f+ {8 D. {7 T 'alias' => FALSE,
9 }1 I' y3 V3 Q1 p* ?) m 'prefix' => '',
1 |, ~( i3 ]* c ]5 {( c0 ]- r* j/ h );3 ]8 k4 h! H! Q/ I% D
2 s0 s$ T1 d! M5 p6 Y0 C" \. u" y ...
- ^& i2 x0 [7 v% K6 r" ^- |) s 0 D! X& b" J! F: S
elseif (!empty($path) && !$options['alias']) {$ U$ ]" F$ U) Z8 g* ~/ c! A
$path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
! {" e( l% `& l( l5 u2 L6 Y }( r* f# u6 u7 |7 G) K# N. b
( k& j7 D9 ]! ~# c4 S
if (function_exists('custom_url_rewrite_outbound')) {+ c* {( N7 G$ w0 K) g) E
// Modules may alter outbound links by reference.3 K; l6 s6 x# P" d: H! E) B( q
custom_url_rewrite_outbound($path, $options, $original_path);
/ G* U# ]- U a8 }# G }我们重点看下面的两个调用 drupal_get_path_alias 和 custom_url_rewrite_outbound。Drupal通过查询url_alias表,把要显示的URL更新为对应的alias就实现了别名的替换。 2). 处理别名的HTTP请求 Drupal在启动所有模块之前,先初始化URL,调用如下函数: function drupal_init_path() {: H X" `8 b3 j1 L* O& r
if (!empty($_GET['q'])) {- j6 U+ S: u, w3 ]/ @2 ~! O
$_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));' Y" l7 ]( s" t: n# \! P( [
}4 O7 j! b! k0 E/ F5 ^: _8 C& |
else {2 W9 v# k8 R4 c5 A3 E4 z5 t: t- }; V
$_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
) {. k( G1 Q- R" G4 o }( s1 j/ `3 F+ C$ A
}+ x1 _) j. L3 s2 H }
# r6 j+ J% |/ ?* j& [. S# ?function drupal_get_normal_path($path, $path_language = '') {
s5 l [ Z# D( | $result = $path;
7 D6 |( |* H6 k" _ if ($src = drupal_lookup_path('source', $path, $path_language)) {4 m% v$ E& I8 e/ l4 T% Z
$result = $src;6 }0 N# u3 N$ v" A l' ]
}. ]" P3 V" E; q3 @
if (function_exists('custom_url_rewrite_inbound')) {# W, T$ l& M) ]& d' e9 P. o
// Modules may alter the inbound request path by reference.
" t0 p* C' L; l+ d6 H/ x custom_url_rewrite_inbound($result, $path, $path_language);/ I$ `* Q0 h j! C1 H' `0 L' V. E/ [
}
: j* j7 c9 b6 b" W; B1 r, s return $result;( E# D, q$ B% t8 p% ~
}函数 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这两个函数。
具体代码如下: /** ^" O9 o' {# n* }
* Define custom_url_rewrite_inbound()* Y* x. @( x2 E+ a' l$ t
* @author robbin' B; D, I/ L: L/ s9 [5 Z: {+ {5 n
*/3 o9 e: ^' E0 f6 O. F" Q y z& k
if (!function_exists('custom_url_rewrite_inbound')) {) }( _& A+ t" r$ l$ @) F# l
function custom_url_rewrite_inbound(&$result, $path, $path_language) { @9 j1 F; o5 k$ N2 ^1 D
{fun_1}_url_inbound_alter($result, $path, $path_language);
' A2 u3 o6 g( [ x" m6 R& Q( x }
2 j# ^5 l% O7 v5 @& G, M s( L}& }9 _: \ J" V6 }" I
0 ~3 `: T" N' e" j
/**
+ U- S) P" E6 {9 D6 X, _ * Define custom_url_rewrite_outbound()
8 c% L2 ]# l! q * @author robbin" X" p. f0 X# Q! O3 A) O5 Z3 J% L
*/
8 Q: B6 b8 B# K6 oif (!function_exists('custom_url_rewrite_outbound')) {
: w, [$ D- L: i5 M# p function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
* K7 \+ G+ M; O( A7 C5 E {fun_1}_url_outbound_alter($path, $options, $original_path);" M* O3 g8 T! W' z9 ^
}; `1 L+ x2 a9 x0 T4 Z
}其中 {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) {/ I: S2 p! ~4 N* g, }( Y2 s, ]9 G4 c
' ~$ c. _* @( r8 {" j; |8 w $arg0 = arg(0); //should be user-name
( W, D* o. b5 Q& r/ D5 s1 `" K $arg1 = arg(1); //should be connections/media/...
5 L7 }+ m3 p. w( I3 {9 d5 y 7 C( a0 Y4 A% ^# {* r) X( P1 R
if ($arg1) {7 r" ]8 n! z# ~$ `8 `) _5 A7 Q
$user_url = drupal_lookup_path('source', $arg0);
: K7 e$ C/ \( J: K9 O6 x if ($user_url != $arg0 && preg_match('{user/(\d+)}i', $user_url, $matches)) { z/ l! g+ d4 ]& z" i
$user_id = $matches[1];9 J' k; K5 r1 t2 u6 I" M) b
$result = "user/$user_id/$arg1";) B" @ J# G- u2 h2 N1 a
! w: M. c: X5 y' B# z1 C0 A, z
//add this to tell global_redirect not to redirect this url again% C- U; H3 b B N8 j( v% d+ H
$_REQUEST['q'] = $result;7 T' y# i" _' X8 G- I0 | t
}
/ }; u6 R/ r6 K, z8 V4 U, K1 ^( t }
0 y5 ?) }+ P1 w' \3 B" X}9 i* _0 w! D* w% ^
) Z' Y- W* [- W2 L- a0 }1 @$ lfunction my_redirect_url_outbound_alter (&$path, $options, $original_path) {
' w8 ]# r% _* V' O |1 U //rewrite user's sub tab url to seo-friendly url
: Q& [3 e- [1 J7 } //such as, user/1/media --> robbin-zhao/media. J6 Z. y; P; Y# Y8 u1 _; W
if (preg_match('{user/(\d+)/(\w+)}i', $path, $matches)) {
6 o o5 a2 {1 N7 r $uid = $matches[1];
6 N9 A1 F1 C1 X- D- Z $tab = $matches[2];/ y; |7 q! ]9 B) |4 g x$ _
. B$ @! |9 H& s) \, }0 {, d4 U $alias = drupal_lookup_path('alias', "user/$uid");
# S+ b8 q ^4 d7 m
/ W! t1 ~! z1 a; J y Y if ($alias != $path) {
& ~( w1 n0 C6 D! C4 h z& d $path = "$alias/$tab";
+ f" S* d4 I1 d% ]% S/ F& f }, U e7 d9 W0 ^6 q! H
//$path = ''
, e1 Q8 ]& r: D5 l5 K }% ^) U" l1 B) J& _' z: C
} 优化过的代码已经提交到Drupal官方网站,并且已经是一个第三方模块,大家可以下载使用。 模块地址:http://drupal.org/project/rewrite_sub_link
声明: 本站所有文章欢迎转载,所有文章未说明,均属于原创,转载均请注明出处。 本文有效链接: http://www.drupal001.com/2011/12/drupal-custom-url-alias/ 版权所有: Drupal与高性能网站架构 http://www.drupal001.com |