Les Sources de données (DataSources) sont les liens entre les models 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 Microsoft SQL Server. CakePHP est distribué avec de nombreuses sources de
données spécifiques d’une base de données (voir les fichiers de classe
dans lib/Cake/Model/Datasource/Database
), un résumé de ceux-ci est listé
ici pour votre confort :
Mysql
Postgres
Sqlite
Sqlserver
Note
Vous pouvez trouver des sources de données contributives de la communauté supplémentaires dans le Dépôt de Sources de Données CakePHP sur GitHub.
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 model. 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 MySQL ou
SQLite) 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
, update
et delete
.
Méthodes qui doivent être implémentées pour toutes les méthodes CRUD:
describe($model)
listSources($data = null)
calculate($model, $func, $params)
Au moins une des suivantes:
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)
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 model.
Et c’est à peu près tout ce qu’il y a dire ici. En couplant cette
source de données à un model, vous êtes alors en mesure d’utiliser
Model::find()/save()/delete()
, 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 le parsing
'conditions'
, 'limit'
ou même vos paramètres personnalisés).
Une des raisons pour laquelle vous voudriez écrire votre propre source de
données pourrait être la volonté d’accéder à l’API d’une librairie tierce en
utilisant les méthodes habituelles Model::find()/save()/delete()
. Ecrivons
une source de données qui va accéder à une API JSON distante et fictive. Nous
allons l’appeler FarAwaySource
et nous allons la placer dans
app/Model/Datasource/FarAwaySource.php
:
App::uses('HttpSocket', 'Network/Http');
class FarAwaySource extends DataSource {
/**
* Une description optionnelle de votre source de données
*/
public $description = 'A far away datasource';
/**
* Nos options de config par défaut. Ces options seront personnalisées dans notre
* ``app/Config/database.php`` et seront fusionnées dans le ``__construct()``.
*/
public $config = array(
'apiKey' => '',
);
/**
* Si nous voulons create() ou update(), nous avons besoin de spécifier la
* disponibilité des champs. Nous utilisons le même tableau indicé comme nous le faisions avec CakeSchema, par exemple
* fixtures et schema de migrations.
*/
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,
),
);
/**
* Créons notre HttpSocket et gérons any config tweaks.
*/
public function __construct($config) {
parent::__construct($config);
$this->Http = new HttpSocket();
}
/**
* Puisque les sources de données se connectent normalement à une base de données
* il y a quelques modifications à faire pour les faire marcher sans base de données.
*/
/**
* listSources() est pour la mise en cache. Vous voulez implémenter la mise en cache
* de votre façon avec une source de données personnalisée. Donc juste ``return null``.
*/
public function listSources() {
return null;
}
/**
* describe() dit au model votre schema pour ``Model::save()``.
*
* Vous voulez peut-être un schema différent pour chaque model mais utiliser
* toujours une unique source de données. Si c'est votre cas, alors
* définissez une propriété ``schema`` dans vos models et retournez
* simplement ``$Model->schema`` ici à la place.
*/
public function describe(Model $Model) {
return $this->_schema;
}
/**
* calculate() est pour déterminer la façon dont nous allons compter
* les enregistrements et est requis pour faire fonctionner ``update()``
* et ``delete()``.
*
* Nous ne comptons pas les enregistrements ici mais retournons une chaîne
* à passer à
* ``read()`` qui va effectivement faire le comptage. La façon la plus
* facile est de juste retourner la chaîne 'COUNT' et de la vérifier
* dans ``read()`` où ``$data['fields'] === 'COUNT'``.
*/
public function calculate(Model $model, $func, $params = array()) {
return 'COUNT';
}
/**
* Implémente le R dans CRUD. Appel à ``Model::find()`` se trouve ici.
*/
public function read(Model $model, $queryData = array(), $recursive = null) {
/**
* Ici nous faisons réellement le comptage comme demandé par notre
* méthode calculate() ci-dessus. Nous pouvons soit vérifier la
* source du dépôt, soit une autre façon pour récupérer le compte
* de l\'enregistrement. Ici nous allons simplement retourner 1
* ainsi ``update()`` et ``delete()`` vont estimer que l\'enregistrement
* existe.
*/
if ($queryData['fields'] === 'COUNT') {
return array(array(array('count' => 1)));
}
/**
* Maintenant nous récupérons, décodons et retournons les données du dépôt.
*/
$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);
}
/**
* Implémente le C dans CRUD. Appel à ``Model::save()`` sans $model->id
* défini se trouve ici.
*/
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;
}
/**
* Implémente le U dans CRUD. Appel à ``Model::save()`` avec $Model->id
* défini se trouve ici. Selon la source du dépôt, vous pouvez juste appeler
* ``$this->create()``.
*/
public function update(Model $model, $fields = null, $values = null, $conditions = null) {
return $this->create($model, $fields, $values);
}
/**
* Implémente le D de CRUD. Appel à ``Model::delete()`` se trouve ici.
*/
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;
}
}
Nous pouvons à présent configurer la source de données dans notre fichier
app/Config/database.php
en y ajoutant quelque chose comme ceci:
public $faraway = array(
'datasource' => 'FarAwaySource',
'apiKey' => '1234abcd',
);
Et ensuite utiliser la configuration de notre source de données dans nos models comme ceci:
class MyModel extends AppModel {
public $useDbConfig = 'faraway';
}
Nous pouvons à présent récupérer les données depuis notre source distante en utilisant les méthodes familières dans notre model:
// Récupère tous les messages de 'Some Person'
$messages = $this->MyModel->find('all', array(
'conditions' => array('name' => 'Some Person'),
));
Astuce
L’utilisation d’autres types de find que 'all'
peut avoir des résultats
inattendus si le résultat de votre méthode read
n’est pas un tableau
indexé numériquement.
De la même façon, nous pouvons sauvegarder un nouveau message:
$this->MyModel->save(array(
'name' => 'Some Person',
'message' => 'New Message',
));
Mettre à jour le précédent message:
$this->MyModel->id = 42;
$this->MyModel->save(array(
'message' => 'Updated message',
));
Et supprimer le message:
$this->MyModel->delete(42);
Vous pouvez également empaqueter vos sources de données dans des plugins.
Placez simplement votre fichier de source de données à l’intérieur de
Plugin/[YourPlugin]/Model/Datasource/[YourSource].php
et faites
y référence en utilisant la syntaxe pour les plugins:
public $faraway = array(
'datasource' => 'MyPlugin.FarAwaySource',
'apiKey' => 'abcd1234',
);
La source de données Sqlserver dépend de l’extension PHP de Microsoft appelée pdo_sqlsrv. Cette extension PHP n’est pas inclue dans l’installation de base de PHP et doit être installée séparément. Le SQL Server Native Client doit également être installé pour que l’extension fonctionne.
Donc si les erreurs de la source de données Sqlserver sortent:
Error: Database connection "Sqlserver" is missing, or could not be created.
Vérifiez d’abord l’extension PHP du Serveur SQL pdo_sqlsrv et le Client Native du Serveur SQL.