データソース は、モデルと、モデルが表現しているデータ自体をリンクするものです。通常、データは MySQL, PostgresSQL もしくは MSSQL といったリレーショナルデータベースから取得されます。CakePHP には、あらかじめいくつかの特定のデータベース用のデータソースが用意されています(cake/libs/model/datasources/dbo/ の中にある dbo_* class というファイルです )。以下に列挙します:
dbo_adodb.php
dbo_db2.php
dbo_firebird.php
dbo_mssql.php
dbo_mysql.php
dbo_mysqli.php
dbo_odbc.php
dbo_oracle.php
dbo_postgres.php
dbo_sqlite.php
dbo_sybase.php
app/config/database.php の中で利用するデータベースを特定することで、CakePHP はすべてのモデル操作において、自動的にそのデータベースのデータソースを利用します。 したがって、通常は特にデータソースのことを意識しなくても、問題なくモデルを扱うことができます。
上記のソースはすべて、 DboSource
というほとんどのリレーショナルデータベースに共通するロジックが書かれた基底クラスから派生しています。もしあなたが
RDBMS のデータソースを書きたいときは、これら(例えばdbo_mysql.php や
dbo_mssql.php)を元に作成するのがよいでしょう。
しかし、多くの方は、リモートの REST API や、 LDAP サーバといった外部データのデータソース作成に興味があると思います。それでは、これからそのやり方についてみていきましょう。
データソースは、
次のメソッドのうち少なくとも一つを実装でき、またそうすべきです:
それは create
, read
, update
そして(あるいは)
delete
です(さしあたり、いまの段階ではメソッドの実際の用法と実装の詳細は置いておきます。のちほど説明します)。必要性がなければ、上記のメソッド以上のものを実装しなくてもかまいません
- もし読み込み専用のデータソースが必要な場合は、create
や
update
といったメソッドを実装しなくてもかまいません。
実装しなければならないメソッド
describe($model)
listSources()
次のうち少なくとも一つ:
create($model, $fields = array(), $values = array())
read($model, $queryData = array())
update($model, $fields = array(), $values = array())
delete($model, $id = null)
モデルの中でなく、データソース自身の中で $_schema
クラス属性を定義することも可能となっています(そしてそれは時に非常に有効です)。
以上ですべてです。このようにしてデータソースをモデルと結びつけることで、いつものように
Model::find() や save()
が利用できます。これらのメソッドを呼ぶための適切なデータやパラメータは、データソース自身に投げられます。そこでは、あなたが必要などんな機能(例えば
Model::find オプションの 'conditions'
や、'limit'
、あるいは独自のカスタムパラメータさえ)も定義することができます。
Here is a simple example of how to use Datasources and HttpSocket
to
implement a very basic Twitter source that
allows querying the Twitter API as well as posting new status updates to
a configured account.
This example will only work in PHP 5.2 and above, due to the use of
json_decode
for the parsing of JSON formatted data.
You would place the Twitter datasource in app/models/datasources/twitter_source.php:
<?php
/**
* Twitter DataSource
*
* Used for reading and writing to Twitter, through models.
*
* PHP Version 5.x
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright 2005-2009, Cake Software Foundation, Inc. (https://www.cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2009, Cake Software Foundation, Inc. (https://www.cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://www.opensource.org/licenses/mit-license.php The MIT License
*/
App::import('Core', 'HttpSocket');
class TwitterSource extends DataSource {
protected $_schema = array(
'tweets' => array(
'id' => array(
'type' => 'integer',
'null' => true,
'key' => 'primary',
'length' => 11,
),
'text' => array(
'type' => 'string',
'null' => true,
'key' => 'primary',
'length' => 140
),
'status' => array(
'type' => 'string',
'null' => true,
'key' => 'primary',
'length' => 140
),
)
);
public function __construct($config) {
$auth = "{$config['login']}:{$config['password']}";
$this->connection = new HttpSocket(
"http://{$auth}@twitter.com/"
);
parent::__construct($config);
}
public function listSources() {
return array('tweets');
}
public function read($model, $queryData = array()) {
if (!isset($queryData['conditions']['username'])) {
$queryData['conditions']['username'] = $this->config['login'];
}
$url = "/statuses/user_timeline/";
$url .= "{$queryData['conditions']['username']}.json";
$response = json_decode($this->connection->get($url), true);
$results = array();
foreach ($response as $record) {
$record = array('Tweet' => $record);
$record['User'] = $record['Tweet']['user'];
unset($record['Tweet']['user']);
$results[] = $record;
}
return $results;
}
public function create($model, $fields = array(), $values = array()) {
$data = array_combine($fields, $values);
$result = $this->connection->post('/statuses/update.json', $data);
$result = json_decode($result, true);
if (isset($result['id']) && is_numeric($result['id'])) {
$model->setInsertId($result['id']);
return true;
}
return false;
}
public function describe($model) {
return $this->_schema['tweets'];
}
}
?>
Your model implementation could be as simple as:
<?php
class Tweet extends AppModel {
public $useDbConfig = 'twitter';
}
?>
If we had not defined our schema in the datasource itself, you would get an error message to that effect here.
And the configuration settings in your app/config/database.php
would
resemble something like this:
<?php
var $twitter = array(
'datasource' => 'twitter',
'login' => 'username',
'password' => 'password',
);
?>
Using the familiar model methods from a controller:
<?php
// Will use the username defined in the $twitter as shown above:
$tweets = $this->Tweet->find('all');
// Finds tweets by another username
$conditions= array('username' => 'caketest');
$otherTweets = $this->Tweet->find('all', compact('conditions'));
?>
Similarly, saving a new status update:
<?php
$this->Tweet->save(array('status' => 'This is an update'));
?>
You can also package Datasources into plugins.
Simply place your datasource file into
plugins/[your_plugin]/models/datasources/[your_datasource]_source.php
and refer to it using the plugin notation:
var $twitter = array(
'datasource' => 'Twitter.Twitter',
'username' => '[email protected]',
'password' => 'hi_mom',
);
In addition, you can also add to the current selection of CakePHP's dbo drivers in plugin form.
Simply add your drivers to
plugins/[your_plugin]/models/datasources/dbo/[your_driver].php
and
again use plugin notation:
var $twitter = array(
'driver' => 'Twitter.Twitter',
...
);
Finally, you're also able to bundle together your own DataSource and respective drivers so that they can share functionality. First create your main class you plan to extend:
plugins/[social_network]/models/datasources/[social_network]_source.php :
<?php
class SocialNetworkSource extends DataSource {
// general functionality here
}
?>
And now create your drivers in a sub folder:
plugins/[social_network]/models/datasources/[social_network]/[twitter].php
<?php
class Twitter extends SocialNetworkSource {
// Unique functionality here
}
?>
And finally setup your database.php
settings accordingly:
var $twitter = array(
'driver' => 'SocialNetwork.Twitter',
'datasource' => 'SocialNetwork.SocialNetwork',
);
var $facebook = array(
'driver' => 'SocialNetwork.Facebook',
'datasource' => 'SocialNetwork.SocialNetwork',
);
Just like that, all your files are included Automagically! No need
to place App::import()
at the top of all your files.