Les requêtes d'update étendant les requêtes de sélection, il est conseillé de commencer par la lecture de
SELECT.
Toutes les méthodes des requêtes de sélection sont accessibles dans les requêtes UPDATE.
Cependant, certaines n'auront aucun impact sur le texte SQL généré.
Voici les éléments qui sont conservés ou ignorés :
| UPDATE | Conservés | Ignorés |
| SELECT | | X |
| JOIN | X | |
| WHERE | X | |
| GROUP BY | | X |
| ORDER BY | X | |
Update simple d'un objet
Pour mettre à jour un objet, on commence en général par le récupérer
<?php
$q = City::query(TRUE);
$q->whereAnd('city_name', '=', 'New York');
$db = DBHelper::getInstance(...);
$newYork = \salt\first($db->execQuery($q)->data); // renverra NULL si l'objet n'est pas trouvé
puis on le modifie
<?php
$newYork->city_name = 'NewYork'; // on enlève l'espace
on génère la requête UPDATE
<?php
$updateQuery = new UpdateQuery($newYork); // On doit passer l'objet modifié au constructeur
echo $updateQuery->toSQL(); // affiche :
// UPDATE city t1
// SET t1.city_name = :v2
// WHERE t1.id = :v1
Attention, comme pour l'INSERT, les valeurs de l'objet sont lues dès le constructeur. Une modification sur l'objet ensuite sera
sans effet pour la requête :
<?php
$newYork->city_name = 'NewYork'; // on enleve l'espace
$updateQuery = new UpdateQuery($newYork);
$newYork->city_name = 'NY'; // sera ignoré
echo $updateQuery->toSQL(); // génèrera une requête avec SET city_name = 'NewYork'
et enfin on peux exécuter la requête
<?php
$r = $db->execUpdate($updateQuery); // renvoi le nombre de lignes modifiées
Si le nombre de lignes modifiées par la requête est différent de 1, une exception
RowCountException sera levée,
avec le nombre de lignes réellement modifiées, le nombre de lignes modifiées attendues (ici 1) et le texte de la requête
préparée.
Ajout de clause WHERE
On peut ajouter des clauses WHERE afin de restreindre la requête UPDATE à une condition.
Par défaut, le framework ajoutera automatiquement la clause WHERE permettant de retrouver l'objet passé en paramètre en utilisant
le champ enregistré dans les métadonnées avec
registerId
Si on ajoute une autre clause WHERE, cette clause peut être fausse et la requête ne pas modifier de lignes. Pour éviter de
lever une exception dans ce cas il faut modifier l'appel à
execUpdate de cette manière :
<?php
$newYork->city_name = 'NewYork'; // on enleve l'espace
$updateQuery = new UpdateQuery($newYork);
// ajout d'une condition
$updateQuery->whereAnd('date_mise_a_jour', '<', SqlExpr::value('2016-08-01')->toDate('%Y-%m-%d'));
// on doit passer NULL en 2ème paramètre pour dire qu'on ne sait pas combien de lignes vont être modifiées, et
// donc ne pas lever d'exception si aucune ligne ne l'est.
$r = $db->execUpdate($updateQuery, NULL);
echo $r.' lignes modifiées'; // Le résultat de execUpdate est le nombre de lignes réellement modifiées.
SET complexe
Si on souhaite générer une valeur dynamique qui dépend d'un ou plusieurs champs de l'objet, on peut utiliser les
méthodes
increment, decrement, set
increment et
decrement permettent de générer une requête de la forme :
SET champ = champ + valeur
Par exemple :
<?php
$updateQuery->increment('champ1');
$updateQuery->increment('champ2', 3);
$updateQuery->decrement('champ3', 4);
echo $updateQuery->toSQL(); // affiche
// UPDATE ... t1
// SET champ1 = t1.champ1 + :v0, // v0 = 1, valeur par défaut de increment et decrement
// champ2 = t1.champ2 + :v1, // v1 = 3
// champ3 = t1.champ3 - :v2 // v2 = 4
// WHERE ...
La méthode
set permet de construire une SqlExpr aussi complexe que l'on souhaite
<?php
$updateQuery->set('champ2',
SqlExpr::_COS(
SqlExpr::implode(' * ', $updateQuery->champ1, 2)
)
);
echo $updateQuery->toSQL(); // affiche
// UPDATE ... t1
// SET champ2 = COS(t1.champ1 * :v2) // v2 = 2
// WHERE ...
Update multiple
Il est possible de ne pas restreindre un UPDATE à l'objet passé en paramètre.
Pour cela on utilisera directement la méthode statique
updateQuery() sur le type souhaité.
Afin d'éviter les erreurs, il faut explicitement déclarer un UPDATE comme étant un update portant sur plusieurs objets avec la méthode
allowMultipleChange()
<?php
$updateQuery = City::updateQuery();
$updateQuery->allowMultipleChange();
En appelant cette méthode, la clause WHERE sur l'ID de l'objet ne sera pas générée. C'est donc au développeur de spécifier la clause
WHERE adéquate
Lors d'un update multiple tous les champs explicitement modifiés avec increment / decrement / set seront modifiés dans les objets satisfaisant la clause WHERE.
Exemple :
Certaines villes ont été enregistrés dans la base avec pour pays 'America' au lieu de 'USA', on veux toutes les mettre à jour
<?php
$updateQuery = City::updateQuery();
$updateQuery->allowMultipleChange(); // permet d'avoir plusieurs objets mis à jour
$updateQuery->whereAnd('country', '=', 'America');
$updateQuery->set('country', 'USA');
$updateQuery->set('date_mise_a_jour', SqlExpr::_NOW()->asDate(SqlDateFormat::DATETIME));
$nbChanges = $db->execUpdate($updateQuery); // Génère la requête :
// UPDATE city t1
// SET country = 'USA', date_mise_a_jour = NOW()
// WHERE t1.country = 'America'
echo $nbChanges.' cities have been updated';
Seuls les champs réellement modifiés dans l'objet seront mis à jour lors de la requête UPDATE.
Le framework est également assez intelligent pour détecter les doubles modifications et ne générer les clauses SET que pour les champs modifiés :
<?php
$cityOfUsa = \salt\first($db->execQuery($q)->data);
echo $cityOfUsa->country; // affiche 'America'
var_dump($cityOfUsa->isModified()); // renvoi FALSE
$cityOfUsa->country = 'USA';
var_dump($cityOfUsa->isModified()); // renvoi TRUE
$cityOfUsa->country = 'America';
var_dump($cityOfUsa->isModified()); // renvoi FALSE

Dans le cas où l'on utilise un UPDATE multiple pour modifier un objet sans l'avoir lu préalablement, on n'a aucun moyen de vérifier
que l'objet n'a pas été modifié par un autre utilisateur. Cette fonctionnalité est donc incompatible avec un mécanisme de locking.
Protection contre les UPDATE sans clause WHERE
La méthode
allowMultipleChange() supprimant la clause sur l'ID de l'objet, si on n'ajoute pas de clause WHERE par la suite
on modifiera tout les objets de la base, ce qui est rarement souhaité.
Le framework interdira pas défaut l'exécution d'une requête UPDATE sans clause WHERE en levant une exception... cela vous évitera de perdre
vos données lors du développement.
Si vous souhaitez réellement modifier toute une table, vous pouvez ajouter une clause du genre 1=1 ou encore appeler explicitement la
méthode
allowEmptyWhere() :
<?php
$updateQuery = MonObjet::updateQuery();
$updateQuery->allowMultipleChange(); // autorise l'update a modifier plusieurs objets
$updateQuery->set('champ1', 1); // champ à mettre à jour
$db->execUpdate($updateQuery); // levera une exception en indiquant que la clause WHERE est vide
$updateQuery->allowEmptyWhere(); // autoriser une clause WHERE vide
$db->execUpdate($updateQuery); // exécutera l'UPDATE sans clause WHERE