国外设计欣赏网站 - DOOOOR.com

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,微信登陆

搜索

[Drupal教程] Drupal自定义代码实现URL重写

[复制链接]
发表于 4-20-2012 21:26 | 显示全部楼层 |阅读模式

Drupal中使用URL别名在SEO以及网站用户体验方面非常重要,通常我们使用如下几个模块,

  1. path(核心模块)
  2. pathauto
  3. path_redirect
  4. 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()) {/ }" I. s$ |2 C( c" v
  // Merge in defaults.
0 t- k+ ?& P/ x; \0 Z  $options += array(7 k  i6 {, _8 L( M# g5 f3 J. \
    'fragment' => '',) ~; S" ~7 t" I- O& G8 g
    'query' => '',
0 ^, q, A' K+ T5 B! o% I6 N    'absolute' => FALSE,
8 c+ {4 f4 Y, Z! X    'alias' => FALSE,' d5 X9 V0 e) `' U, T, n: n
    'prefix' => '',
3 w" R" I: O. w/ Y8 F  );; A( c  u/ J" L1 ]
 . O5 |$ Q1 M1 p0 u: @! u/ u$ Y: K6 o
 ...
1 g$ G1 P- X- t& r7 X/ u 2 |7 ~8 B  [: t  z! _+ y
  elseif (!empty($path) && !$options['alias']) {
! f5 Q9 L6 @1 r/ S1 Q- S( E    $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
7 z1 g4 @& S/ [0 L+ m  }
: V" r$ o: Y: ?5 j 
9 O2 R0 a/ ^2 S4 {; `# d2 `  if (function_exists('custom_url_rewrite_outbound')) {4 w$ ]* n/ U% K4 K
    // Modules may alter outbound links by reference.& ]  E* {4 J& Z6 M+ a4 {9 {5 t+ ]* Z
    custom_url_rewrite_outbound($path, $options, $original_path);
4 L5 ^: Q3 o8 e  }

我们重点看下面的两个调用 drupal_get_path_alias 和 custom_url_rewrite_outbound。Drupal通过查询url_alias表,把要显示的URL更新为对应的alias就实现了别名的替换。

2). 处理别名的HTTP请求

Drupal在启动所有模块之前,先初始化URL,调用如下函数:

function drupal_init_path() {6 x' }: {/ T' _6 H3 p) y! {
  if (!empty($_GET['q'])) {
2 C* `! }% o$ w1 k# R* u7 D    $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));- S9 ]$ H) U5 Y. q- q/ d  v; ]4 n
  }" u0 d. K: F; r* D5 x" t
  else {9 W( M7 q' w8 O
    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));! ^# W0 F8 `' P% _7 }1 u
  }
4 z& y6 R8 Z$ i% F" W}
8 ], x/ l) S4 L/ Z 
1 d& l: _" _/ ?$ J# {function drupal_get_normal_path($path, $path_language = '') {7 [  n$ g0 g3 e9 g( C
  $result = $path;& O7 l' d% i. {/ h$ c
  if ($src = drupal_lookup_path('source', $path, $path_language)) {) j6 b0 v1 y% ~4 M' C
    $result = $src;" Y. g# O0 D. N8 T
  }( N- @4 O& j: V: P8 |0 @
  if (function_exists('custom_url_rewrite_inbound')) {
+ F2 d! Q) n3 E, x% {    // Modules may alter the inbound request path by reference.& j: E% F! e. x' c
    custom_url_rewrite_inbound($result, $path, $path_language);6 Q- K6 j! r0 O) x* ~* L4 H
  }$ j- D5 C9 ~% X) F' B
  return $result;: |5 K0 [  L% q* ]( _6 P0 a
}

函数 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文件里面,根本掉用不到,这里提供一个目前较为合理的解决方案:

  1. 写一个inc文件,放到(任意)自定义模块下面,比如 my-core/my-core.rewrite.inc
  2. 修改settings文件,include这个文件。比如 include “sites/all/modules/custom/my-core/my-core.rewrite.inc”;
  3. 在该文件中加入inbound和outbound这两个函数。

具体代码如下:

/**
9 _& m8 _' X  J( Z0 d) P& `: [ * Define custom_url_rewrite_inbound()4 p+ @3 P: a" p3 a" U) Y) N
 * @author robbin
% T' M7 }1 V' s9 P% A */4 f7 v9 d! R, u+ V. B
if (!function_exists('custom_url_rewrite_inbound')) {
9 e! u( S5 Q9 J& F1 A  function custom_url_rewrite_inbound(&$result, $path, $path_language) {
: I. ]5 N" r: P  ^  K. y    {fun_1}_url_inbound_alter($result, $path, $path_language);8 k+ Y: }6 b/ \& X0 Y
  }) Y% T& u0 b2 W  j3 {* z" h% `
}
6 d6 L* g: t3 v& L% ^ 
& [: }5 W- A- v9 F7 @1 @* W/**: a* i. t! X  X+ o7 @
 * Define custom_url_rewrite_outbound()5 w3 _# \" K: b0 q
 * @author robbin
  g. N! s! X) I */
: x8 Y( o! k- o9 I# Lif (!function_exists('custom_url_rewrite_outbound')) {. l! a0 E, T1 }, }
  function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
! ~. z  L* `* C1 I2 c6 B% Y: a    {fun_1}_url_outbound_alter($path, $options, $original_path);( [. ?* I6 {' {
  }/ u; K: I  z* s' k, L9 l. Y6 }
}

其中 {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) {/ j+ f5 H) h7 W) \3 v
 
6 d1 @  Z% o5 u4 }( \- l- }5 e  $arg0 = arg(0); //should be user-name
- h; H% F4 R+ Y' ~  $arg1 = arg(1); //should be connections/media/...) E* _8 `5 T! t- f. \6 N7 R
 
6 r! W& v! I$ u# b  if ($arg1) {5 h. g7 q& _( b% m; `
    $user_url = drupal_lookup_path('source', $arg0);! Y. @- j9 ?7 C1 U& I$ }
    if ($user_url != $arg0 && preg_match('{user/(\d+)}i', $user_url, $matches)) {& ^4 b# ]& k, C) O* i
      $user_id = $matches[1];9 F8 u. ^! g8 P5 w. X- r& f2 ?6 H
      $result = "user/$user_id/$arg1";# N- |  y5 P) T# J' b, K
 
4 J! i8 _7 E# f5 l      //add this to tell global_redirect not to redirect this url again, \( E/ a6 W5 ]$ y6 J* O' `/ @5 \
      $_REQUEST['q'] = $result;. P1 r) d  K4 b% l( I! p# ~
    }
  f, [! `( p* h7 n( ^  }
9 _+ M" i3 \: D6 @}
/ r6 m- u; W4 W' w 
0 @' j5 V: ^$ h  Q; m0 }function my_redirect_url_outbound_alter (&$path, $options, $original_path) {" V/ E, j1 D( j" V6 r3 ~3 N. |
  //rewrite user's sub tab url to seo-friendly url# [9 z  G% I2 A6 l5 x+ e2 v
  //such as, user/1/media --> robbin-zhao/media+ n- \& k3 [9 |3 M
  if (preg_match('{user/(\d+)/(\w+)}i', $path, $matches)) {. S5 V! n1 G) w& z
    $uid = $matches[1];6 M8 L( b8 g- o1 d) T2 g
    $tab = $matches[2];/ W8 V+ @5 J: U6 B
 
, R& \; }, X  O1 P) S4 {/ j    $alias = drupal_lookup_path('alias', "user/$uid");
3 K1 P/ g% R* c * s2 j  h$ h: n& N3 x+ g
    if ($alias != $path) {
/ ]; h! `& [3 }      $path = "$alias/$tab";
$ {/ g) |. \- T' j) Q; `8 e    }
' s! u8 x0 @% C! [    //$path = ''
: O% f# p3 K. d* }5 u1 q: h3 R  }- h7 X0 x1 h! H8 P2 I
}


优化过的代码已经提交到Drupal官方网站,并且已经是一个第三方模块,大家可以下载使用。
模块地址:http://drupal.org/project/rewrite_sub_link





声明: 本站所有文章欢迎转载,所有文章未说明,均属于原创,转载均请注明出处。 
本文有效链接: http://www.drupal001.com/2011/12/drupal-custom-url-alias/ 
版权所有: Drupal与高性能网站架构 http://www.drupal001.com

|2011-2026-版权声明|平台(网站)公约|手机版|手机版|DOOOOR 设计网 ( 吉ICP备2022003869号 )

GMT+8, 11-11-2025 21:18 , Processed in 0.211733 second(s), 115 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表