Les Sources de données (DataSources) sont les liens entre les modèles et la source de données qu’ils représentent. Dans de nombreux cas, les données sont récupérées depuis une base de données relationnelle telle MySQL, PostgreSQL ou MSSQL. CakePHP est distribué avec de nombreuses sources de données spécifiques d’une base de données (voir les fichiers de classe dbo_* dans cake/libs/model/datasources/dbo/), un résumé de ceux-ci est listé ici pour votre confort :
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
Quand vous spécifiez une configuration de connexion à une base de données dans app/config/database.php, CakePHP utilise de manière transparente la source de données correspondant à la base de données pour toutes les opérations de modèle. Donc, même si vous pensiez ne rien connaître aux sources de données, vous les utilisez tout le temps.
Toutes les sources ci-dessus dérivent d’une classe de base
DboSource
, qui agrège de la logique commune à la plupart des bases
de données relationnelles. Si vous décidez d’écrire une source de donnée
RDBMS, travailler à partir de l’une d’entre elles (par ex dbo_mysql.php
ou dbo_mssql.php) est plus sûr.
La plupart des gens cependant, sont intéressés par l’écriture de sources de données pour des sources externes, telles les APIs REST distantes ou même un serveur LDAP. C’est donc ce que nous allons voir maintenant.
Une source de données peut et devrait implémenter au moins l’une des
méthodes suivantes : create
, read
, update
et/ou delete
(les signatures exactes de méthode et les détails d’implémentation ne
sont pas importants pour le moment, ils seront décrits plus tard). Vous
n’êtes pas obligé d’implémenter plus que nécessaire, parmi les méthodes
listées ci-dessus - si vous avez besoin d’une source de données en
lecture seule, il n’y a aucune raison d’implémenter create
et
update
.
Méthodes qui doivent être implémentées :
describe($model)
listSources()
et au moins l’une de celles-ci :
create($model, $fields = array(), $values = array())
read($model, $queryData = array())
update($model, $fields = array(), $values = array())
delete($model, $id = null)
Il est possible également (et souvent très pratique), de définir
l’attribut de classe $_schema
au sein de la source de données
elle-même, plutôt que dans le modèle.
Et c’est à peu près tout ce qu’il y a dire ici. En couplant cette source
de données à un modèle, vous êtes alors en mesure d’utiliser
Model::find()/save()
, comme vous le feriez normalement ; les données
et/ou paramètres appropriés, utilisés pour appeler ces méthodes, seront
passés à la source de données elle-même, dans laquelle vous pouvez
décider d’implémenter toutes les fonctionnalités dont vous avez besoin
(par exemple les options de Model::find comme 'conditions'
,
'limit'
ou même vos paramètres personnalisés).
Voici un exemple simple sur la manière d’utiliser les Datasources et
HttpSocket
, pour implémenter une source
Twitter vraiment basique, qui permet de requêter
l’API Twitter et de poster les nouvelles mises à jour du statut vers un
compte configuré.
Cet exemple ne fonctionnera qu’avec PHP 5.2 et supérieur, à cause de
l’usage de json_decode
pour l’analyse des données au format JSON.
Vous pourriez placez la source de données Twitter dans app/models/datasources/twitter_source.php :
<?php
* DataSource Twitter
*
* Utilisée pour lire et écrire sur Twitter, à travers les modèles.
*
* 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'];
}
}
?>
Votre implémentation de modèle pourrait être aussi simple que :
<?php
class Tweet extends AppModel {
public $useDbConfig = 'twitter';
}
?>
Si nous n’avions pas défini notre schéma dans la source de données elle-même, vous obtiendriez ici un message d’erreur.
Et les paramètres de configuration dans votre
app/config/database.php
devraient ressembler à quelque chose comme
çà :
<?php
var $twitter = array(
'datasource' => 'twitter',
'login' => 'username',
'password' => 'password',
);
?>
Utilisation des méthodes de modèle familières depuis un contrôleur :
<?php
// Utilisera le nom d'utilisateur défini dans $twitter, comme montré ci-dessus :
$tweets = $this->Tweet->find('all');
// Trouve les tweets par un autre nom d'utilisateur
$conditions= array('username' => 'caketest');
$autresTweets = $this->Tweet->find('all', compact('conditions'));
?>
De la même façon, une nouvelle mise à jour du statut :
<?php
$this->Tweet->save(array('status' => 'Ceci est une mise à jour'));
?>
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.