Blogチュートリアル


公式のドキュメントを見ながらLithiumで最初に作るアプリケーションとしてBlogチュートリアルをやってみます。

参照元00_quickstart/blog.wiki

まずエラーを表示する為に、bootstrapに下記の記述をします。
vi app/config/bootstrap.php

ini_set('display_errors', 1);

コネクションの設定

vi app/config/bootstrap/connections.php

/**
 * コメントアウトして編集する
 */
 Connections::add('default', array(
    'type' => 'MongoDb',
    'host' => 'localhost',
    'database' => 'my_app'
 )); 

最初のパラメータがコネクション名となり、特に指定しなければこのコネクションをLithiumのモデルが使用します。次の配列オプションにはtypeはクラス名か名前空間にadapterが含まれるものを指定するようです。ここでは、ローカルホストのMongoDBのmy_appというデーターベースを使用するよう設定しています。

CouchDbの場合
Connections::add('default', array(
  'type' => 'http',
  'adapter' => 'CouchDb',
  'host' => 'localhost',
  'database' => 'my_app'
));

たとえばCouchDbだと、名前空間が、lithium\data\source\http\adapterでクラス名がCouchDbになってますので、typeが"http"、adapterがCouchDbになります。

MySQLの場合
Connections::add('default', array(
 'type' => 'database',
 'adapter' => 'MySql',
 'host' => 'localhost',
 'login' => 'root',
 'password' => '',
 'database' => 'my_app'
));

ついでにMySQLの場合ですが、名前空間lithium\data\source\database\adapterでクラス名がMySqlなので、typeがdatabaseでadapterがMySqlになります。

モデルの作成

モデルはドメインロジックを処理します。
vi app/models/Post.php

<?php

namespace app\models;

class Post extends \lithium\data\Model {

}

?>

実にシンプルですが、バックグラウンドでコネクションを使ったり、postsというMongoDBのコレクションを使っています。最初にスキーマを設定してなくともMongoDBが自動的に作成するようです。

ちなみにLithiumはコーディング規約が結構きっちりしています。上記のようななんでもないサンプルコードもそれに習っています。PHPでは終了タグ(?>)を省略することが多いですがLithiumはそれを推奨していなくきっちりと終了タグを書くようです。そしてnamespaceの前後に空行を入れる、終了タグの前には空行入れるなどこだわっています。それらに違反してないかをQAツールで自動チェックしているようです。

コントローラの作成

vi app/controllers/PostsController.php

<?php

namespace app\controllers;

class PostsController extends \lithium\action\Controller {

    public function index() {
        return array('foo' => 'bar', 'title' => 'Posts');
    }
}

?>

クラス名と同じくファイル名がキャメルケースになっており、ファイルパスはネームスペースとマッチし、アンダースコアになっています。
Lithiumのアクションは連想配列をreturnすることでビューにデータを送ります。上記では$titleという変数で'Post'、$fooという変数で'bar'という文字列にアクセスできます。compactも使用可能のようです。

ビューを作成する

vi app/views/posts/index.html.php

Lithium is less dense than <?=$foo;?>ium.


<?= ?>

を使っていますが、PHPのshort_open_tagではなくLithiumが用意していて自動的にエスケープするようです。エスケープしたく無いときはいつもの

<?php echo ?>

を使えばいいとマニュアルには記載されていました。

ビューにphpphpphpphp...と書きまくって「どんだけPHPの宣伝させるのか!」と思いませんか。これって冷静に考えてみると他の言語ではあり得ない文化ですがPHPの世界では脳が思考停止したかのように普通になってますよね。Lithiumのこの機能は「もう目を覚ませ!」と我々の頬をビンタしているようなものなのだと思います。

最初の投稿

投稿の追加を処理する空のアクションを追加します。

vi app/controllers/PostsController.php

    public function add() {
    }

ビューにフォームを作成します。

vi app/views/posts/add.html.php

    <?=$this->form->create(); ?>
        <?=$this->form->field('title');?>
        <?=$this->form->field('body', array('type' => 'textarea'));?>
        <?=$this->form->submit('Add Post'); ?>
    <?=$this->form->end(); ?>

フォームの画面が表示されました。

パラメータが渡されないcreateフォームヘルパーは現在のコントローラのaddメソッドとLithiumは判断し/posts/addが指定されます。
次にコントローラでフォームからPostされたデータを処理させます。

public function add() {
    $success = false;

    if ($this->request->data) {
        $post = Post::create($this->request->data);
        $success = $post->save();
    }
    return compact('success');
}

上記の処理では、まずデータがPostされているかどうかをチェックして、リクエストデータでPostモデルをcreateしています。
次のコードは同じで配列でもオプジェクトとしても使えるということのようです。

$post = Post::create(array('title' => 'First Post', 'body' => 'Body text!'));

$post = Post:create();
$post->title = 'First Post';
$post->body = 'Body Text';

この段階ではコントローラはモデルの参照がないので、Post::create()メソッドが呼び出せません。namespace宣言の下にuse app\model\Post行を追加します。

<?php
    namespace app\controllers;

    use app\models\Post;

    class PostsController extends \lithium\action\Controller {

        // ...

    }
?>

個人的にCakeのusers = array('Post')というのは訳分かんなかったんですが、こちらの方が直感的に感じます。

データをセットしたのでsave()メソッドを読んで結果をビューに返します。

<?php if ($success): ?>
    <p>Post Successfully Saved</p>
<?php endif; ?>


うまく出来たようです。データが入っていることも確認してみます。

$ mongo
MongoDB shell version: 1.6.6-pre-
connecting to: test
> use my_app
switched to db my_app
> show collections
posts
system.indexes
> db.posts.find();
{ "_id" : ObjectId("4d95146ca8a9de9f02000000"), "title" : "はじめての投稿です。", "body" : "投稿内容です。投稿内容です。投稿内容です。投稿内容です。投稿内容です。投稿内容です。投稿内容です。" }

投稿をチェックする

データベースにデータを格納したので、Postモデルを使ってデータをビューに表示します。Postsコントローラのindexアクションを下記のように書き換えます。

    public function index() {
        $posts = Post::all();
        return compact('posts');
    }

Post:all()から返ったオブジェクトはLithiumのDocumentオブジェクトになります。DocumentオブジェクトはMongoDBのレスポンスを適切に処理するレンスポンスオブジェクトになります。

デバッグ目的でdataをインスペクトする必要がある場合はDocumentオブジェクトのto()メソッドを使うとよいようです。

ビューに投稿情報を表示してみます。
vi app/views/posts/index.html.php

    <?php foreach($posts as $post): ?>
    <article>
        <h1><?=$post->title ?></h1>
        <p><?=$post->body ?></p>
    </article>
    <?php endforeach; ?>

一通りブログチュートリアルを実行してみました。MongoDBのことや最後Media.phpコメントアウトしてjsonデータをどうのこうのといったことが記載されていましたがとりあえず無視しました。

まあ、一通りやってみた感想は、

  • ビューでオブジェクトのプロパティとして値が表示できるのは良い
  • ビューでエスケープを自動でやってくれるshort_open_tagをエミュレートしてくれる機能はいい。
  • 確かに、Post::find('all')よりもPost:all()の方がOOPっぽい
  • 今のところ悪いところがみつからないがJOINもできないドキュメント指向DBでほんとにまともなアプリケーションができるのかとNoSQL知らない人は不安がよぎる

ぐらいですかね。次からは今回もそうでしたけど公式のドキュメントを意訳というか独自解釈しながらやっていく予定です。基本的に英語よく分からないですから。