.. |vspace| raw:: latex \vspace{5mm} .. |embed_js| raw:: html |embed_js| ************************************************* Formulaires et enregistrements en base de données ************************************************* ============================================================ Les enregistrements de base de données et la classe dbRecord ============================================================ Que ce soit dans une application ou dans un site Internet, un développeur est très souvent appelé à extraire et écrire des données depuis une base de données. Néanmoins la syntaxe est complexe, et il est souvent fastidieux d'aller lire ou écrire ces données. Dans MindFlow il est possible (et recommandé) de créer un objet hérité de la classe dbRecord pour simplifier l'accès à la base de données. Ainsi, si on veut lire l'email du membre ayant pour uid 113, plutôt que d'écrire : .. code-block:: php $sql = "SELECT email FROM members WHERE uid = ".$pdo->quote(113); $stmt = $pdo->query($sql); $row = $stmt->fetch(PDO::FETCH_ASSOC); echo $row['email'] A partir du moment ou notre classe est définie, on peut écrire avec MindFlow: .. code-block:: php $member = new member(); $member->load(113); echo $member->data['email']['value']; Et si on veut y modifier l'email d'un membre, plutôt que d'écrire : .. code-block:: php $sql = "UPDATE members set email = ".$pdo->quote("john@doe.com")." WHERE uid = ".$pdo->quote(113); $stmt = $pdo->query($sql); On peut écrire : .. code-block:: php $member->data['email']['value'] = "john@doe.com"; $member->store(); Dans les 2 cas, vous noterez que l'on lit ou qu'on écrit dans le tableau nommé 'data' contenu dans notre objet : .. code-block:: php //lecture : echo $member->data['email']['value']; //écriture $member->data['email']['value'] = "john@doe.com"; Le tableau 'data' contient donc toutes les données de notre objet. MindFlow se charge ensuite de lire ou d'écrire son contenu en base de données. Ce service de lecture / écriture est fourni de base par la classe dbRecord, et vous n'avez pas à écrire de code SQL pour le faire fonctionner. Il vous faut simplement respecter le format du tableau data en respectant quelques règles élémentaires : Un tableau data contient une clé pour chaque champ stocké en base de données. Chaque clé stocke un tableau contenant la définition du champ concerné. Si dans votre table vous avez une clé nommée 'name' alors vous la définirez comme suit : .. code-block:: php $this->data['name'] = array( 'value' => '', //valeur du champ 'dataType' => 'input', //type de champ pour l'édition formulaire 'valueType' => 'text', //type de la valeur du champ ); * ``value`` contient la valeur du champ, qui sera écrite ou lue dans la base de données. |vspace| * ``dataType`` indique le type de champ d'édition lorsqu'on affiche l'objet sous forme de formulaire (rappelez-vous, MindFlow génère les formulaires quasi automatiquement). Voir les valeurs possibles dans la section Les clés génériques du tableau 'data'. |vspace| * ``valueType :`` pas vraiment obligatoire, mais permet de déterminer le type de valeur contenue dans le champ, indépendamment du type de champ d'édition. Voir les valeurs possibles dans la section Les clés génériques du tableau 'data'. Cette information est utile pour traiter la valeur de manière appropriée lors d'un export au format Excel CSV par exemple. Il est conseillé de remplir ce champ lors de la définition. .. important:: Lorsque vous accédez à la valeur d'un champ, attention à ne pas oublier de spécifier le sous-tableau ``['value']`` : .. code-block:: php echo $member->data['email']['value']; // BON : l'email est renvoyé echo $member->data['email']; // MAUVAIS : lève une erreur Notice: Array to string conversion Pour pouvoir utiliser les services de dbRecord, il vous faut préalablement créer un objet hérité de cette classe. **Cette définition inclus la spécification SQL des colonnes de la table associée à l'objet.** Créons la classe member : .. code-block:: php class member extends dbRecord { //on étend la classe dbRecord // nom de la table dans la BDD sur laquelle va travailler cet objet static $tableName = 'member'; // définition de la table dans la BDD. // Celle-ci est utilisée pour pouvoir créer la table depuis l'installeur MindFlow static $createTableSQL= " `name` varchar(256) DEFAULT NULL, `email` varchar(256) DEFAULT '' "; // on peut spécifier des clés d'indexation pour augmenter les performances // ou pour effectuer des recherches fulltext static $createTableKeysSQL=" FULLTEXT KEY `member_search` (`name`,`email`) "; // on initialise le tableau 'data' contenant les données de l'objet en précisant // les valeurs par défaut dans les champ 'value' et le type de champ d'édition // pour leur formulaire dans le champ 'dataType' : function init(){ parent::init(); //Important ! Inclus les champs standard de la classe dbRecord (creation_date, hidden_deleted...) dans notre nouveau type d'enregistrement. // on charge le fichier de localisation qui contient les noms d'affichage // des champs suivants pour les être humains. Par exemple 'name' sera // traduit en 'Votre nom' $this->loadL10n('/mf/plugins/mf_users','objects'); $this->data['name'] = array( 'value' => '', 'dataType' => 'input', 'valueType' => 'text', ); $this->data['email'] = array( 'value' => '@', 'dataType' => 'input', 'valueType' => 'text', ); } } Après avoir défini ainsi votre objet, celui-ci devient utilisable. Peut-être aurez-vous remarqué que le champ 'uid' qui apparaissait dans exemples précédent n'est pas cité dans la classe ? En fait, celui, ainsi qu'un certain nombre d'autres champs sont directement hérités de la classe dbRecord, qui fourni systématiquement les champs suivants : .. code-block:: mysql `uid` int(11) NOT NULL AUTO_INCREMENT, # Identifiant unique de l'enregistrement `deleted` tinyint(4) NOT NULL DEFAULT '0', # valeur à 1 quand l'enregistrement est effacé `hidden` tinyint(1) NOT NULL DEFAULT '0', # valeur à 1 quand l'enregistrement est masqué `sorting` int(11) NOT NULL, # index de tri pour ordonner plusieurs enregistrements `creation_date` datetime NOT NULL, # date de création de l'enregistrement `modification_date` datetime NOT NULL, # date de dernière modification `creator` int(11) NOT NULL, # uid du créateur de l'enregistrement (encore inutilisé) `change_log` text NOT NULL, # log des modifications de l'enregistrement (encore inutilisé) `edit_lock` int(11) NOT NULL, # pour verrouiller un enregistrement (encore inutilisé) `start_time` datetime NOT NULL, # date d'activation d'un enregistrement (encore inutilisé) `end_time` datetime NOT NULL, # date de désactivation d'un enregistrement (encore inutilisé) `language` varchar(5) NOT NULL, # Code ISO langue de l'enregistrement `alt_language` tinytext, # uid des enregistrements similaires dans des langues différentes **Tous ces champs sont créés et maintenus automatiquement par MindFlow. Vous n'avez rien à faire.** Lors de la création de la table de votre objet, lors de son instanciation ou de son enregistrement, ceux-ci sont gérés par la classe dbRecord dont est hérité votre objet. Vous avez néanmoins la possibilité de les lire ou de les modifier selon vos souhaits. Si par exemple vous souhaitez altérer la valeur du champ 'creation_date', il vous suffira d'y accéder via le tableau data : .. code-block:: php $monObjet->data['creation_date']['value'] = '2016-03-02'; //notez au passage l'usage ici de l'argument optionel 'false' de la fonction store, //qui permet ici de forcer la date de création (sinon elle serait gérée automatiquement par dbRecord). $monObjet->store(false); | ====================================== Format des données : Array vs stdClass ====================================== A ce stade, vous vous demandez peut-être pourquoi nous avons fait le choix d'utiliser un objet de type array pour stocker les données. En effet, la syntaxe serait un poil plus agréable si on écrivait : .. code-block:: php $monObjet->creationDate->value = '2016-03-02'; Il y a une première raison à cela : la performance. En effet les tableaux sont des objets lourdement optimisés dans tous les langages de programmation, et il offrent des performances supérieures lorsqu'il s'agit de lire et écrire des données. **Voici le temps d'exécution d'un test de création de 1 million d'objets de type stdClass et d'1 million d'objets de type Array :** .. code-block:: php stdClass=0.443578004837 sec Array=0.210082054138 sec **Les objets Array sont 2 fois plus rapides à créer que les stdClass.** La seconde raison, et la plus importante, est que les Arrays peuvent être facilement multidimensionnels et surtout disposent de toute **une foule de fonctions** d'itération, de fusion, de scission, de tri, d'empilement etc qui en font **la structure le plus approprié qui soit pour stocker des données et les manipuler.** Leur plus gros défaut, en définitive, c'est leur syntaxe puisqu'il faut taper des crochets et des guillemets, soit 2 caractères de plus à chaque accès ([''] vs ->). Espérons que les gains de performance et de possibilités obtenus vous motiveront à supporter cet affront ! ======================================== Créer un formulaire ou un enregistrement ======================================== Spécification du formulaire --------------------------- Avec MindFlow, il ne vous est plus nécessaire de rédiger le HTML pour créer un formulaire, ni d'écrire le code collectant les valeurs lors de sa soumission. Il vous faut juste spécifier les champs souhaités, avec la possibilité de personnaliser le formulaire dans une certaine mesure, qui tend à offrir de plus en plus de possibilités avec les nouvelles versions de MindFlow. L'affichage des formulaires a recours à la bibliothèque Twitter Bootstrap qui permet de générer des formulaires responsive et mobile friendly. Pour créer un formulaire, **vous devez créer une classe héritant soit de la classe** ``dbRecord`` **, soit de la classe** ``dbForm`` **:** * **dbRecord :** crée un objet PHP qui pourra être affiché/édité sous forme de formulaire et qui pourra être sauvegardé en base de données. La création d'un objet dbRecord requiert qu'il existe dans la base de données une table comportant les champs listés dans le tableau 'data' de la classe définie. |vspace| * **dbForm :** crée un objet PHP qui pourra être affiché/édité sous forme de formulaire, mais qui n'a pas d'existence en base de données. Il vous appartient ensuite de traiter les valeurs obtenues selon vos besoins, en les glissant dans un mail par exemple. Dans les 2 cas, **la génération de formulaire repose sur la définition saisie dans le tableau 'data' contenu dans la classe**, qui est 100% compatible entre les 2 classes. Ainsi il est possible de copier / coller la spécification du tableau data d'une classe héritée de dbRecord vers une classe héritée de dbForm, et vis-et-versa. **Le tableau 'data' doit toujours être initialisé dans la fonction** ``init()`` **de l'objet** ``dbRecord`` **ou** ``dbForm`` **.** Voici un exemple de formulaire avec 1 seul champ de type 'select' (select box) : .. code-block:: php class myContact extends dbRecord{ function init(){ //chargement du fichier de localisation situé //dans le sous dossier 'objects' du plugin 'myContact' $this->loadL10n('myContact','objects'); //creation d'un champ 'contact' $this->data['contact'] = array( 'value' => '1', // 'dataType' => 'select', 'possibleValues' => array( 1 => 'Par téléphone et email', 2 => 'Par téléphone', 3 => 'Par email' ), 'valueType' => 'number', 'field_attributes' => array( 'class' => 'MyFieldClass MyOtherFieldClass', // Classe perso ou Bootstrap //appel d'une fonction JS lors d'un changement sur le champ 'onChange' => 'updateOtherfield();' ), 'div_attributes' => array( 'class' => 'col-lg-3' // Classe Bootstrap grid ) ); } } Dans l'exemple précédent, on notera les clés suivantes : * ``value :`` la valeur par défaut du champ, tel qu'elle sera affichée si le formulaire est affiché vide. Cette valeur par défaut sera substituée par la valeur saisie par l'utilisateur une fois le formulaire rempli, ou par la valeur chargée depuis la base de données si l'objet y est lu. * ``dataType :`` le type de champ de formulaire qui sera affiché. * ``valueType :`` type de la valeur pour export CSV * ``possibleValues :`` cette clé n'existe que pour les champs de type select. On y défini un tableau associatif ' valeur' => 'texte' qui définira les champs