CakePHPとWPFの相互運用 cake bake を使って MVC を自動生成する

最初に CakePHP の cake bake all を使って Model/View/Controller を一気に作成したいところですが、既存のデータベースから移行する場合、CakePHP の複数形ってのにひっかかります。

データベース上に store というテーブルがあって、これに対応する MVC を作ろうとすると、こんな風に Model.php 内でエラーがでます。CakePHP のテーブル名のルールとして複数形にするわけで、本来ならば「stores」を探しているのですが、このテーブルがありません、ってな具合ですね。

Error: Table stores for model Store was not found in datasource default.
#0 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(3498): Model->setSource('stores')
#1 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(1355): Model->getDataSource()
#2 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(503): Model->schema(true)
#3 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(801): ModelTask->doAssociations(Object(Model))
#4 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandBakeShell.php(174): ModelTask->bake(Object(Model), false)
#5 C:xampphtdocscakephp-2.4.5libCakeConsoleShell.php(431): BakeShell->all()
#6 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(207): Shell->runCommand('all', Array)
#7 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(66): ShellDispatcher->dispatch()
#8 C:xampphtdocscakephp-2.4.5appConsolecake.php(36): ShellDispatcher::run(Array)
#9 {main}

Model 名は「Store」なんだから、なんとか store テーブルも見つけてくれれば便利じゃないかと思うんですが、これで結構悩みます。
で、テーブル名を変えられる、あるいは新しいシステムの場合はいいんですが、既存のシステムがあったり、それなりの命名規約があたりすると変更は難しいのです。なので、テーブル名はそのまま「store」のままで使いたいですね。

■テーブル名に単数名を使う

そういう場合は、あらかじめ、$useTable に store を指定した Model を作っておきます。そうやって、

<?php
App::uses('AppModel', 'Model');
/**
 * Store Model
 *
 */
class Store extends AppModel {

/**
 * Use table
 *
 * @var mixed False or table name
 */
	public $useTable = 'store';
}
&#91;/code&#93;
<p>
そのあとに、
</p>
<ol>
<li>cake bake Model で Store モデルクラスを作り直す(バリデーションとか)</li>
<li>cake bake all で Store テーブルに対する MVC を作る</li>
</ol>
<p>
という手順を踏みます。こうすると、Store モデルクラスが、「store」テーブルを対象にするという複数形なしの方法で作れます...が、これってコマンドラインでぽちぽち y/n を決めないといけないので、もっといい方法がないものかと思っ、
</p>
[code]
> cake bake Model Store
> cake bake all Store

の2コマンドで一気にできますね。何故か cake bake Model Store のほうは複数系を探さずに単数系のままなので、一気にモデルクラスができます。そのあと、bake all Store でモデル名(テーブル名?)を指定すると、これまた PHPUnit で y を押すだけで、一気に作れます。
なので、「y」だけ書かれた応答ファイルだけ作っておいて次のようなバッチファイルで動かせば大丈夫ですね。

cmd /c cake bake Model Store
cmd /c bake all Store < yes.txt
&#91;/code&#93;
<p>
テーブル全体は適当なperlかPHPでスクリプトを書いてやればOKです。
そうして作成してやると、ひとまず1つのテーブルに対して CRUD ができる MVC が作成されます。
</p>
<a href='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02org1.jpg'><img border='0' src='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02thum1.jpg'/></a>
<p>
この例では、starttime というテーブルに http://localhost:81/cakephp-2.4.5/Starttimes でアクセスをします。コントローラー自体は複数形で生成されるので、元テーブル名と若干ずれてしまうのですが、まあ良しとしましょう。
Person テーブルが People でアクセスするとか、迷う点もあるのですが。
</p>
<p>
■テーブルの外部結合を指定する
</p>
<p>
あらかじめ、テーブルの外部結合をしておけば CakePHP が拾ってくれる?と思うのですが、既存のテーブルには外部結合が設定されていませんでした。まあ、そのまま使うのもよいのですが、せっかく自動生成された View で ID の羅列があるのはデバッグ上面倒くさいです。$belongsTo を使うと Model に外部結合を設定できます。
</p>
<p>
Model クラスに下記な風にアソシエーションの設定を追加します。
アソシエーションの設定は、$belongsTo(多対1)だけを使いました。データ上、親からその子を検索するというのがなくて、子テーブルから親の名前が頻繁に発生するからです。このあたりは、システムの特性に合わせてですね。
</p>
[code lang="php"]
class Store extends AppModel {
	public $belongsTo = array(
	    'Areagroup' => array(
	        'className'    => 'Areagroup',
	        'foreignKey'   => 'AreaGroupID'
	    ),
	    'Sellingcompany' => array(
	        'className'    => 'Sellingcompany',
	        'foreignKey'   => 'SellingCompanyID'
	    )
	);

この例では、Storeテーブルが、AreagroupテーブルとSellingcompanyテーブルに外部結合しているという設定です。子であるStore(お店)が、Areagroup(地域)やSellingcompany(会社)に含まれているというスタイルが直観的に書けます。あとはいろいろ調べたのですが、あまり必要はないでしょう。必要であれば適宜クエリを書いて取ってきてしまいます。
というのも、親から子を探す時にたいていは検索条件を入れるので、単純な $hasMany ではうまくいかないのです。適切な設定をしたり適切なモデルを作ってもよいですが、そうなると特定の検索条件ごとにモデルが必要になってしまいモデルクラス自体が氾濫してしまいます。なので、普通に ER 図にできる範囲でよいかと。

アソシエーションの設定が終わったら View だけを書き換えます。

cake bake View Store

とすると Store モデルに関する View だけを書き換えます。cake bake all をするとせっかく設定した $belongsTo が消えてしまうので注意してください。

アソシエーションをつけたときには、指定先のページが開くリンクが作られます。

■Viewでリンク先の名称を変える

先ほどの SellingCompanyID(会社ID)はわかるのですが、これが数値のままだと編集しづらいですね。これは View のほうを直接修正します。 $belongsTo で設定したときは、$store[‘Sellingcompany’][‘ID’] のように外部連携先のテーブル名とIDが設定されています。ページを開くときは、IDで指定するのですが画面表示の場合は、
$store[‘Sellingcompany’][‘Name’] のように名前を設定しておきます。

<td>
  <?php echo $this->Html->link($store['Sellingcompany']['Name'], array('controller' => 'sellingcompanies', 'action' => 'view', $store['Sellingcompany']['ID'])); ?>
</td>
<td>
  <?php echo $this->Html->link($store['Areagroup']['Name'], array('controller' => 'areagroups', 'action' => 'view', $store['Areagroup']['ID'])); ?>
</td>

こうすることで、会社名と地域名をが表示できます。

実際は、CakePHP は Web API のバックエンドとしか使っていないので、この View は使われません。ですが、最初のデバッグ時には有効なのと、簡単なデータ編集ならばこれで十分なのでそのままにしておきます。

カテゴリー: CakePHP, WPF パーマリンク