DataSources son el enlace entre los modelos y la fuente de datos que cada modelo representa. En muchos caos, los datos son recuperados de una base de datos relacional, como MySQL, PostgreSQL o MSSQL. CakePHP se distribuye con varias datasources específicas para varias bases de datos (consulta los archivos de clases dbo_* class files en cake/libs/model/datasources/dbo/), aquí se lista un resumen de los mismos para tu comodidad:
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
Cuando se especifica una configuración de conexión a base de datos en app/config/database.php, CakePHP usa de forma transparente la datasource correspondiente a la base de datos para todas las operaciones con modelos. Por eso, aunque creas que no sabes nada de datasources, ya las has estado usando desde siempre.
Todas las fuentes de datos indicadas arriba derivan de una clase base
DboSource
la cual añade alguna lógica común a la mayoría de bases de
datos relaciones. Si decides crear una datasource RDBMS, tu mejor
apuesta es trabajar a paritr de una de ellas (por ejemeplo:
dbo_mysql.php o dbo_mssql.php) La mayor parte de la gente, sin
embargo, está interesada en escribir datasources para fuentes de datos
externas, como APIs REST remotas o incluso servidores LDAP. Así que eso
es lo que vamos a examinar en adelante.
Una datasource puede, y debería implementar al menos uno de los
siguientes métodos: create
, read
, update
y/o delete
(la
signatura y detalles de implementación no son importantes en este
momento, y serán descritos más tarde). No necesitas implementar más
métodos de los necesarios de los descritos arriba - si estás escribiendo
una datasource de sólo-lectura, no hay razón para implementar create
y update
.
Métodos que deben ser implementados
describe($model)
listSources()
Al menos uno de:
create($model, $fields = array(), $values = array())
read($model, $queryData = array())
update($model, $fields = array(), $values = array())
delete($model, $id = null)
Es posible también (y muchas veces bastante útil) definir el atributo de
clase $_schema
dentro la datasource misma, en lugar de en el modelo.
Y esto es casi todo lo que hay. Emparejando una datasource a un modelo
podrás utilizar Model::find()/save/()
como harías normalmentem y los
datos y/o parámetros adecuados serán usados para llamar a esos métodos
serán pasados a la propia datasource, donde puedes decidir implementar
cualquier prestación que necesites (por ejemplo: opciones para
Model::find como procesasr 'conditions'
, 'limit'
o incluso tus
propios parámetros a medida).
Lo que sigue es un ejemplo simple de como usar dataSources y
HttpSocket
para implementar una fuente muy básica de
Twitter que nos permita utilizar la API de
twitter y enviar nuestras actualizaciones.
Este ejemplo sólo funcionará sobre PHP 5.2 o superior, debido al uso
de json_decode
para procesar los datos en formato JSON.
Tendrás que colocar la datasource para Twitter en tu app/models/datasources/twitter_source.php:
<?php
/**
* Twitter DataSource
*
* Utilizada para leer y escribir en Twitter usando modelos.
*
* 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'];
}
}
?>
La implementación de tu modelo puede ser tan simple como:
<?php
class Tweet extends AppModel {
public $useDbConfig = 'twitter';
}
?>
Si no hemos definido nuestro esquema en la propia datasource, nos dará un mensaje de error al efecto aquí.
Y los ajustes de configuración en tu app/config/database.php
se
parecerán a estos:
<?php
var $twitter = array(
'datasource' => 'twitter',
'login' => 'username',
'password' => 'password',
);
?>
Usando los familiares métodos de modelo desde un controlador:
<?php
// Usará el nombre de usuario definido en $twitter como se mostró arriba:
$tweets = $this->Tweet->find('all');
// Encontrar tweets de otros usuario
$conditions= array('username' => 'caketest');
$otherTweets = $this->Tweet->find('all', compact('conditions'));
?>
De forma similar, guardando una actualización del estatus:
<?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.