Phalcon公式チュートリアル(1)を和訳しつつ解説（v1.2.6対応）

# Phalcon公式チュートリアル(1)を和訳しつつ解説（v1.2.6対応）

「最速 PHPフレームワーク」Phalconのインストールからサンプルアプリを動かすまでをやります。基本的には[公式チュートリアル](http://docs.phalconphp.com/en/latest/reference/tutorial.html)の流れをなぞります。
<del>が、公式チュートリアルは最新バージョンに追従していない部分があるので、ところどころツッコミを入れながらやっていきます。</del>
<ins>本記事では、公式チュートリアルでは使用していないPhalcon Devtoolsを使用しています。Devtools 1.2.6が生成するコードは、公式チュートリアルに掲載されているものと比較すると、機能はほぼ同等ですが、記述や構成は異なります。</ins>

## Vagrant Boxのセットアップ

インストールにはVagrantを使います。手順を再現しやすいのと、壊しても簡単に作り直せるのが理由です。
[Vagrant](http://www.vagrantup.com/)と[VirtualBox](https://www.virtualbox.org/)のインストールは終わっているものとします。
なお、この記事で使用しているのはVagrant 1.4.3、VirtualBox 4.3.8です。また、開発用マシンはOS X 10.9.2を使用しています。

まず、ローカルのマシンでVagrantを使ってVMを立ち上げます。[Phalconのセットアップ済みのBox](http://phalconphp.com/ja/download/vagrant)があるので、それをベースにします。

<pre>
$ vagrant init phalconbox https://s3-eu-west-1.amazonaws.com/phalcon/phalcon125-apache2-php54-mysql55.box
$ vagrant up
$ vagrant ssh
</pre>

VMにログインできることを確認できたら、一旦ログアウトします。

Vagrantのサーバーの表示をブラウザで確認できるよう、Vagrantfileを変更しましょう。

<pre>
# config.vm.network :private_network, ip: "192.168.33.10"
</pre>

↑という記述があるので、先頭の#を消して保存します。その後、

<pre>
$ vagrant reload
</pre>

すれば、設定ファイルを読み込んでVMを再起動します。

この状態で、 http://192.168.33.10/info.php にアクセスすると、[phpinfo()](http://www.php.net/manual/ja/function.phpinfo.php)の結果が表示されます。ページ内検索で「phalcon」を探すと、Phalcon 1.2.5がインストールされ、有効になっていることがわかります。

なお、Phalconの最新安定版は1.2.6です。1.2.5と1.2.6の差分はわずかなので、学習用であれば1.2.5でも問題無いと思います。この記事では、1.2.6を使います。
## 最新バージョンのインストール

以下で、Phalconの最新版のソースを取得し、インストールします。

<pre>
$ mkdir ~/bin
$ cd ~/bin
$ git clone git://github.com/phalcon/cphalcon.git
$ cd cphalcon/build/
$ sudo ./install
$ sudo /etc/init.d/apache2 restart
</pre>

※Phalcon1.3.0の場合、拡張の読み込みの関係で、上記の手順では動きません。/etc/php5/mods-available/phalcon.ini を作成し、extension = phalcon.so と書いて保存した後、sudo php5enmod phalcon でPhalconを有効にできます。

再度 http://192.168.33.10/info.php にアクセスすると、Phalcon Versionが1.2.6になっているはずです。

## Devtoolsのインストール

次に、アプリの雛形の自動生成をやってくれる、Phalcon Devtoolsのインストールをします。合わせて、PHPライブラリの依存管理ツールであるComposerも導入します。

<pre>
$ sudo apt-get install curl # Composerのダウンロードに使用する
$ cd ~/bin
$ curl -s http://getcomposer.org/installer | php # composer.pharをダウンロード
$ php composer.phar require phalcon/devtools dev-master # Devtoolsをインストール
$ sudo ln -s /home/vagrant/bin/vendor/phalcon/devtools/phalcon.php /usr/bin/phalcon
</pre>

この状態で「phalcon」コマンドを実行すると、「ERROR: Phalcon extension isn't installed」と表示されます。これは、CLI版PHPのphp.iniに「extension=phalcon.so」が記述されていないためです。/etc/php5/cli/php.ini に「extension=phalcon.so」を追加して、再度「phalcon」コマンドを実行すると、今度はPhalcon Devtoolsのヘルプが表示されるはずです。

## チュートリアルプロジェクトのセットアップ

/var/www 配下には、以下のようなファイルが配置されています。

<pre>
$ cd /var/www
$ ls
index.html  info.php  invo  website
</pre>

invoはサンプルアプリです。CRUDやセッション管理等、基本的な操作が実装されているため、参考になります。websiteはPhalconの公式サイトのソースです。

環境構築が完了したので、公式のチュートリアルを進めていきます。

<pre>
$ cd /var/www
$ phalcon create-project tutorial
</pre>

tutorialプロジェクトが作成できました。この時点でのディレクトリ構造とファイル配置は以下のようになっています。
（treeコマンドはデフォルトでは入っていないので、sudo apt-get install treeでインストールしています）

<pre>
$ tree tutorial/
.
|-- app
|   |-- cache
|   |-- config
|   |   |-- config.php
|   |   |-- loader.php
|   |   `-- services.php
|   |-- controllers
|   |   |-- ControllerBase.php
|   |   `-- IndexController.php
|   |-- models
|   `-- views
|       |-- index
|       |   `-- index.volt
|       |-- index.volt
|       `-- layouts
|-- index.html
`-- public
    |-- css
    |-- files
    |-- img
    |-- index.php
    |-- js
    `-- temp

14 directories, 9 files
</pre>

http://192.168.33.10/tutorial/ にアクセスしてみると、「Volt directory can't be written」と表示されます。これは、tutorial/app/cache の書き込み権限の問題です。

<pre>
$ chmod 777 tutorial/app/cache
</pre>

これで、「Congratulations!」と表示されるはずです。

## Bootstrap

tutorial/public/index.phpは以下のようになっているはずです（公式サイトのチュートリアルで示されるソースとは異なる部分もありますが、Devtools 1.2.6で生成したソースは以下のようになっています）。

```tutorial/public/index.php
<?php

error_reporting(E_ALL);

try {

    /**
     * Read the configuration
     */
    $config = include __DIR__ . "/../app/config/config.php";

    /**
     * Read auto-loader
     */
    include __DIR__ . "/../app/config/loader.php";

    /**
     * Read services
     */
    include __DIR__ . "/../app/config/services.php";

    /**
     * Handle the request
     */
    $application = new \Phalcon\Mvc\Application($di);

    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo $e->getMessage();
}
```

includeしているファイルを1つずつみていくと、実際に行われている処理が見えてきます。

## Config

Devtools 1.2.6でプロジェクトを生成した場合、tutorial/app/config/config.php に各種の設定が記載されています。

```tutorial/app/config/config.php
<?php

return new \Phalcon\Config(array(
    'database' => array(
        'adapter'     => 'Mysql',
        'host'        => 'localhost',
        'username'    => 'root',
        'password'    => '',
        'dbname'      => 'test',
    ),
    'application' => array(
        'controllersDir' => __DIR__ . '/../../app/controllers/',
        'modelsDir'      => __DIR__ . '/../../app/models/',
        'viewsDir'       => __DIR__ . '/../../app/views/',
        'pluginsDir'     => __DIR__ . '/../../app/plugins/',
        'libraryDir'     => __DIR__ . '/../../app/library/',
        'cacheDir'       => __DIR__ . '/../../app/cache/',
        'baseUri'        => '/tutorial/',
    )
));

```

次のオートローダで、アプリケーションのディレクトリ設定を読み取る部分があるので、先に\Phalcon\Configオブジェクトを作っています。


## Autoloader

```tutorial/app/config/loader.php

<?php

$loader = new \Phalcon\Loader();

/**
 * We're a registering a set of directories taken from the configuration file
 */
$loader->registerDirs(
    array(
        $config->application->controllersDir,
        $config->application->modelsDir
    )
)->register();
```

オートローダーにコントローラとモデルのディレクトリを登録しています。例えば、 tutorial/app/library/ に共通ライブラリを置くなら、registerDirsの引数の配列に「$config->application->libraryDir」を追加すればOKです。

## 依存管理

PhalconにはDIコンテナという仕組みがあります。アプリケーション全体から利用する機能をDIコンテナに入れておいて、いつでも取り出せるようにしておく、というものです。

```tutorial/app/config/services.php

<?php

// 名前空間のインポートは省略

/**
 * The FactoryDefault Dependency Injector automatically register the right services providing a full stack framework
 */
$di = new FactoryDefault();

// 以下省略
```

    
ここでは、\Phalcon\DI\FactoryDefaultをインスタンス化しています。このオブジェクトに、サービスを登録していきます（\Phalcon\DI\FactoryDefaultは、\Phalcon\DIの別バージョンです。\Phalcon\DIに、Phalconで共通して用いられるサービスを登録したのが\Phalcon\DI\FactoryDefaultです）。

```tutorial/app/config/services.php
// 上の続き

/**
 * The URL component is used to generate all kind of urls in the application
 */
$di->set('url', function () use ($config) {
    $url = new UrlResolver();
    $url->setBaseUri($config->application->baseUri);

    return $url;
}, true);

/**
 * Setting up the view component
 */
$di->set('view', function () use ($config) {

    $view = new View();

    $view->setViewsDir($config->application->viewsDir);

    $view->registerEngines(array(
        '.volt' => function ($view, $di) use ($config) {

            $volt = new VoltEngine($view, $di);

            $volt->setOptions(array(
                'compiledPath' => $config->application->cacheDir,
                'compiledSeparator' => '_'
            ));

            return $volt;
        },
        '.phtml' => 'Phalcon\Mvc\View\Engine\Php'
    ));

    return $view;
}, true);

// 以下省略
```
    
ここでは、urlコンポーネントとviewコンポーネントをサービスに登録しています。この後、db, modelsMetadata, sessionというコンポーネントも登録していますが、やってることは同じなので解説は省略します。要は、アプリケーションで使用する依存関係を全て$diという変数に閉じ込めている、ということです。

## アプリケーションの初期化

```tutorial/app/public/index.php

/**
 * Handle the request
 */
$application = new \Phalcon\Mvc\Application($di);

echo $application->handle()->getContent();
```
    
最後に、$diをパラメータとして\Phalcon\Mvc\Applicationオブジェクトを作ります。

## コントローラー

```tutorial/app/controllers/IndexController.php

<?php

class IndexController extends ControllerBase
{

    public function indexAction()
    {

    }

}
```

コントローラーはデフォルトでは↑のようになっています。以下のように改変してみましょう。

```tutorial/app/controllers/IndexController.php
public function indexAction()
{
    echo "<p>Hello!</p>";
}
```

## ビュー

この状態で再度 http://192.168.33.10/tutorial/ を表示すると、「Hello!」と画面に表示される…とチュートリアルには書いてありますが、表示されません。原因は、ビューのファイルにあります（チュートリアルには「.phtml」とありますが、Devtools 1.2.6では「.volt」です）。

```app/views/index/index.volt

<h1>Congratulations!</h1>

<p>You're now flying with Phalcon. Great things are about to happen!</p>
```
    
indexコントローラで表示されるビューのファイルの中身は、単なる静的HTMLになっています。出力命令を追記してみます。

```app/views/index/index.volt

<h1>Congratulations!</h1>

<p>You're now flying with Phalcon. Great things are about to happen!</p>

{{ content() }}
```

今度は、「Hello!」と表示されるはずです。content()は、コントローラの出力をそのまま表示する命令です。

.voltファイルの中では、通常のPHPのコードも書けます。

```app/views/index/index.volt

<h1>Congratulations!</h1>

<p>You're now flying with Phalcon. Great things are about to happen!</p>

<?php
echo "<p>Hello!</p>";
?>
```

Phalconが提供しているヘルパーメソッドも使ってみましょう。

```app/views/index/index.volt

echo Phalcon\Tag::linkTo("signup", "Sign Up Here!");

```

このヘルパーメソッドは、「&lt;a href="/tutorial/signup"&gt;Sign Up Here!&lt;/a&gt;」という文字列を返します。

## Signupコントローラーの追加

Signupコントローラーはまだないので、以下のコマンドで作成します。

<pre>
$ phalcon create-controller signup
</pre>

対応するビューは自動では作成されないので、手で作成します。

<pre>
$ mkdir app/views/signup
</pre>

```app/views/signup/index.volt

<?php use Phalcon\Tag; ?>

<h2>Sign up using this form</h2>

<?php echo Tag::form("signup/register"); ?>

 <p>
    <label for="name">Name</label>
    <?php echo Tag::textField("name") ?>
 </p>

 <p>
    <label for="email">E-Mail</label>
    <?php echo Tag::textField("email") ?>
 </p>

 <p>
    <?php echo Tag::submitButton("Register") ?>
 </p>

</form>

```

このファイルでは、signup/registerアクションを呼び出していますが、signup/registerアクションはまだありません。そのため、「register」を押すと、「Action 'register' was not found on handler 'signup'」と表示されます。signup/registerアクションを追加しましょう。

```app/controllers/SignupController.php

<?php

class SignupController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function signupAction()
    {

    }

}

```
    
これで、「register」ボタンを押してもエラーは出なくなります（空白ページが表示されるようになります）。

## モデル

モデルを動かす前に、DBにテーブルを作成します。

<pre>
$ mysql -u root -ppass test
</pre>

```sql
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(70) NOT NULL,
  `email` varchar(70) NOT NULL,
  PRIMARY KEY (`id`)
);
```

次に、アプリケーションの側のDB設定を変更します。

```tutorial/app/config/config.php

<?php

return new \Phalcon\Config(array(
    'database' => array(
        'adapter'     => 'Mysql',
        'host'        => 'localhost',
        'username'    => 'root',
        'password'    => 'pass', // '' => 'pass' に変更
        'dbname'      => 'test',
    ),
    // 以下省略
```
        
最後に、モデル自動生成コマンドを実行します。

<pre>
$ phalcon create-model users
</pre>

これで、 tutorial/app/models/Users.php が生成されます。次に、Usersモデルを使用してsignup/registerアクションを実装します。

```app/controllers/SignupController.php

public function registerAction()
{
    $user = new Users();

    //Store and check for errors
    $success = $user->save($this->request->getPost(), array('name', 'email'));

    if ($success) {
        echo "Thanks for registering!";
    } else {
        echo "Sorry, the following problems were generated: ";
        foreach ($user->getMessages() as $message) {
            echo $message->getMessage(), "<br/>";
        }
    }

    $this->view->disable();
}

```

この後、http://192.168.33.10/tutorial/signup で情報を登録して「register」を押すと、情報がDBに登録されます。「phalcon create-model」はバリデータも生成するので、NameやE-Mailが空だったり、E-Mail欄にメールアドレスとして無効な形式の文字列を入れたりすると、バリデーションエラーが発生します。

これで、PhalconのTutorial 1は終了です。お疲れ様でした。