Phalcon Devtools modelのオプションを理解する

[Phalcon Devtools](https://github.com/phalcon/phalcon-devtools)は、Phalconの開発支援用のコマンドラインツールです。

Devtoolsの主要な機能は「プロジェクト・コントローラー・モデルの新規作成」だと思います。今回はモデルの機能をみていきます。

## 事前準備

### プロジェクト作成

Phalconのプロジェクトを作成します。

```bash
$ phalcon project myapp

Phalcon DevTools (2.0.3)


  Success: Controller "index" was successfully created.


  Success: Project "myapp" was successfully created.
$ cd myapp/
$ ls -lha
total 32K
drwxr-xr-x 5 vagrant vagrant 4.0K Jun 19 17:03 .
drwxrwxrwx 5 root    root    4.0K Jun 19 17:03 ..
-rw-r--r-- 1 vagrant vagrant  119 Jun 19 17:03 .htaccess
-rw-r--r-- 1 vagrant vagrant  130 Jun 19 17:03 .htrouter.php
drwxr-xr-x 2 vagrant vagrant 4.0K Jun 19 17:03 .phalcon
drwxr-xr-x 8 vagrant vagrant 4.0K Jun 19 17:03 app
-rw-r--r-- 1 vagrant vagrant  123 Jun 19 17:03 index.html
drwxr-xr-x 7 vagrant vagrant 4.0K Jun 19 17:03 public
```

Devtoolsの「phalcon model」コマンドは、カレントディレクトリに「.phalcon」という名前のディレクトリが有るかチェックして、ある場合には実行する、という仕組みになっています（[ソースコード](https://github.com/phalcon/phalcon-devtools/blob/c7d9a3ede6cd6d803f8347d827803f49dd2136e1/scripts/Phalcon/Commands/CommandsListener.php#L48)）。

phalcon modelコマンドはファイルの生成を行うため、コマンドの実行の際はプロジェクトのルートディレクトリ（上記例の場合は/var/www/myapp）で行うようにしてください。

### データベースとテーブルの作成

PhalconはデフォルトではMySQLを使用するようになっているので、MySQLを使います。以下のような感じで適当に作成します。

```mysql
CREATE DATABASE IF NOT EXISTS `myapp`;

CREATE TABLE IF NOT EXISTS `myapp`.`user` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`))
  ENGINE = InnoDB;
```

### DB接続設定

app/config/config.phpを編集します。

```php
<?php

defined('APP_PATH') || define('APP_PATH', realpath('.'));

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

上記ファイルの、username、password、dbnameを適宜書き換えてください。

## コマンドを実行する

プロジェクトのルートディレクトリにいることを確認して、「phalcon model [テーブル名]」を実行します。

```bash
$ phalcon model user

Phalcon DevTools (2.0.3)


  Success: Model "User" was successfully created.

```

成功すると、app/models/に、User.phpという名前で、以下のようなクラスが自動生成されます。

```php
<?php

class User extends \Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     * Returns table name mapped in the model.
     *
     * @return string
     */
    public function getSource()
    {
        return 'user';
    }

    /**
     * Allows to query a set of records that match the specified conditions
     *
     * @param mixed $parameters
     * @return User[]
     */
    public static function find($parameters = null)
    {
        return parent::find($parameters);
    }

    /**
     * Allows to query the first record that match the specified conditions
     *
     * @param mixed $parameters
     * @return User
     */
    public static function findFirst($parameters = null)
    {
        return parent::findFirst($parameters);
    }

}
```

## オプションを試す

phalcon modelコマンドには、DevTools v2.0.2現在、以下のオプションがあります。

```
 --name=s             Table name
 --schema=s           Name of the schema. [optional]
 --namespace=s        Model's namespace [optional]
 --get-set            Attributes will be protected and have setters/getters. [optional]
 --extends=s          Model extends the class name supplied [optional]
 --excludefields=l    Excludes fields defined in a comma separated list [optional]
 --doc                Helps to improve code completion on IDEs [optional]
 --directory=s        Base path on which project is located [optional]
 --output=s           Folder where models are located [optional]
 --force              Rewrite the model. [optional]
 --trace              Shows the trace of the framework in case of exception. [optional]
 --mapcolumn          Get some code for map columns. [optional]
 --abstract           Abstract Model [optional]
```

### --name

テーブル名を指定するオプションです。「phalcon model user」と、「phalcon model --name user」は同じ結果になります。

### --schema

データベース名を指定するオプションです。config.phpで設定していれば、このオプションは特に必要ありません。

### --namespace

クラスの名前空間を指定するオプションです。上に掲載したクラス定義を見ればわかるように、デフォルトでは名前空間は指定されません。

```
$ phalcon model user --namespace=Acme\\MyApp
```

とすると、

```php
<?php

namespace Acme\MyApp;

class User extends \Phalcon\Mvc\Model
```

といった名前空間が含まれるクラスが定義されます。

### --get-set

このオプションを付けると、クラスのプロパティがprotectedになり、publicなgetter/setterが自動生成されます。

```php
    /**
     *
     * @var integer
     */
    protected $id;

    /**
     * Method to set the value of field id
     *
     * @param integer $id
     * @return $this
     */
    public function setId($id)
    {
        $this->id = $id;

        return $this;
    }

    /**
     * Returns the value of field id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }
```

### --extends

```php
<?php

namespace Acme\MyApp;

class BaseModel extends \Phalcon\Mvc\Model
{
}
```

のような、各モデルの共通処理を書いたクラスがあるとします。自動生成したモデルがこのクラスを継承するようにするには、

```bash
$ phalcon model user --extends=\\Acme\\MyApp\\BaseModel
```

を実行します。結果は以下のようになります。

```
class User extends \Acme\MyApp\BaseModel
```

### --exludefields

モデルクラスには追加しないフィールド名を、カンマ区切りで指定します。例えば、

```bash
$ phalcon model user --excludefields=name,birthday
```

なお、本コマンドには2015年6月20日現在バグと思われる挙動があり、--get-setオプションと同時に指定した場合しか動作しません。GitHubで[Pull Request](https://github.com/phalcon/phalcon-devtools/pull/432)を投げているので、将来的には直っていると思います。

### --doc

「Helps to improve code completion on IDEs」と謳ってはいますが、実際のところ、IDE用のDocコメントはこのオプションが無くても生成されます。このオプションを付けると何が変わるかというと、クラスのDocコメントが追加されます（[ソースコード](https://github.com/phalcon/phalcon-devtools/blob/master/scripts/Phalcon/Builder/Model.php#L493)）。

```php
/**
 * User
 *
 * @autogenerated by Phalcon Developer Tools
 * @date 2015-06-19, 18:35:16
 */
class User extends \Phalcon\Mvc\Model
```

### --directory

モデルのベースディレクトリを指定します。ただし、このオプションが働くのは、config.phpのmodelsDirが相対パス指定になっている場合のみです（[ソースコード](https://github.com/phalcon/phalcon-devtools/blob/master/scripts/Phalcon/Builder/Model.php#L142)）。

モデルを任意の場所に出力したい場合、以下の--outputオプションを使います。

### --output

指定したディレクトリにモデルファイルを出力します。

```php
$ mkdir app/models/base
$ phalcon model user --output=app/models/base

Phalcon DevTools (2.0.3)


  Success: Model "User" was successfully created.

$ tree app/models/
app/models/
`-- base
    `-- User.php

1 directory, 1 file
```

### --force

既にファイルが存在する場合、上書きします。ただし、既存のファイルを消して上書きしているわけではなく、もう少し複雑な動作をしています。

具体的には、既存のモデルファイルを読み込んで、既存のメソッドを残したまま、DBの差分を更新します。既存のメソッドは残りますが、既存のプロパティは消えます。

### --trace

「Shows the trace of the framework in case of exception.」ということになってますが、[ソースコード](https://github.com/phalcon/phalcon-devtools/blob/master/scripts/Phalcon/Commands/Builtin/Model.php#L71)をみる限り、このオプションに対応する機能は実装されていないようにみえます。。。

### --mapcolumn

columnMapメソッドを生成します。このメソッドを書き換えることで、DB上のカラム名と、モデルクラスのフィールド名の対応関係を設定できます。

```php
    /**
     * Independent Column Mapping.
     * Keys are the real names in the table and the values their names in the application
     *
     * @return array
     */
    public function columnMap()
    {
        return array(
            'id' => 'id',
            'name' => 'name'
        );
    }
```

### --abstract

最近導入されたオプションで、モデルを抽象クラスとして作成します。

```php
abstract class AbstractUser extends \Phalcon\Mvc\Model
```

AbstractUserの方は抽象クラスとして自動生成された状態のまま置いておいて、アプリケーションのビジネスロジックはこのクラスを継承したクラスに書くようにすると良いと思います。

## おわりに

以上、phalcon modelコマンドのオプションについてみてきました。ちょっと試すだけで踏めるバグがあったり、未実装っぽい部分もあったりで発展途上なところもありますが、便利なのは間違いないので、是非使ってみてください。