CakePHP のモデルでデータベースをリファクタリング

リファクタリングとは言え、いきなりデータベースを変えてしまうのではなくて、database-Model の関係を利用して、「管理のしやすいテーブル名」を、「意味のわかりやすいテーブル名」に変えましょう、という方法です。

# ASP.NET MVC で使う LINQ to Entities や LINQ to SQL なんかは、テーブル名=クラス名が固定(だと思う)なので、この技が使えないのです。

例えば、既存のテーブルが次の 3 つで校正されています。

  • T100 雑誌テーブル
  • T200 出版社テーブル
  • T300 読者テーブル

ああ、なんて【管理しやすい】テーブルなんでしょうかッ!!! おそらく、将来的に雑誌に関係するテーブル名は、「T101」とか「T102」になる、ってことが容易に想像できます。

…が、決して分かり易いテーブル≒プログラミングしやすいテーブルではないですよね。
# こういう管理の仕方は嫌いではないのですが、避けておいたほうがいいというかなんというか。
# ただし、テーブル数が数百を超えると、こんな風に管理せざるを得ない時もありますよね。名前がだぶらないようにとか、名前が長くなりすぎないようにとか、制限があるので。

さて、これをプログラムで扱うときには、次のようにしたいわけです。

  • Magazine 雑誌テーブル
  • Publisher 出版社テーブル
  • User 読者テーブル

ちなみに、CakePHP の命名規則に従えば、

  • magazines 雑誌テーブル
  • publishers 出版社テーブル
  • users 読者テーブル

のように、小文字かつ複数形にするのが良いのですが、あらかじめデータベースができていたり、社内的な命名規則があったりすると、この限りではないのです。

テーブルを次のように作ります。

-- 雑誌テーブル
create table T100 (
 id int,
 name varchar(50),
 T100id int, -- 出版社ID
 price int
);
-- 出版社テーブル
create table T200 (
 id int,
 name varchar(50)
 );
-- 読者テーブル
create table T300 (
 id int,
 name varchar(50),
 age int
 );

な風に、(極悪)テーブル名を付けておいても、Model で書き換えれば OK。

<!-- models/magzine.php -->
<?php
class Magazine extends AppModel
{
	var $name = 'Magazine';
	var $useTable = 'T100';
}
?>

<!-- models/publisher.php -->
<?php
class Publisher extends AppModel
{
	var $name = 'Publisher ';
	var $useTable = 'T200';
}
?>

<!-- models/user.php -->
<?php
class User extends AppModel
{
	var $name = 'User';
	var $useTable = 'T300';
}
?>

これでコントローラーで使うときは、Magazine、Publisher、User が使えるようになります。

<?php
class MagazinesController extends AppController {
	var $name = 'Magazines';
	var $uses = array('Magazine','Publisher');
	function index() {
		$this->set('Magazines',$this->Magazine->find('all'));
		$this->set('Publishers',$this->Publisher->find('all'));
	}
}
?>

まぁ、対応表を作ってちまちま見るよりはいいか、とは思いますけど、結局は「T100」は「magazine」だったしと、言葉の置き換えは必要になるわけですが。

ただし、これでも問題があって、T100.T200id っていう出版社テーブルへのリンクはモロにビューに出てしまうのですよね。

<!-- views/magazines/index.ctp -->
<h2>雑誌一覧</h2>
<table>
	<tr>
		<td>id</td>
		<td>name</td>
		<td>publisher_id</td>
	</tr>
<?php foreach($Magazines as $item) : ?>
	<tr>
		<td><?php echo $item['Magazine']['id'] ?></td>
		<td><?php echo $item['Magazine']['name'] ?></td>
		<td><?php echo $item['Magazine']['T200id'] ?></td>
	</tr>
<?php endforeach ; ?>
</table>
<h2>出版社一覧</h2>
<table>
	<tr>
		<td>id</td>
		<td>name</td>
	</tr>
<?php foreach($Publishers as $item) : ?>
	<tr>
		<td><?php echo $item['Publisher']['id'] ?></td>
		<td><?php echo $item['Publisher']['name'] ?></td>
	</tr>
<?php endforeach ; ?>
</table>

こういうヘンテコな名前「$item[‘Magazine’][‘T200id’]」が残ってしまうので、完全にリファクタリングって訳でもなさそうですなぁ。ま、テーブル名ぐらいは変えられるってことで。

と、思ったらですね。

virtualFields :: Model の属性 :: モデル :: CakePHPによる開発 :: マニュアル :: 1.3コレクション :: The Cookbook
http://book.cakephp.org/ja/view/1588/virtualFields

を見ると CakePHP 1.3 から モデルに仮の名前を付けられるそうです。
テーブル自体を更新できないので、CRUD 全体には使えませんが、ビューで表示するときには便利かと。

<!-- models/magazine.php -->
<?php
class Magazine extends AppModel
{
	var $name = 'Magazine';
	var $useTable = 'T100';
	var $virtualFields = array(
		'publisher_id'=>'T200id' );
}
?>

こんな風に別名を定義しておくと、

<!-- views/magazines/index.ctp -->
<h2>雑誌一覧</h2>
<table>
	<tr>
		<td>id</td>
		<td>name</td>
		<td>publisher_id</td>
		<td>publisher_id</td>
	</tr>
<?php foreach($Magazines as $item) : ?>
	<tr>
		<td><?php echo $item['Magazine']['id'] ?></td>
		<td><?php echo $item['Magazine']['name'] ?></td>
		<td><?php echo $item['Magazine']['T200id'] ?></td>
		<td><?php echo $item['Magazine']['publisher_id'] ?></td>
	</tr>
<?php endforeach ; ?>

のように、再定義した publisher_id で値を取得できます。

すべてのカラムを再定義するのは問題ありでしょうが、こんな風に一時的に変な名前が付いているカラムには適用して良いかと思います。特に *.ctp で表示するときには、「T200id」よりも「publisher_id」のほうが間違いが少なそうですよね。

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