February 17, 2013

FuelPHP x Pjax のサンプル

2013/06/18 追記:
タイトルの変更は、以下のようにカスタムヘッダを用いた方が良さそうです。

jquery-pjax後にHTMLのタイトルを変更する
http://madroom-project.blogspot.jp/2013/05/pjaxhtml.html

また、監視するイベントはajaxCompleteではなくpjax:completeの方が妥当かもしれません。

jquery-pjaxでリダイレクト
http://madroom-project.blogspot.jp/2013/06/jquery-pjax.html


2013/03/23 追記:
v1.5.3用に、サンプルコードの一部を修正しました。

--

極力ミニマムな内容を意識して、メモしておきます。

確認したブラウザは
* Mac Chrome 24.0.1312.57
* Mac Firefox 18.0.2
* Mac Safari 6.0.2 (8536.26.17)
です。(Winはまだです。)

2013/02/19追記:
IE9(Win)は動かないです。普通の遷移になります。(10で対応??)
普通の遷移にも対応させておくことが重要です。

以下、ポイントです。
* 画面遷移時、URLを変えつつ非同期にコンテンツを取得すること。
* pjax時も非pjax時(直アクセス時)も、HTMLのタイトルが変わること。
* 非pjax時、該当ページのコンテンツが表示されること。

以下、手順です。

(1)
FuelPHP 1.5.2 をDL、展開

(2)
pjaxをDL、展開
https://github.com/defunkt/jquery-pjax
* public/assets/app/pjax/jquery.pjax.js の配置になります。

(3)
以下、各ファイルのサンプルです。正しい場所に正しいファイル名で作成して、そのままコピペで動くと思います。

classes/controller/index.php (Controller_Index クラス)
* pjax時も非pjax時も、とりあえずコレを呼びます。
* 各actionで、後述のController_Ajaxの対応するget_xxxを呼びます。
* pjax時はget_xxxの戻り値をそのまま返却、非pjax時はget_xxxの戻り値をtemplateに当てはめます。
<?php

class Controller_Index extends Controller_Template
{

    private static function is_pjax()
    {
        // TODO: 本来は Input クラスを拡張するのが好ましそう
        return Input::server('HTTP_X_PJAX') !== null;
    }

    public function before()
    {
        parent::before();

        $this->template->title = 'FuelPHP x Pjax';

        // 参考:
        // http://madroom-project.blogspot.jp/2012/09/fuelphpassetassets.html
        Asset::add_path('assets/app/', 'css');
        Asset::add_path('assets/app/', 'js');
        Asset::add_path('assets/app/', 'img');

        // Input::is_ajax() で true を返却させるため
        $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
    }

    /**
     * index ページ
     */
    public function action_index()
    {
        $response = Request::forge('ajax/index')->execute()->__toString();

        if (static::is_pjax())
        {
            // pjax でのアクセス時
            return $response;
        }
        else
        {
            // 直接なアクセス時
            $this->template->title .= ' - INDEX';
            $this->template->set('content', $response, false);
        }
    }

    /**
     * foo ページ
     */
    public function action_foo()
    {
        $response = Request::forge('ajax/foo')->execute()->__toString();

        if (static::is_pjax())
        {
            return $response;
        }
        else
        {
            $this->template->title .= ' - FOO';
            $this->template->set('content', $response, false);
        }
    }

    /**
     * bar ページ
     */
    public function action_bar()
    {
        $response = Request::forge('ajax/bar')->execute()->__toString();

        if (static::is_pjax())
        {
            return $response;
        }
        else
        {
            $this->template->title .= ' - BAR';
            $this->template->set('content', $response, false);
        }
    }

}
classes/controller/ajax.php (Controller_Ajax クラス)
* 各ページのコンテンツ取得メソッド(get_xxx)の他に、HTMLタイトル取得メソッド(get_title)を用意しています。
<?php

class Controller_Ajax extends Controller_Rest
{

    protected $_supported_formats = array(
        'html' => 'text/html',
    );

    public function before()
    {
        parent::before();

        ! Input::is_ajax() and die();
    }

    /**
     * タイトルを取得
     */
    public function get_title($page = null)
    {
        $page === null and die();

        $title = 'FuelPHP x Pjax';

        switch ($page)
        {
            case 'index':
                $title .= ' - INDEX';
                break;
            case 'foo':
                $title .= ' - FOO';
                break;
            case 'bar':
                $title .= ' - BAR';
                break;
            default:
                break;
        }

        $this->response($title);
    }

    /**
     * index ページのコンテンツを取得
     */
    public function get_index()
    {
        $this->response(View::forge('ajax/index'));
    }

    /**
     * foo ページのコンテンツを取得
     */
    public function get_foo()
    {
        $this->response(View::forge('ajax/foo'));
    }

    /**
     * bar ページのコンテンツを取得
     */
    public function get_bar()
    {
        $this->response(View::forge('ajax/bar'));
    }

}
views/template.php
* <a data-pjax ... のリンクをクリックすると"#pjax-container"の中身がpjaxで変化します。
* pjaxの確認用に、YouTube動画を埋め込んで有ります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title><?php echo $title; ?></title>

<?php echo Asset::js('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js'); ?>
<?php echo Asset::js('pjax/jquery.pjax.js'); ?>
</head>

<script>
$(document).ready(function() {
    // pjaxの初期化
    $(document).pjax('[data-pjax] a, a[data-pjax]', '#pjax-container');

    // タイトルの取得
    $(document).on('click', 'a[data-pjax]', function(event) {
        $("title").load("/ajax/title/" + $(this).attr("title"));
    });
});
</script>

<body>

<a data-pjax href="<?php echo Uri::create('index/index'); ?>" title="index">index</a>
<a data-pjax href="<?php echo Uri::create('index/foo'); ?>" title="foo">foo</a>
<a data-pjax href="<?php echo Uri::create('index/bar'); ?>" title="bar">bar</a>

<div id="pjax-container">
<?php echo $content; ?>
</div>

<iframe width="420" height="315" src="http://www.youtube.com/embed/MDBLhdSy5t4" frameborder="0" allowfullscreen></iframe>

</body>
</html>
views/ajax/index.php
<p>Page - index</p>
views/ajax/foo.php
<p>Page - foo</p>
views/ajax/bar.php
<p>Page - bar</p>
以下が確認出来れば、成功です。
* YouTube動画を再生しながら画面遷移をして、動画の再生が途切れないこと。
* 各ページのURLに直接アクセスしてコンテンツが正しく表示されること。
* HTMLのタイトルが正しく変わること。

No comments:

Post a Comment