Author Archive

    はてなブックマーク - CakePHP 静的コンテンツのURLについて
    このエントリーをはてなブックマークに追加

    こんにちは、一年前に買ったデニムのサイズが合わなくなってきた島田です。

    皆さん食べ過ぎには注意しましょう。。。
    (俺が悪いんじゃない、会社周辺に美味しいお店が多いからいけないんだ!!)

    では、早速本題。

    CakePHP環境下で静的コンテンツは、通常『インストールフォルダ/app/webroot』の配下に設置しますよね。

    /app/webroot/test/index.html

    にファイルを設置すれば、

    example.com/test/index.html
    example.com/test/

    といった感じのURLでアクセスできます。

    ではこの状態で、 example.com/test (『/』なし)にアクセスするとどうでしょう?

    どん!

    example.com/app/webroot/test/

    そうなんです。

    『/app/webroot/』 が付いた状態にリダイレクトされちゃうんです。
    mod_rewite をとおさず直接アクセスしてますねぇ。

    画像やCSSファイルへのパスは、相対的位置関係が変わらないので、表示が崩れるということはありませんが、なんだか気持ち悪い。。。
    同じ情報のページに2URLからアクセス出来るのはSEO的に良くないし、
    Google Analytics などでアクセスログを取得しているときは、同一コンテンツなのに集計結果がわかれてしまう。。。

    ということで、こいつを解決しちゃいます。

    まずは Apache のログを見てみよう

    問題の解決に移る前にまず Apache のログを見てみましょう。

    192.168.0.1 - - [19/Dec/2010:23:08:06 +0900] "GET /test HTTP/1.1" 301 354 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
    192.168.0.1 - - [19/Dec/2010:23:08:07 +0900] "GET /app/webroot/test/ HTTP/1.1" 200 928 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"

    301エラーを返したあとで、『/app/webroot/test/』にリダイレクトしています。

    簡単にいうとこんな感じです。

    1. スラッシュなしでアクセスしたため、Apache がファイルとして『test』を検索
    2. ファイルは見当たらなかったがディレクトリは見つかったので、クライアントに301エラーとリダイレクトメッセージ(『/』つきのURL)を返す
    3. リダイレクトメッセージにあるURLに再度リクエストを投げる
    4. コンテンツを表示

    2. で返ってくる『リダイレクトメッセージ』を直してリダイレクト先を変えてやればいいのか思いきや、
    それだと直接『example.com/app/webroot/test/』にアクセスされてしまう。。。

    というわけで、今回は

    example.com/app/webroot/test/

    に飛んできたURLを

    example.com/test/

    にリダイレクトさせるという方法で問題を解決しちゃいます。

    .htaccess を改変しよう

    CakePHP ではデフォルトで 3つの .htaccess が設置されていますが、今回は『インストールフォルダ/app/webroot/.htaccess』を改変します。

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
    </IfModule>

    インストール時のままだとこんな感じです。
    これをこんな感じに改変します。

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
    RewriteCond %{THE_REQUEST} "^(.+?) (.*?)/app/webroot/(.*?) (.+?)$"
    RewriteRule ^(.*?)$ %2/%3 [R=301,L]

    </IfModule>

    THE_REQUEST とは 先程のログの 『”GET /app/webroot/test/ HTTP/1.1″』 の部分を返します。
    要するにホストネーム以下のURLに /app/webroot/ が含まれていたら、それを省いたものへリダイレクトすると書かれています。

    Apache のログはこんな感じ

    192.168.0.1 - - [19/Dec/2010:23:18:06 +0900] "GET /test HTTP/1.1" 301 354 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
    192.168.0.1 - - [19/Dec/2010:23:18:06 +0900] "GET /app/webroot/test/ HTTP/1.1" 301 342 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
    192.168.0.1 - - [19/Dec/2010:23:18:07 +0900] "GET /test/ HTTP/1.1" 200 928 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"

    二回リダイレクトして目的のURLにたどりついているのがわかります。

    最後に

    今回の解法は、Rewriteの条件を追加していますので、どうしてもその部分にコストが追加でかかってしまいます。
    レスポンススピードをシビアに求められるコンテンツには向かない場合もありますので、注意してください。

    それでは、また次回お会いしましょう。
    さようなら。

    追記

    RewriteRule でURLを書き換えた時、自動的に URLエンコード が行われてしまいます。
    クエリ文字列に特殊文字を含む場合、意図しないURLに書き変わってしまいますので、 NEフラグを指定してエンコードを行わないようにした方がよいでしょう。

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
    RewriteCond %{THE_REQUEST} "^(.+?) (.*?)/app/webroot/(.*?) (.+?)$"
    RewriteRule ^(.*?)$ %2/%3 [R=301,L,NE]

    </IfModule>

      はてなブックマーク - jquery.simplesearch.js のご紹介
      このエントリーをはてなブックマークに追加

      はじめまして、同世代が次々と結婚していく現状にそろそろ焦りを感じ始めた島田です。
      (安元さんおめでとーございます!)

      Fusicでは主に、PHP, Javascript, サーバ構築・管理を担当しています。
      今後ともよろしくお願いします。

      早速ですが、今回は 自作 jQueryプラグイン の紹介をさせていただきます。

      内容

      このプラグインは、Firefoxの各種設定ページ(アドレスバーに about:config と入力した時に出てくるページ)のフィルタをイメージして作成しています。
      ※Ajax は使ってないです。

      デモ
      ※ デモのテキストボックスに『FW』と入力すると、Position が FW のリストだけが表示されます。

      行数の多いテーブルなどで、フィルタをかけて表示したい時などにご利用ください。
      デモでは、単純にHTMLのテーブルをフィルタリングしてますが、
      私はよく、環境変数や定数などを度忘れした時なんかに、print_a で表示してフィルタリングをかけたりしています。。

      使い方

      HTML:

      <!-- 検索用テキストボックス -->
      <p><input type="text" name="jquery.search" id="jquerysearch" value="" /></p>
      
      <hr />
      
      <!-- フィルタをかけるリスト -->
      <table id="seachtable">
        <thead>
          <tr>
            <th>No.</th>
            <th>Nationality.</th>
            <th>Position.</th>
            <th>Name.</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td class="test">スペイン</td>
            <td>GK</td>
            <td>ビクトール・バルデス</td>
          </tr>
          <tr>
            <td>2</td>
            <td>ブラジル</td>
            <td>DF</td>
            <td>ダニエウ・アウヴェス</td>
          </tr>
          <tr>
            <td>3</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>ジェラール・ピケ</td>
          </tr>
          <tr>
            <td>5</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>カルレス・プジョル</td>
          </tr>
          <tr>
            <td>6</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>シャビ</td>
          </tr>
          <tr>
            <td>7</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ダビド・ビジャ</td>
          </tr>
          <tr>
            <td>8</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>アンドレス・イニエスタ</td>
          </tr>
          <tr>
            <td>9</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ボージャン・クルキッチ</td>
          </tr>
          <tr>
            <td>10</td>
            <td>アルゼンチン</td>
            <td>FW</td>
            <td>リオネル・メッシ</td>
          </tr>
          <tr>
            <td>11</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ジェフレン・スアレス</td>
          </tr>
          <tr>
            <td>13</td>
            <td>スペイン</td>
            <td>GK</td>
            <td>ホセ・マヌエル・ピント</td>
          </tr>
          <tr>
            <td>14</td>
            <td>アルゼンチン</td>
            <td>MF</td>
            <td>ハビエル・マスチェラーノ</td>
          </tr>
          <tr>
            <td>15</td>
            <td>マリ</td>
            <td>MF</td>
            <td>セイドゥ・ケイタ</td>
          </tr>
          <tr>
            <td>16</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>セルヒオ・ブスケツ</td>
          </tr>
          <tr>
            <td>17</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ペドロ・ロドリゲス</td>
          </tr>
          <tr>
            <td>18</td>
            <td>アルゼンチン</td>
            <td>DF</td>
            <td>ガブリエル・ミリート</td>
          </tr>
          <tr>
            <td>19</td>
            <td>ブラジル</td>
            <td>DF</td>
            <td>マクスウェル</td>
          </tr>
          <tr>
            <td>21</td>
            <td>ブラジル</td>
            <td>DF</td>
            <td>アドリアーノ・コレイア</td>
          </tr>
          <tr>
            <td>22</td>
            <td>フランス</td>
            <td>DF</td>
            <td>エリック・アビダル</td>
          </tr>
          <tr>
            <td>26</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>アンドレウ・フォンタス</td>
          </tr>
          <tr>
            <td>27</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ノリート</td>
          </tr>
          <tr>
            <td>28</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>セルジ・ロベルト</td>
          </tr>
          <tr>
            <td>29</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ビクトール・バスケス</td>
          </tr>
          <tr>
            <td>30</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>ティアゴ・アルカンタラ</td>
          </tr>
          <tr>
            <td>31</td>
            <td>スペイン</td>
            <td>GK</td>
            <td>ルベン・ミーニョ</td>
          </tr>
          <tr>
            <td>32</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>マルク・バルトラ</td>
          </tr>
          <tr>
            <td>33</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>セルジ・ゴメス</td>
          </tr>
          <tr>
            <td>34</td>
            <td>メキシコ</td>
            <td>MF</td>
            <td>ジョナタン・ドス・サントス</td>
          </tr>
          <tr>
            <td>35</td>
            <td>スペイン</td>
            <td>DF</td>
            <td>マルク・ムニエッサ</td>
          </tr>
          <tr>
            <td>36</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>マルティ・リベロラ</td>
          </tr>
          <tr>
            <td>37</td>
            <td>スペイン</td>
            <td>MF</td>
            <td>オリオール・ロメウ</td>
          </tr>
          <tr>
            <td>38</td>
            <td>スペイン</td>
            <td>GK</td>
            <td>オイエル・オラサバル</td>
          </tr>
          <tr>
            <td>39</td>
            <td>スペイン</td>
            <td>FW</td>
            <td>ルベン・ロチーナ</td>
          </tr>
        </tbody>
      </table>

      Javascript:

      $(function(){
        $('#jquerysearch').simplesearch({
          // フィルタをかけた時、表示・非表示にするリスト
          lines: '#seachtable tbody tr',
          // 対象となる文字列が含まれるセレクタ
          //※nullの場合は、lines 全体の文字列を検索
          targets: 'td',
          // 最後の入力から interval秒 経過するとフィルタリングを開始します
          interval: 500
        });
      });
      

      DOMがロードされた時点(ready(fn))で、文字列・jQueryオブジェクトを変数に格納して、検索時にはその変数を参照するなどなるべくスムーズに動せるように実装しています。

      なお、ソースコードはここに置いてますので、ご自由にご利用ください。

      それでは、また次回お会いしましょう。
      さようなら。