DAO

La classe DAO est en charge des opérations de chargement et de persistance des modèles :

Connexion à la base de données

Vérifiez que les paramètres de connexion à la base de données sont correctement renseignés dans le fichier de configuration :

Ubiquity config -f=database

Depuis la version 2.3.0

Le démarrage de la base de données avec DAO::startDatabase($config) dans le fichier services.php est inutile, nul besoin de démarrer la base de données, la connexion est faite automatiquement à la première requête. Utilisez DAO::start() dans le fichier app/config/services.php lorsque vous utilisez plusieurs bases de données (avec la fonctionnalité multi db).

Chargement de données

Chargement d’une instance

Chargement d’une instance de la classe modelsUser avec l’identifiant 5.

use Ubiquity\orm\DAO;
use models\User;

$user=DAO::getById(User::class, 5);

Chargement d’une instance en utilisant une condition :

use Ubiquity\orm\DAO;
use models\User;

DAO::getOne(User::class, 'name= ?',false,['DOE']);

Chargement de BelongsTo

Par défaut, les membres définis par une relation belongsTo sont automatiquement chargés.

Chaque utilisateur n’appartient qu’à une seule catégorie :

$user=DAO::getById(User::class,5);
echo $user->getCategory()->getName();

Il est possible d’empêcher ce chargement par défaut ; le troisième paramètre permet de charger ou non les membres belongsTo :

$user=DAO::getOne(User::class,5, false);
echo $user->getCategory();// NULL

Chargement de HasMany

Le chargement des membres hasMany doit toujours être explicite ; le troisième paramètre permet le chargement explicite des membres.

Chaque utilisateur fait partie de plusieurs groupes :

$user=DAO::getOne(User::class,5,['groupes']);
foreach($user->getGroupes() as $groupe){
    echo $groupe->getName().'<br>';
}

Clé primaire composite

Soit le modèle ProductDetail correspondant à un produit commandé sur une commande et dont la clé primaire est composite :

app/models/ProductDetail.php
 1 namespace models;
 2
 3use Ubiquity\attributes\items\Id;
 4
 5 class ProductDetail{
 6
 7   #[Id]
 8   private $idProduct;
 9
10   #[Id]
11   private $idCommand;
12
13   ...
14 }

Le deuxième paramètre $keyValues peut être un tableau si la clé primaire est composite :

$productDetail=DAO::getOne(ProductDetail::class,[18,'BF327']);
echo 'Command:'.$productDetail->getCommande().'<br>';
echo 'Product:'.$productDetail->getProduct().'<br>';

Chargement de plusieurs objets

Chargement d’instances de la classe User :

$users=DAO::getAll(User::class);
foreach($users as $user){
    echo $user->getName()."<br>";
}

Requêtage utilisant des conditions

Requêtes simples

Le paramètre condition est équivalent à la partie WHERE d’une instruction SQL :

$users=DAO::getAll(User::class,'firstName like "bren%" and not suspended',false);

Pour éviter les injections SQL et bénéficier de la préparation des statements, il est préférable d’effectuer une requête paramétrée :

$users=DAO::getAll(User::class,'firstName like ? and suspended= ?',false,['bren%',false]);

UQueries

L’utilisation de U-queries permet de poser des conditions sur les membres associés :

Sélection des utilisateurs dont l’organisation possède le domaine lecnam.net :

$users=DAO::uGetAll(User::class,'organization.domain= ?',false,['lecnam.net']);

Il est possible de visualiser la requête générée dans les logs (si le logging est activé) :

../_images/uquery-users-log.png

Le résultat peut être vérifié en sélectionnant tous les utilisateurs de cette organisation :

$organization=DAO::getOne(Organization::class,'domain= ?',['users'],['lecnam.net']);
$users=$organization->getUsers();

Les logs correspondants :

../_images/uquery-users-orga-log.png

Comptage

Test de l’existance

if(DAO::exists(User::class,'lastname like ?',['SMITH'])){
    //there's a Mr SMITH
}

Comptage

Pour compter les instances, ce qu’il ne faut pas faire, si les utilisateurs ne sont pas déjà chargés :

$users=DAO::getAll(User::class);
echo "there are ". \count($users) ." users";

Ce qui doit être fait :

$count=DAO::count(User::class);
echo "there are $count users";

Avec une condition :

$notSuspendedCount=DAO::count(User::class, 'suspended = ?', [false]);

Avec une condition sur les objets associés :

Nombre d’utilisateurs appartenant à l’organisation nommée OTAN.

$count=DAO::uCount(User::class,'organization.name= ?',['OTAN']);

Modification de données

Ajout d’une instance

Ajout d’une organisation :

$orga=new Organization();
$orga->setName('Foo');
$orga->setDomain('foo.net');
if(DAO::save($orga)){
  echo $orga.' added in database';
}

Ajout d’une instance d’utilisateur, dans une organisation :

$orga=DAO::getById(Organization::class, 1);
$user=new User();
$user->setFirstname('DOE');
$user->setLastname('John');
$user->setEmail('doe@bar.net');
$user->setOrganization($orga);
if(DAO::save($user)){
  echo $user.' added in database in '.$orga;
}

Mise à jour d’une instance

Dans un premier temps, l’instance doit être chargée :

$orga=DAO::getOne(Organization::class,'domain= ?',false,['foo.net']);
$orga->setAliases('foo.org');
if(DAO::save($orga)){
  echo $orga.' updated in database';
}

Suppression d’une instance

Si l’instance est déjà chargée depuis la base de données :

$orga=DAO::getById(Organization::class,5,false);
if(DAO::remove($orga)){
  echo $orga.' deleted from database';
}

Si l’instance n’est pas chargée, il est plus approprié d’utiliser la méthode delete :

if(DAO::delete(Organization::class,5)){
  echo 'Organization deleted from database';
}

Suppression de plusieurs instances

Suppression de plusieurs instances sans chargement préalable :

if($res=DAO::deleteAll(models\User::class, 'id in (?,?,?)',[1,2,3])){
    echo "$res elements deleted";
}

Requêtes en masse (bulk)

Les requêtes en masse permettent d’effectuer plusieurs opérations (insertion, modification ou suppression) en une seule requête, ce qui contribue à améliorer les performances.

Insertions en masse

Exemple d’insertion :

$u = new User();
$u->setName('Martin1');
DAO::toInsert($u);
$u = new User();
$u->setName('Martin2');
DAO::toInsert($u);
//Perform inserts
DAO::flushInserts();

Mises à jour en masse

Exemple de mise à jour :

$users = DAO::getAll(User::class, 'name like ?', false, [
   'Martin%'
]);
foreach ($users as $user) {
   $user->setName(\strtoupper($user->getName()));
   DAO::toUpdate($user);
}
DAO::flushUpdates();

Suppressions en masse

Exemple de suppression :

$users = DAO::getAll(User::class, 'name like ?', false, [
     'BULK%'
]);
DAO::toDeletes($users);
DAO::flushDeletes();

La méthode DAO::flush() peut être appelée si des insertions, des mises à jour ou des suppressions sont en attente.

Transactions

Transactions explicites

Toutes les opérations DAO peuvent être insérées dans une transaction, ce qui permet d’atomiser une série de changements :

try{
   DAO::beginTransaction();
   $orga=new Organization();
   $orga->setName('Foo');
   DAO::save($orga);

   $user=new User();
   $user->setFirstname('DOE');
   $user->setOrganization($orga);
   DAO::save($user);
   DAO::commit();
}catch (\Exception $e){
   DAO::rollBack();
}

En cas de bases de données multiples définies dans la configuration, les méthodes liées aux transactions peuvent prendre l’offset de base de données défini en paramètre.

DAO::beginTransaction('db-messagerie');
//some DAO operations on messagerie models
DAO::commit('db-messagerie');

Transactions implicites

Certaines méthodes DAO utilisent implicitement les transactions pour regrouper les opérations d’insertion, de mise à jour ou de suppression.

$users=DAO::getAll(User::class);
foreach ($users as $user){
    $user->setSuspended(true);
    DAO::toUpdate($user);
}
DAO::updateGroups();//Perform updates in a transaction

Classe SDAO

La classe SDAO accélère les opérations CRUD pour les classes métier sans relations.

Les modèles doivent dans ce cas déclarer uniquement des membres publics, et ne pas respecter l’encapsulation habituelle.

app/models/Product.php
 1 namespace models;
 2 class Product{
 3   /**
 4    * @id
 5    */
 6   public $id;
 7
 8   public $name;
 9
10   ...
11 }

La classe SDAO hérite de DAO et possède les mêmes méthodes pour effectuer des opérations CRUD.

use Ubiquity\orm\DAO;

$product=DAO::getById(Product::class, 5);

Requêtes DAO préparées

La préparation de certaines requêtes peut améliorer les performances avec les serveurs Swoole, Workerman ou Roadrunner.
Cette préparation initialise les objets qui seront ensuite utilisés pour exécuter la requête.
Cette initialisation est effectuée au démarrage du serveur, ou au démarrage de chaque worker, si un tel événement existe.

Exemple Swoole

Préparation

app/config/swooleServices.php
$swooleServer->on('workerStart', function ($srv) use (&$config) {
   \Ubiquity\orm\DAO::startDatabase($config);
   \Ubiquity\orm\DAO::prepareGetById('user', User::class);
   \Ubiquity\orm\DAO::prepareGetAll('productsByName', Product::class,'name like ?');
});

Utilisation

app/controllers/UsersController.php
public function displayUser($idUser){
   $user=DAO::executePrepared('user',[1]);
   echo $user->getName();
}

public function displayProducts($name){
   $products=DAO::executePrepared('productsByName',[$name]);
   ...
}