Modèles
Les modèles sont la base de chaque application, car chaque application contient des données. PHPDevShell tente de rendre la gestion des données dans la structure MVC aussi simple que possible. Nous voyons toutes les données comme des objets, et ces objets peuvent être n'importe quelle forme de données. S'ils contiennent des informations, ils peuvent être interrogés et les données seront renvoyées. Les requêtes peuvent inclure des requêtes de base de données réelles, des fonctions ou des données définies manuellement.
Notez que lorsque vous faites référence à un dossier ou à un fichier, cela signifie qu'il s'agit d'un dossier ou d'un fichier à l'intérieur de votre propre dossier de plugiciel.
Appeler une requête de données
Peu importe que vous soyez dans une classe d'assistance, un contrôleur ou même un modèle lui-même, vous pouvez toujours exécuter une requête en utilisant :
- $this->db->invokeQuery('nameOfDataClass');
La requête que vous appelez se trouve dans un fichier portant le même nom que votre contrôleur. Par exemple, si vous avez un contrôleur appelé :
my-controller.php |
le modèle avec toutes les requêtes sera appelé :
my-controller.query.php |
dans le dossier models.
Un fichier modèle peut également être réutilisé dans d'autres controllers/models/classes en incluant simplement le nom de fichier à partir de n'importe quel autre endroit où il est nécessaire.
Chaque requête de données est une classe à part entière, ce qui signifie que pour chaque requête de données, vous créerez une classe comme celle-ci :
Maintenant, chaque fois que nous appelons :
- $array=$this->db->invokeQuery('nameOfDataClass');
nous aurons les données que 'nameOfDataClass' renvoie.
L'utilisation de la fonction publique facultative invoke() à partir de la classe de requête indique toujours à la classe que vous souhaitez renvoyer des données personnalisées. Vous pouvez soit manipuler les données d'une requête de base de données, soit renvoyer des données n'ayant rien à voir avec la base de données.
La couche de base de données
Le système de requête ne s'arrête pas là. Il vous permet d'interroger la base de données avec des fonctions de base de données puissantes et faciles à utiliser avec très peu d'effort.
Faisons donc notre première requête de lecture. Obtenons simplement les résultats de la base de données. Un fichier de requête peut avoir plusieurs objets de classe, dans notre fichier d'exemple my-controller.query.php nous écrivons :
- class exampleQuery extends PHPDS_query {
- protected $sql = "SELECT id, example_name, example_note FROM _db_ExamplePlugin_example";
- }
Maintenant, si nous appelons ces données depuis notre contrôleur avec quelque chose comme ceci :
- $array = $this->db->invokeQuery('exampleQuery');
Cela renverra un tableau prêt à être utilisé ou bouclé.
Remarque : Lisez le commutateur de requête de données $keyField ci-dessous pour voir comment les résultats sont renvoyés par la requête.
Transmission de données à la requête
Évidemment, la vie n'est pas si simple et nous devrons généralement modifier les conditions SQL dans notre requête. Comment gérerions-nous cela ?
Le modèle de requête gère tout ce que vous lui envoyez. Lui transmettre autant de variables que possible est en fait très simple.
Regardons l'exemple :
- class exampleQuery extends PHPDS_query {
- protected $sql = "SELECT id, example_name, example_note FROM _db_ExamplePlugin_example WHERE example_name = '%s' AND example_note = '%s'";
- }
Comme on peut le voir, dans cet exemple, nous devons transmettre deux valeurs à la requête de base de données pour la clause WHERE. Pour ce faire, c'est simple :
- $array = $this->db->invokeQuery('exampleQuery', 'Sylvain', 'Hello');
Maintenant, 'Sylvain' et 'Hello' seront inclus dans la condition WHERE et seuls les résultats requis seront renvoyés. Les variables sont utilisées dans l'ordre passé. Ainsi, Sylvain est la première variable passée, elle sera donc utilisée avec la première occurrence d'un spécificateur de type de données, dans ce cas le %s dans example_name.
Remarque : Le %s signifie chaîne et est un spécificateur de type de données spécifiant le type de données transmises dans la requête (%s pour chaîne de caractères, %u pour entier non signé,...). Ce sont les mêmes que ceux que la fonction sprintf de PHP utiliserait.
Ce cas d'utilisation est simple mais présente deux problèmes : vous devez connaître l'ordre des paramètres, et si un paramètre est utilisé deux fois, vous devez le transmettre deux fois. Pour éviter cela, utilisez simplement des paramètres nommés :
- class exampleQuery extends PHPDS_query {
- protected $sql = "SELECT id, example_name, example_note FROM _db_ExamplePlugin_example WHERE example_name = '%(name)s' AND example_note = '%(note)s'";
- }
Pour utiliser cette requête, vous devez utiliser un tableau associatif :
- $array = $this->db->invokeQueryWith('exampleQuery',
- array('name' => 'Sylvain', 'note' => 'Hello')
- );
Remarque : vous pouvez demander à la base de données d'appeler une requête de deux manières, soit en ajoutant les paramètres directement dans l'appel de fonction, soit AVEC un tableau. Notez la différence :
- $array = $this->db->invokeQuery('exampleQuery', 'Sylvain', 'Hello')
- );
- $array = $this->db->invokeQueryWith('exampleQuery',
- array('name' => 'Sylvain', 'note' => 'Hello')
- );
Commutateurs de requête de données
L'objet de requête vous permet de demander la manipulation de données via des commutateurs simples. Il existe quelques commutateurs disponibles. Les voici avec une brève explication :
- /**
- * Le nom du champ à utiliser comme clef.
- *
- * Par défaut, cette valeur est définie sur la valeur spéciale '__auto__', ce qui signifie que la classe
- * déterminera automatiquement quel champ/colonne doit être utilisé comme clef
- * Si vous souhaitez que les clefs de tableau renvoyées soient définies sur une valeur de champ/colonne
- * spécifique, il vous suffit de spécifier le nom de ce champ/colonne dans $keyField
- * Si vous souhaitez que les clefs de tableau renvoyées soient définies sur un index numérique
- * commençant à 0, vous devez définir $keyField=false
- * @var string
- */
- protected $keyField = '__auto__';
-
- /**
- * Faire d'un champ le point d'intérêt
- *
- * Ce champ modifie la façon dont certains tableaux sont renvoyés :
- * - si $focus contient un nom de champ, une ligne sera la valeur de ce champ (scalaire) au lieu d'un tableau de toutes les valeurs de la ligne
- * - si la ligne ne contient pas de champ, une valeur vide est utilisée pour la ligne
- * @var string
- */
- protected $focus = ''; /* peut être vide pour « non » ou toute autre valeur pour le nom du champ */
-
- /**
- * supprime toute ligne sans contenu
- * @var boolean
- */
- protected $noEmptyRow = false;
-
- /**
- * Lignes directrices pour le typage/la conversion forcée des données de résultat
- *
- * @var string | array of strings
- */
- protected $typecast;
-
- /**
- * La première ligne du résultat est renvoyée au lieu d'un tableau d'une seule ligne
- *
- * @var boolean
- */
- protected $singleRow = false;
-
- /**
- * Échapper automatiquement aux mauvais caractères pour tous les paramètres entrants
- * @var boolean
- */
- protected $autoProtect = false;
-
- /**
- * Au lieu du résultat de la requête, renvoie le last_insert_id()
- * @var boolean
- */
- protected $returnId = false;
-
- /**
- * Renvoie une valeur du champ demandé de la ligne demandée
- * @var boolean
- */
- protected $singleValue = false;
Pour demander la manipulation des données dans une requête, incluez simplement le commutateur dans la classe d'objet de base de données, par exemple :
- class editExampleQuery extends PHPDS_query {
- protected $sql = "SELECT example_name FROM _db_ExamplePlugin_example WHERE id = %u";
- protected $singleValue = true;
- }
Nous demandons au système de requête d'appeler la requête, mais de ne pas renvoyer de tableau, mais uniquement la valeur requise. Désormais, seul le nom de la colonne sera renvoyé directement.
Pour aller plus loin dans l'exemple, disons que nous voulons renvoyer l'ID de la ligne que nous venons d'insérer, nous pouvons également ajouter le commutateur suivant :
- class writeExampleQuery extends PHPDS_query {
- protected $sql = "INSERT INTO _db_ExamplePlugin_example (id, example_name, example_note, alias) VALUES (%u, '%s', '%s', '%s')";
- protected $returnId = true;
- }
Notez que %u et %s sont des spécificateurs de type de données (string=%s et unsigned integer=%u). Ce sont les mêmes que ceux que sprintf utiliserait.
Appelons la requête et obtenons l'ID renvoyé :
- $id = $this->db->invokeQuery('exampleQuery', '', 'Sylvain', 'Quelques remarques', 'hello');
- /* $id contient désormais l'ID de base de données renvoyé. */
Vérification ou modification des paramètres avant l'exécution de la requête
Tous les paramètres d'une requête ne doivent pas être spécifiés lors de son appel. Par exemple, une requête peut utiliser l'ID utilisateur actuel. Regardons cet exemple étendant la requête ci-dessus :
La méthode checkParameters() est appelée juste avant que la requête ne soit effectivement envoyée à la base de données. Elle permet à l'objet de requête de vérifier les valeurs des paramètres de l'appelant et/ou d'injecter ses propres valeurs. Elle peut également empêcher l'envoi de la requête (si par exemple certains paramètres sont manquants).
Comme vous pouvez également le voir dans cet exemple, les requêtes sont de véritables entités OOP, et bénéficient donc de tous les mécanismes comme l'extension d'une classe existante.
Vérification ou modification des résultats après l'exécution de la requête
Les données extraites de la base de données ont parfois besoin d'être un peu peaufinées avant d'être renvoyées à l'appelant. Un objet de requête a la possibilité de s'assurer que ses résultats sont conformes à ce que l'appelant attend. Voir cet exemple :
La méthode checkResults() permet à la requête de vérifier et/ou de modifier les données de résultat avant de les renvoyer. Dans ce cas, elle ajoute le mot "User" avant le nom d'utilisateur si la requête en a renvoyé un, ou signale une erreur (en renvoyant False) dans le cas contraire.
Amélioration supplémentaire des objets de base de données avant qu'ils ne soient renvoyés
Comme tout le reste dans PHPDevShell, la requête peut être remplacée. Dans de nombreux cas, vous souhaiterez manipuler des données, améliorer davantage les requêtes et filtrer vos données avant qu'elles ne soient renvoyées à ce qui les a demandées.
Pour ce faire, c'est simple, vous allez remplacer l'appel (en utilisant la fonction publique invoke()), effectuer la requête et renvoyer les données. Pour remplacer l'appel, nous allons procéder ainsi :
- class exampleQuery extends PHPDS_query {
- protected $sql = "SELECT id, example_name, example_note FROM _db_ExamplePlugin_example WHERE example_name = '%s' AND example_note = '%s'";
-
- /**
- * Remplacer l'appel
- * @return string
- */
- /* REMARQUE : l'ajout de cette «public function invoke» est la façon dont nous remplaçons et manipulons les données renvoyées par la requête SQL. */
-
- public function invoke($parameters)
- {
- /* Nous sommes maintenant dans une nouvelle instance de substitution d'appel. Commençons par capturer les données qui sont transmises aux paramètres. */
- list($example_name, $example_note) = $parameters;
-
- /* Nous pouvons maintenant manipuler les données, supprimons les espaces blancs de $example_note à titre d'exemple. */
- $example_note = trim($example_note);
-
- /* Nous pouvons maintenant effectuer la requête normalement et continuer à manipuler les données. Pour ce faire, nous effectuons l'appel d'origine. */
- $array = parent::invoke(array($example_name, $example_note));
-
- /* Nous pouvons maintenant boucler les données et en faire ce que nous voulons. */
- foreach ($array as $values) {
- $id = $values['id'];
- $name = $values['example_name'];
- $count ++;
- $new_array[] = array($id, $name, $count);
- }
-
- /* Nous pouvons même faire plus de requêtes à partir d'ici. */
- $this->db->invokeQuery('MoreQueries');
- return $new_array;
- }
- }
Comme vous pouvez le voir, vous avez vraiment le contrôle total. N'oubliez pas que vous pouvez invokeQueries dans d'autres requêtes, ou inclure n'importe quel fichier .query.php dans n'importe quel autre fichier, ce qui vous permet de réutiliser les requêtes encore et encore.
Passer un tableau de valeurs au lieu de valeurs individuelles
Que se passe-t-il si j'ai un tableau de valeurs que je souhaite transmettre avec invokeQuery, au lieu de transmettre des valeurs individuelles ? Disons par exemple :
- $where_clause = array('Sylvain', 'Hello');
- $array = $this->db->invokeQuery('exampleQuery', $where_clause);
Étant donné que les valeurs transmises sont placées dans un tableau, nous devons remplacer l'appel pour les extraire. Notez dans le code ci-dessous qu'il n'y a qu'un seul changement. Voir les remarques :
- class exampleQuery extends PHPDS_query {
- protected $sql = "SELECT id, example_name, example_note FROM _db_ExamplePlugin_example WHERE example_name = '%s' AND example_note = '%s'";
-
- /**
- * Remplacer l'appel
- * @return string
- */
- /* REMARQUE : l'ajout de cette «public function invoke» est la façon dont nous remplaçons et manipulons les données renvoyées par la requête SQL. */
- public function invoke($parameters)
- {
- /* Étant donné que nous avons transmis un tableau de valeurs, ce tableau sera indexé dans un tableau à l'index [0], nous devons donc les récupérer.
- * REMARQUE : Le seul changement apporté au code ci-dessous est que nous avons modifié ceci :
- * = parameters;
- * à ceci :
- * = $parameters['0'];
- */
- /* Nous sommes maintenant dans une nouvelle instance de substitution d'appel. */
- /* Commençons par capturer les données étant transmises aux paramètres. */
- list($example_name, $example_note) = $parameters['0'];
-
- /* Nous pouvons maintenant manipuler les données, supprimons les espaces blancs de $example_note à titre d'exemple. */
- $example_note = trim($example_note);
-
- /* Nous pouvons maintenant effectuer la requête normalement et continuer à manipuler les données. Pour ce faire, nous effectuons l'appel d'origine. */
- $array = parent::invoke(array($example_name, $example_note));
-
- /* Nous pouvons maintenant boucler les données et en faire ce que nous voulons. */
- foreach ($array as $values) {
- $id = $values['id'];
- $name = $values['example_name'];
- $count ++;
- $new_array[] = array($id, $name, $count);
- }
-
- /* Nous pouvons même faire plus de requêtes à partir d'ici. */
- $this->db->invokeQuery('MoreQueries');
- return $new_array;
- }
- }
Enregistrer une classe
Parfois, les modèles deviennent complexes et le même modèle est utilisé encore et encore. Lorsque c'est le cas, il est fortement recommandé d'enregistrer une classe à la place, d'ajouter la classe dans includes, puis de l'appeler avec la méthode factory.
C'est plus simple qu'il n'y paraît. Nous allons d'abord créer la classe dans le dossier includes de vos plugins. La classe doit être nommée avec .class.php à la fin pour permettre le chargement à la demande.
Appelons donc cette classe :
includes/myClass.class.php |
De toute évidence, une classe d'assistance doit avoir son propre fichier de modèles pour effectuer ses requêtes, cela se fait exactement comme avec les contrôleurs normaux, dans le dossier racine des modèles que nous ajoutons :
models/myClass.query.php |
Vous pouvez maintenant invoquer des requêtes à partir de votre classe d'aide appelée myClass.class.php comme vous vous y attendriez.
La classe d'aide est généralement appelée à partir du contrôleur ou du fichier modèle, mais il n'y a pas d'emplacement approprié pour l'appeler, juste là où elle est nécessaire. Une classe d'aide doit commencer par le nom que vous avez donné au fichier, elle doit également étendre PHPDS_dependant, ce qui lui permet de partager des ressources :
Appeler des classes d'aide est également facile.
Remarque : vous souhaiterez peut-être enregistrer votre classe dans votre fichier de configuration de plugiciels, ce qui vous permettra de partager votre classe avec tous vos plugiciels.
Appelons notre aide :
- $myClass = $this->factory('myClass');
- echo $myClass->someMethod();
Transactions et verrouillage de ligne
Dans certaines situations, vous devrez utiliser START TRANSACTION et COMMIT pour effectuer une requête de base de données et verrouiller une ligne afin que personne d'autre ne puisse y accéder jusqu'à ce que vous ayez terminé. Si vous utilisez MySQL, le moteur InnoDB prend en charge ce type de transaction et de verrouillage de ligne.
Remarque : de nombreux serveurs utilisant MySQL ont le moteur par défaut défini sur MyISAM. Le moteur MyISAM ne peut pas effectuer de transactions ou de verrouillage de ligne. Vous devez utiliser un moteur comme InnoDB prenant en charge les transactions et le verrouillage de ligne.
Prenons un exemple dans lequel nous devons récupérer un numéro unique et nous assurer que personne d'autre ne récupère ce même numéro. Voici les étapes à suivre :
- Utilisez START TRANSACTION.
- Utilisez SELECT avec FOR UPDATE pour verrouiller la ligne et récupérer le numéro.
- UPDATE la ligne avec un nouveau numéro pour la personne suivante.
- Utilisez COMMIT pour déverrouiller la ligne pour les autres.
Exemple de DATABASE TABLE
Voici le code SQL pour la table de base de données de test que nous allons utiliser dans cet exemple :
- CREATE TABLE `pds_test_unique_number` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `prefix` text COLLATE utf8_unicode_ci NOT NULL,
- `unique_number` int(11) NOT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-
- INSERT INTO `pds_test_unique_number` (`id`, `prefix`, `unique_number`) VALUES (1, 'ABC', 1000001);
Exemple de fichier CONTRÔLEUR
Notre fichier contrôleur ressemblera à ceci :
- <?php
- class getUniqueNumber extends PHPDS_controller {
- public function execute() {
- /* Donnez-lui un joli titre */
- $this->template->heading(_('GET UNIQUE NUMBER'));
-
- /* Appelez notre requête qui renverra un identifiant unique */
- $unique_number = $this->db->invokeQuery('myPlugin_getNewUniqueId');
-
- /* Appeler le fichier de gabarit */
- $view = $this->factory('views');
- $view->set('unique_number', $unique_number);
- $view->show();
- }
- }
- return 'getUniqueNumber';
Exemple de fichier MODELE
Notre fichier modèle ressemblera à ceci :
- <?php
- class myPlugin_getNewUniqueId extends PHPDS_query {
- public function invoke($params) {
- /* Nous devons START TRANSACTION pour pouvoir verrouiller la ligne. */
- /* REMARQUE : pour verrouiller une ligne avec SELECT, vous devez utiliser FOR UPDATE. */
-
- $this->db->startTransaction();
-
- /* Obtenez le prochain identifiant unique */
- $results = $this->db->invokeQuery('myPlugin_getNextAvailableUniqueId');
-
- /* Extraire l'ID de ligne et l'ID unique de la requête renvoyée */
- $id = $results['id'];
- $unique_id = $results['unique_id'];
-
- /* Augmentez la partie numérique de l'identifiant d'un dans le tableau pour la personne suivante */
- $this->db->invokeQuery('myPlugin_increaseUniqueIdByOne', $id);
-
- /* Il faut s'engager pour pouvoir déverrouiller la ligne */
- $this->db->endTransaction();
-
- /*retourne $id; */
- return $unique_id;
- }
- }
-
- class myPlugin_getNextAvailableUniqueId extends PHPDS_query {
- protected $sql = "SELECT id,prefix,unique_number FROM pds_test_unique_number LIMIT 1 FOR UPDATE;";
- protected $keyField=false;
-
- public function invoke($params) {
- /* Appelez la requête de base de données. */
- /* REMARQUE : la requête SELECT doit utiliser FOR UPDATE pour verrouiller la ligne. */
- $results = parent::invoke();
-
- /* Obtenons l'ID de ligne de base de données qui a été utilisé afin que nous puissions le mettre à jour ultérieurement */
- $id = $results[0]['id'];
-
- /* L'ID UNIQUE est une combinaison des deux champs suivants, alors mettons-les ensemble. */
- $unique_id = $results[0]['prefix'].$results[0]['unique_number'];
-
- /* Renvoyer les résultats */
- return array('id' => $id, 'unique_id' => $unique_id);;
- }
- }
-
- class myPlugin_increaseUniqueIdByOne extends PHPDS_query
- {
- /* Cela incrémentera le champ spécifié d'un pour la personne suivante. */
- protected $sql = "UPDATE pds_test_unique_number SET unique_number = unique_number + 1 WHERE id=%u LIMIT 1;";
- }
N'oubliez pas que les modèles peuvent être réutilisés en incluant simplement le modèle d'un contrôleur dans un autre contrôleur à l'aide de PHP include standard :
- require_once 'plugins/ExamplePlugin/models/someModel1.query.php';
- require_once 'plugins/ExamplePlugin/models/someModel2.query.php';
- /* Maintenant, nous pensons utiliser leurs appellations. */
- $this->db->invokeQuery('queryFromModel1');
- $this->db->invokeQuery('queryFromModel2');
Exemple de fichier VUE
Notre fichier view ressemblera à ceci :
- L'identifiant unique est : {$unique_number}