CakePHPのScaffold作成クラスをまとめる

ここ一ヶ月ほど、CakePHPを使ったシステムの仕事をしていました。その中で調べたり作ったりしたものを忘れたときの為にメモ。
まずは、簡単なマスタメンテ画面をScaffold作成ソースをベースに作成したのですが、どれも似たようなソースになるので、できるだけ一つのクラスにまとめられないか検討してみました。まずは、Controllerクラスから。

<?php
class AppController extends Controller {
}

class ModelController extends AppController {
	var $helpers = array('Html', 'Form', 'Time');

	function __construct() {
		parent::__construct();
		$this->indexDataName = strtolower(substr($this->name, 0, 1)) . substr($this->name, 1);
		$this->viewDataName = strtolower(substr($this->modelClass, 0, 1)) . substr($this->modelClass, 1);
		App::import('Model', 'Column');
		App::import('Model', 'Table');
		$this->Column = new Column();
		$this->Table = new Table();
	}
	
	function index() {
		return $this->admin_index();
	}

	function view($id = null) {
		return $this->admin_view($id);
	}

	function add() {
		return $this->admin_add();
	}

	function edit($id = null) {
		return $this->admin_edit($id);
	}

	function delete($id = null) {
		return $this->admin_delete($id);
	}


	function admin_index() {
		$this->{$this->modelClass}->recursive = 0;
		$this->set($this->indexDataName, $this->paginate());
		$this->set('tables', $this->Table->getTableName($this->{$this->modelClass}));
		$this->set('columns', $this->Column->getColumns($this->{$this->modelClass}));
		$this->render('index');
	}

	function admin_view($id = null) {
		if (!$id) {
			$this->Session->setFlash(__('Invalid '. $this->modelClass .'.', true));
			$this->redirect(array('action'=>'index'));
		}
		$this->set($this->viewDataName, $this->{$this->modelClass}->read(null, $id));
		$tables = $this->Table->getTableName($this->{$this->modelClass});
		$columns = $this->Column->getColumns($this->{$this->modelClass});
		$relations = array_merge(
			$this->{$this->modelClass}->hasOne,
			$this->{$this->modelClass}->hasMany,
			$this->{$this->modelClass}->hasAndBelongsToMany);
		foreach ($relations as $key => $value) {
			$tables = array_merge($tables, $this->Table->getTableName($this->{$this->modelClass}->{$key}));
			$columns = array_merge($columns, $this->Column->getColumns($this->{$this->modelClass}->{$key}));
		}
		$this->set('tables', $tables);
		$this->set('columns', $columns);
		$this->render('view');
	}

	function admin_add() {
		if (!empty($this->data)) {
			$this->{$this->modelClass}->create();
			if ($this->{$this->modelClass}->save($this->data)) {
				$this->Session->setFlash(__('The '.$this->modelClass.' has been saved', true));
				$this->redirect(array('action'=>'index'));
			} else {
				$this->Session->setFlash(__('The '.$this->modelClass.' could not be saved. Please, try again.', true));
			}
		}
		foreach ($this->{$this->modelClass}->belongsTo as $key => $value) {
			$list = $this->{$this->modelClass}->{$key}->find('list');
			$attrName = Inflector::pluralize($key);
			$this->set(strtolower(substr($attrName, 0, 1)) . substr($attrName, 1), $list);
		}
		$this->set('tables', $this->Table->getTableName($this->{$this->modelClass}));
		$this->set('columns', $this->Column->getColumns($this->{$this->modelClass}));
		$this->render('add');
	}

	function admin_edit($id = null) {
		if (!$id && empty($this->data)) {
			$this->Session->setFlash(__('Invalid '.$this->modelClass, true));
			$this->redirect(array('action'=>'index'));
		}
		if (!empty($this->data)) {
			if ($this->{$this->modelClass}->save($this->data)) {
				$this->Session->setFlash(__('The '.$this->modelClass.' has been saved', true));
				$this->redirect(array('action'=>'index'));
			} else {
				$this->Session->setFlash(__('The '.$this->modelClass.' could not be saved. Please, try again.', true));
			}
		}
		if (empty($this->data)) {
			$this->data = $this->{$this->modelClass}->read(null, $id);
		}
		foreach ($this->{$this->modelClass}->belongsTo as $key => $value) {
			$list = $this->{$this->modelClass}->{$key}->find('list');
			$attrName = Inflector::pluralize($key);
			$this->set(strtolower(substr($attrName, 0, 1)) . substr($attrName, 1), $list);
		}
		$this->set('tables', $this->Table->getTableName($this->{$this->modelClass}));
		$this->set('columns', $this->Column->getColumns($this->{$this->modelClass}));
		$this->render('edit');
	}

	function admin_delete($id = null) {
		if (!$id) {
			$this->Session->setFlash(__('Invalid id for '.$this->modelClass, true));
			$this->redirect(array('action'=>'index'));
		}
		$this->{$this->modelClass}->getDatasource()->begin($this->{$this->modelClass});
		if ($this->{$this->modelClass}->del($id)) {
			$this->{$this->modelClass}->getDatasource()->commit($this->{$this->modelClass});
			$this->Session->setFlash(__($this->modelClass.' deleted', true));
			$this->redirect(array('action'=>'index'));
		} else {
			$this->{$this->modelClass}->getDatasource()->rollback($this->{$this->modelClass});
		}
	}
}
?>

それぞれのControllerは、自分の名前に紐付けられたモデル名を$this->modelClassに持っているので、$this->{$this->modelClass}と書いてやればそのモデルを呼び出すことが出来ます。
ここでは、Tableモデルがinformation_schemaのtablesテーブルを、Columnモデルがcolumnsテーブルを見に行ってます。そのソースはこちら

<?php
class Table extends AppModel {
	var $useDbConfig = 'info_schema';
	
	
	function getTableName($model) {
		$data = $this->find('first', array(
			'conditions' => array('Table.table_schema' => $model->getDataSource()->config['database'],
								'Table.table_name' => $model->table),
			'fields' => array('Table.table_comment')));
		if ($data['Table']['table_comment']) {
			$data2 = mb_split(';', $data['Table']['table_comment']);
			$data['Table']['table_comment'] = $data2[0];
		}
		return array($model->name => $data['Table']['table_comment']);
	}
}
?>
<?php
class Column extends AppModel {
	var $useDbConfig = 'info_schema';
	
	
	function getColumns($model) {
		$list = $this->find('all', array(
			'conditions' => array('Column.table_schema' => $model->getDataSource()->config['database'],
								'Column.table_name' => $model->table),
			'fields' => array('Column.column_name', 'Column.column_comment')));
		$returnList = array();
		foreach($list as $data) {
			$returnList[$model->name][$data['Column']['column_name']] = $data['Column']['column_comment'];
		}
		return $returnList;
	}
}
?>

これらのモデルを使って、DBのスキーマに定義したテーブルやカラムに対するコメントを取得しています。information_schemaを見る権限が必要というところがちょっと気になる部分ではありますが。
あとは、ModelControllerを継承してControllerを作成してやればOK。