DataSources são o link entre modelos e a fonte de dados que os modelos representam. Em muitos casos, os dados são obtidos de um banco de dados relacional como MySQL, PostgreSQL ou MSSQL. CakePHP é distribuido com diversos datasources específcios de banco de dados (veja o arquivo da classe dbo_* em cake/libs/model/datasources/dbo/), um resumo de cada está listado aqui por conveniência:
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
Ao especificar uma configuração de conexão com o banco de dados, em app/config/database.php, CakePHP usa o datasource de banco de dados correspondente para uma operação de modelo. Então, mesmo que você não soubia sobre datasource, você tem os usado o tempo todo.
Todas as fontes acima derivam de uma base, a classe DboSource
, a
qual agrega alguma lógica que é comum para a maiorida dos bancos de
dados relacionais. Se você decidir escrever uma data source RDBMS,
trabalhando com um destes (e.g. dbo_mysql.php ou dbo_mssql.php) é sua
melhor aposta.
Muitas pessoas, contudo, estão interessadas em escrever datasources para fontes externas de dados, como uma REST API remota ou até mesmo um servidor LDAP. Então é isso que veremos agora.
Um datasource pode e deve implementar ao menos os seguintes métodos:
create
, read
, update
e/ou delete
(as assinaturas destes
métodos e seus detalhes de implementaçāo nāo sāo importantes neste
momento, e iremos descrevê-los mais adiante). Nāo é necessário
implementar métodos além destes - aliás, se você precisar de um
datasource somente-leitura, nāo precisa nem implementar os métodos
create
e update
.
Métodos que devem ser implementados:
describe($model)
listSources()
Ao menos um destes:
create($model, $fields = array(), $values = array())
read($model, $queryData = array())
update($model, $fields = array(), $values = array())
delete($model, $id = null)
Também é possível (e algumas vezes, útil) definir a variável de classe
$_schema
dentro do próprio datasource, ao invés de no model.
E essencialmente é isso. Acoplando este datasource a um model, você
passa a poder usar Model::find()/save()
como faria normalmente, e os
dados e/ou parâmetros apropriados usados para chamar estes métodos serāo
passados para o datasource em si, onde você pode decidir implementar
quaisquer recursos que você precise (p.ex, processamento de opções como
'conditions'
ou 'limit'
para o Model::find, ou mesmo seus
próprios parâmetros personalizados).
Aqui está um exemplo simples de como usar DataSources e o HttpSocket
para implementar um datasource muito básico para o
Twitter, que lhe permita consultar a Twitter API
bem como submeter novas atualizações de status para uma conta
configurada.
Este exemplo só irá funcionar nas versões de PHP 5.2 e superior, em
virtude do uso de json_decode
para processamento dos dados no
formato JSON.
Você deve salvar o datasource do Twitter em app/models/datasources/twitter_source.php:
<?php
/**
* Twitter DataSource
*
* Usado para ler e escrever no Twitter, atravé dos models.
*
* PHP Version 5.x
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright 2005-2009, Cake Software Foundation, Inc. (https://www.cakefoundation.org)
*
* Disponibilizado sob licença MIT.
* Redistribuições dos arquivos devem manter a nota de copyright acima.
*
* @filesource
* @copyright Copyright 2009, Cake Software Foundation, Inc. (https://www.cakefoundation.org)
* @link https://cakephp.org Projeto CakePHP(tm)
* @license https://www.opensource.org/licenses/mit-license.php A licença MIT
*/
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'];
}
}
?>
A implementaçāo de seu model pode ser tāo simples quanto isto:
<?php
class Tweet extends AppModel {
public $useDbConfig = 'twitter';
}
?>
Se o esquema nāo tiver sido definido no próprio datasource, você vai receber uma mensagem de erro neste ponto.
E as opções de configuraçāo em seu arquivo app/config/database.php
devem ser algo parecido com:
<?php
var $twitter = array(
'datasource' => 'twitter',
'login' => 'username',
'password' => 'password',
);
?>
Usando os conhecidos métodos de model a partir do controller:
<?php
// Isto irá usar o usuário definido na configuraçāo $twitter como mostrado acima:
$tweets = $this->Tweet->find('all');
// Encontra os tweets de outro usuário
$conditions= array('username' => 'caketest');
$otherTweets = $this->Tweet->find('all', compact('conditions'));
?>
De forma semelhante, pode-se salvar um novo registro de status:
<?php
$this->Tweet->save(array('status' => 'Esta é uma atualzaçāo'));
?>
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.