データソースはモデルとモデルが表現するデータの元(ソース)とを
仲立ちするものです。多くの場合、データは MySQL, PostgreSQL, Microsoft SQL Server
といったリレーショナルデータベースから取り出されます。
CakePHP は、以下の一覧にあるようないくつかのデータベース固有の
データソース( lib/Cake/Model/Datasource/Database
を参照)
とともに配布されています。:
Mysql
Postgres
Sqlite
Sqlserver
注釈
上記以外にも、 GitHub 上の CakePHP データソースリポジトリ にコミュニティが開発しているデータソースがあります。
app/Config/database.php
でデータベースの接続設定を行うと、
モデルの操作全般において CakePHP は透過的に対応するデータソースを
使用します。このため、それらのデータソースに関する知識がなくても
利用できるようになっています。
前述のソースはどれも DboSource
から派生したもので、これにより
ほとんどのリレーショナルデータベースで共通な、いくつかのロジックを
集約しています。新しく RDBMS のデータソースを書いてみようという向きには、
まずこれらのどれかを手本にするとよいでしょう
(MySQL または SQLite が最もオススメです)。
ただし、多くの人はリモート REST API さらには LDAP サーバといった、 外部ソースに関するデータソースを書くことに興味を持っています。 なので、我々も現在これらに目を向けつつあるところです。
データソースには少なくとも create
, read
, update
および/または delete
メソッドを実装することができ、また
『実装するべき』です。(現時点では、実際の各メソッドの特徴的な性質や
実装の詳細は重要ではありません。これらについては後述しています。)。
これらのメソッド以外については、特に必要でなければ実装する必要はありません。
さらに、もしリードオンリーのデータソースが必要なら、
create
, update
, delete
これら3つの存在意義はありません。
すべての CRUD (CREATE/READ/UPDATE/DELETE) メソッドのために 実装しなければならないメソッド:
describe($model)
listSources($data = null)
calculate($model, $func, $params)
少なくとも以下のうちのひとつ:
create(Model $model, $fields = null, $values = null)
read(Model $model, $queryData = array(), $recursive = null)
update(Model $model, $fields = null, $values = null, $conditions = null)
delete(Model $model, $id = null)
モデルではなくデータソース自身の中で $_schema
クラス属性を
定義することも可能です(これは非常に有用な場合もあります)。
これはそれほど特殊なケースではありません。
データソースをモデルに結合させてもなお、
Model::find()/save()/delete()
はいつも通りに使えます。
また、これらのメソッドを呼ぶときに使われる妥当なデータ
および/またはパラメータも、データソース自身に渡されます。
このため、(たとえば Model::find へのオプションであれば
'conditions'
のパースや 'limit'
さらにはデータソース
自身が使うカスタムパラメータといった)必要と思われるどんな
機能でも実装することができます。
自らデータソースを書き起こしたいと思う理由でよくあるのは、
いつもの Model::find()/save()/delete()
メソッドを使って
サードパーティの API にアクセスしたいという場合です。
ここでは架空のリモート JSON ベースの API にアクセスする
データソースを書いてみましょう。 FarAwaySource
という名前で
app/Model/Datasource/FarAwaySource.php
に置くことにします:
App::uses('HttpSocket', 'Network/Http');
class FarAwaySource extends DataSource {
/**
* データソースの説明(オプション)
*/
public $description = 'A far away datasource';
/**
* デフォルトの設定オプション。これらは ``app/Config/database.php``
* でカスタマイズされ、 ``__construct()`` の中でマージされます。
*/
public $config = array(
'apiKey' => '',
);
/**
* create() や update() を行いたい(実装したい)場合、利用できる
* 項目を指定する必要があります。たとえば固定情報やスキーマの
* 移行など、CakeSchema でやる場合と同じ配列キーを使います。
*/
protected $_schema = array(
'id' => array(
'type' => 'integer',
'null' => false,
'key' => 'primary',
'length' => 11,
),
'name' => array(
'type' => 'string',
'null' => true,
'length' => 255,
),
'message' => array(
'type' => 'text',
'null' => true,
),
);
/**
* HttpSocket を生成し、設定の調整を行う。
*/
public function __construct($config) {
parent::__construct($config);
$this->Http = new HttpSocket();
}
/**
* データソースは通常データベースに接続するので、データベース
* なしでも動くようにいくらか調整する必要があります。
*/
/**
* listSources() はキャッシュ操作を行います。あなたはカスタム
* データソースでも独自のやり方でキャッシュ機構を実装したいと思う
* かもしれません。とりあえず、単に ``return null`` してください。
*/
public function listSources($data = null) {
return null;
}
/**
* describe() はモデルに対して ``Model::save()`` のための
* スキーマを伝えます。
*
* モデルごとに異なったスキーマを使いたい場合もあるかもしれませんが、
* それでも単一のデータソースを使ってください。その場合、モデルに
* 対して ``schema`` プロパティをセットし、ここでは単に
* ``$model->schema`` を返すようにします。
*/
public function describe($model) {
return $this->_schema;
}
/**
* calculate() はレコード数のカウント方法を決定します。これは
* ``update()`` と ``delete()`` の動作を決めるのに必要です。
*
* ここでは実際にはレコードのカウントはせず、 ``read()`` に渡されるべき
* 文字列を返します。これが実際のカウント処理を行います。
* 最も簡単なのは、ここでは単に 'COUNT' という文字列を返しておき、
* ``read()`` の中で ``$data['fields'] === 'COUNT'`` かどうかを
* 聞くようにすることです。
*/
public function calculate(Model $model, $func, $params = array()) {
return 'COUNT';
}
/**
* CRUD のうちの R を実装します。 ``Model::find()`` への呼び出しは
* ここに来ます。
*/
public function read(Model $model, $queryData = array(),
$recursive = null) {
/**
* ここでは、前述の calculate() メソッドで返されるカウントの
* 実際の処理を行います。リモートソースのチェックをするか、
* またはそれ以外の方法でレコードカウントを取得します。
* ここでは単に 1 を返しているので、 ``update()`` や
* ``delete()`` ではレコードが存在するものとみなされます。
*/
if ($queryData['fields'] === 'COUNT') {
return array(array(array('count' => 1)));
}
/**
* ここではリモートデータを取得およびデコードして返します。
*/
$queryData['conditions']['apiKey'] = $this->config['apiKey'];
$json = $this->Http->get(
'http://example.com/api/list.json',
$queryData['conditions']
);
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return array($model->alias => $res);
}
/**
* CRUD のうちの C を実装します。 ``Model::save()`` への呼び出しのうち
* $model->id がセットされないケースがここに来ます。
*/
public function create(Model $model, $fields = null, $values = null) {
$data = array_combine($fields, $values);
$data['apiKey'] = $this->config['apiKey'];
$json = $this->Http->post('http://example.com/api/set.json', $data);
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return true;
}
/**
* CRUD のうちの U を実装します。 ``Model::save()`` への呼び出しのうち
* $model->id がセットされているケースがここに来ます。リモートソース
* に依存するためここでは単に ``$this->create()`` をコールできます。
*/
public function update(Model $model, $fields = null, $values = null,
$conditions = null) {
return $this->create($model, $fields, $values);
}
/**
* CRUD のうちの D を実装します。
* ``Model::delete()`` への呼び出しがここに来ます。
*/
public function delete(Model $model, $id = null) {
$json = $this->Http->get('http://example.com/api/remove.json', array(
'id' => $id[$model->alias . '.id'],
'apiKey' => $this->config['apiKey'],
));
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return true;
}
}
データソースを構成するには app/Config/database.php
ファイルの中で以下のように追加します:
public $faraway = array(
'datasource' => 'FarAwaySource',
'apiKey' => '1234abcd',
);
そしてモデルの中でデータベースを以下のように使います:
class MyModel extends AppModel {
public $useDbConfig = 'faraway';
}
これで、使い慣れたモデルのメソッドを使ってリモートソースから データを取り出せるようになります:
// Get all messages from 'Some Person'
$messages = $this->MyModel->find('all', array(
'conditions' => array('name' => 'Some Person'),
));
Tip
あなたの read
メソッドで返される配列のインデックスが数値以外の場合、
find のタイプで 'all'
以外を使うと予期しない結果が返る場合があります。
同様に、新しいメッセージを保存できます:
$this->MyModel->save(array(
'name' => 'Some Person',
'message' => 'New Message',
));
以前のメッセージを更新します:
$this->MyModel->id = 42;
$this->MyModel->save(array(
'message' => 'Updated message',
));
そしてそのメッセージを削除します:
$this->MyModel->delete(42);
データソースをパッケージにしてプラグインにすることもできます。
単にあなたのデータソースファイルを
Plugin/[YourPlugin]/Model/Datasource/[YourSource].php
に置いて、それをプラグイン記法で参照するだけです:
public $faraway = array(
'datasource' => 'MyPlugin.FarAwaySource',
'apiKey' => 'abcd1234',
);
SQL Server のデータソースは、pdo_sqlsrv と呼ばれるマイクロソフトの PHP エクステンションに依存しています。この PHP エクステンションは PHP の基本構成には含まれておらず、別途インストールする必要があります。
また、そのエクステンションが動作するためには SQL Server の ネイティブクライアントがインストールされている必要があります。 そのネイティブクライアントは Windows 用しかないので、 これを Linux, Mac OS, FreeBSD 上で動かすことはできません。
このため、SQL Server が以下のようなエラー:
Error: Database connection "Sqlserver" is missing, or could not be created.
を出す場合は、まず SQL Server の PHP エクステンション pdo_sqlsrv と SQL Server のネイティブクライアントが正しくインストール されているかどうかを確認して下さい。