主頁 > Drupal | SEO > Drupal自定義代碼實現URL重寫

Drupal自定義代碼實現URL重寫

PDF版本

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()) {
  // Merge in defaults.
  $options += array(
    'fragment' => '',
    'query' => '',
    'absolute' => FALSE,
    'alias' => FALSE,
    'prefix' => '',
  );
 
 ...
 
  elseif (!empty($path) && !$options['alias']) {
    $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
  }
 
  if (function_exists('custom_url_rewrite_outbound')) {
    // Modules may alter outbound links by reference.
    custom_url_rewrite_outbound($path, $options, $original_path);
  }

我們重點看下面的兩個調用 drupal_get_path_alias 和 custom_url_rewrite_outbound。Drupal通過查詢url_alias表,把要顯示的URL更新為對應的alias就實現了別名的替換。

2). 處理別名的HTTP請求

Drupal在啟動所有模塊之前,先初始化URL,調用如下函數:

function drupal_init_path() {
  if (!empty($_GET['q'])) {
    $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));
  }
  else {
    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
  }
}
 
function drupal_get_normal_path($path, $path_language = '') {
  $result = $path;
  if ($src = drupal_lookup_path('source', $path, $path_language)) {
    $result = $src;
  }
  if (function_exists('custom_url_rewrite_inbound')) {
    // Modules may alter the inbound request path by reference.
    custom_url_rewrite_inbound($result, $path, $path_language);
  }
  return $result;
}

函數 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這兩個函數。

具體代碼如下:

/**
 * Define custom_url_rewrite_inbound()
 * @author robbin
 */
if (!function_exists('custom_url_rewrite_inbound')) {
  function custom_url_rewrite_inbound(&$result, $path, $path_language) {
    {fun_1}_url_inbound_alter($result, $path, $path_language);
  }
}
 
/**
 * Define custom_url_rewrite_outbound()
 * @author robbin
 */
if (!function_exists('custom_url_rewrite_outbound')) {
  function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
    {fun_1}_url_outbound_alter($path, $options, $original_path);
  }
}

其中 {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) {
 
  $arg0 = arg(0); //should be user-name
  $arg1 = arg(1); //should be connections/media/...
 
  if ($arg1) {
    $user_url = drupal_lookup_path('source', $arg0);
    if ($user_url != $arg0 && preg_match('{user/(\d+)}i', $user_url, $matches)) {
      $user_id = $matches[1];
      $result = "user/$user_id/$arg1";
 
      //add this to tell global_redirect not to redirect this url again
      $_REQUEST['q'] = $result;
    }
  }
}
 
function my_redirect_url_outbound_alter (&$path, $options, $original_path) {
  //rewrite user's sub tab url to seo-friendly url
  //such as, user/1/media --> robbin-zhao/media
  if (preg_match('{user/(\d+)/(\w+)}i', $path, $matches)) {
    $uid = $matches[1];
    $tab = $matches[2];
 
    $alias = drupal_lookup_path('alias', "user/$uid");
 
    if ($alias != $path) {
      $path = "$alias/$tab";
    }
    //$path = ''
  }
}


優化過的代碼已經提交到Drupal官方網站,并且已經是一個第三方模塊,大家可以下載使用。
模塊地址:http://drupal.org/project/rewrite_sub_link


聲明: 本站所有文章歡迎轉載,所有文章未說明,均屬于原創,轉載均請注明出處。
本文有效鏈接: http://www.vczhtn.live/2011/12/drupal-custom-url-alias/
版權所有: Drupal與高性能網站架構 http://www.vczhtn.live


, , ,

評論:3

發表評論
  1. avatar
    回復 方域
    13/03/19

    全篇都在說別名的實現過程,沒有講到使用您這個模塊的優缺點呀。

    • avatar
      回復 robbin
      13/03/23

      確實漏了說。

      優點:不使用數據庫(如url_alias表),性能好。尤其是在url alias表比較大的時候。

      缺點:管理沒有url alias那么靈活。

  2. avatar
    回復 vino
    13/09/21

    不得不贊一下你寫教程類文章的風格,詳細,以前只在國外網站見過,國內博主都是一位某個知識點別人知道,就一筆帶過,帶過也沒關系,至少給個鏈接讓別人去補這一塊。
    博主的風格就很好,剛看了ABTEST,至少博主不厭其煩,考慮到每個瀏覽者的入門程度,能讓初學的人一看就懂,真的非常感謝。希望這種詳盡的風格一致保持啊。初入DRUPAL不容易,謝謝你的知識分享

發表評論

電子郵件地址不會被公開。 必填項已用 * 標注


六 + 4 =

您可以使用這些 HTML 標簽和屬性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

引用:0

下面所列的是引用到本博客的鏈接
Drupal自定義代碼實現URL重寫 來自 Drupal與高性能網站架構
頂部
安徽福彩15选5走势图