Section: Fichier DAO de base
^ jDao : mapping objet relationnel | Utilisation d'une factory et d'un record DAO » |
− Table des matières
Pour utiliser jDao, il faut d'abord décrire dans un fichier XML le mapping objet-relationnel, c'est à dire indiquer dans ce fichier la correspondance entre les propriétés de l'objet DAO et les champs d'une ou plusieurs tables
Création automatique ¶
Le fichier d'un DAO peut être généré par le script jelix en ligne de commande, à partir d'une table de base de données existante.
php jelix.php --nom_application createdao nom_module nom_dao nom_table nom_sequence
Par exemple, si vous voulez créer dans le module "myshop", un DAO "product" qui sera basé sur la table "shop_product", vous taperez donc :
php jelix.php --nom_application createdao myshop product shop_product
Dans le répertoire daos
du module myshop vous allez donc avoir un fichier XML product.dao.xml
qui va contenir la description du mapping. Le nom de la table est optionnel si vous voulez utiliser le même nom de dao que celui de la table, et que vous n'avez pas de séquence. Indiquez le nom de la séquence utilisée pour auto incrémenter la clé primaire (paramètre nécessaire pour les bases oracles et parfois postgresql).
Bien que la génération automatique permette de gagner du temps, il faut souvent retoucher le fichier pour que le mapping corresponde mieux à ce que l'on veut faire, le générateur ne pouvant tout deviner. Voyez alors la suite pour le compléter, ou en créer un à la main.
Détails sur le format XML ¶
La structure d'un fichier DAO ressemble à cela :
<dao xmlns="http://jelix.org/ns/dao/1.0">
<datasources>
section datasources
</datasources>
<record>
section properties
</record>
<factory>
section methodes
</factory>
</dao>
Il y a trois sections, sachant que la section <factory>
(ou "methodes") est facultative et est décrite dans un autre chapitre dédiée.
datasources
: indique les tables sur lequel reposera l'objet.record
: indique la correspondance entre les propriétés de l'objet et les champs des tables et définit donc les propriétés qu'il y aura sur l'objet record.
Correspondance simple ¶
Déclaration de la table ¶
On appelle correspondance simple, une correspondance où un record est mappé sur une seule table. Pour déclarer la table sur laquelle reposera le DAO, on utilise la balise <primarytable>
, avec les attributs suivants :
name
: alias donné à la table et qui sera utilisé dans les requêtesrealname
(facultatif) : nom réel de la table dans la base de données. Si cet attribut n'est pas précisé, il prend la même valeur que l'attributname
. Dans ce casname
doit être le nom réel de la table.primarykey
indique la clé primaire. Vous pouvez indiquer plusieurs champs en les séparant par un espace ou une virgule.
<datasources>
<primarytable name="p" realname="products" primarykey="id_product" />
</datasources>
On déclare ici que le record sera basé sur la table "products", qui a pour alias "p", et dont la clé primaire est "id_product".
Il n'y a toujours qu'une seule table "primaire" dans un DAO (donc une seule balise <primarytable>
). Vous verrez que l'on peut indiquer des tables annexes (étrangères) plus loin.
Ensuite, il faut déclarer la correspondance propriété - champs.
Déclaration des propriétés ¶
La section <record>
déclare les propriétés d'un objet record
(enregistrement). Chaque propriété correspond à l'un des champs de la table primaire, ou l'un de ceux des tables étrangères comme vous le verrez plus loin. Bien sûr, vous n'êtes pas obligés de déclarer une propriété pour tous les champs existants. On peut ainsi faire plusieurs DAO qui travaillent sur une même table mais qui sont destinés à des usages différents. Par exemple faire un DAO spécifique pour récupérer des listes légères d'enregistrement (on ne déclarera que les propriétés essentielles), et un autre pour les gérer de manière complète (on y indiquera tous les champs).
La section record
doit donc contenir une ou plusieurs balises <property>
. Voici une balise <property>
avec la liste des attributs possibles :
<property
name="nom simplifié"
fieldname="nom du champ"
datatype="" required="true/false" minlength="" maxlength="" regexp=""
sequence="nom de la sequence" autoincrement="true/false"
updatepattern="" insertpattern="" selectpattern=""
default=""
/>
L'attribut name
est le nom de la propriété de l'objet.
L'attribut fieldname
est le nom du champ qui correspond. Si name
et fieldname
sont égaux, on peut omettre fieldname
.
Les attributs datatype
, required
, minlength
, maxlength
, et regexp
sont des contraintes. Cela permet par la suite d'appeler la méthode check()
sur un record pour vérifier les valeurs des propriétés (avant son stockage par exemple).
L'attribut default
permet d'indiquer une valeur par défaut.
L'attribut datatype
indique le type SQL du champs. Voici les types de champs reconnus par jDao, qui sont bien plus nombreux que dans les versions précédentes de jelix :
varchar
,varchar2
,nvarchar2
,character
,character varying
,char
,nchar
,name
,longvarchar
,string
(deprecated),int
,integer
,tinyint
,smallint
,mediumint
,bigint
,autoincrement
,serial
,bigserial
,bigautoincrement
,double
,double precision
,float
,real
,number
,binary_float
,binary_double
,money
,numeric
,decimal
,dec
,date
,time
,datetime
,timestamp
,utimestamp
,year
,interval
boolean
,bool
,bit
tinytext
,text
,mediumtext
,longtext
,long
,clob
,nclob
tinyblob
,blob
,mediumblob
,longblob
,bfile
,varbinary
,bytea
,binary
,raw
,long raw
enum
,set
,xmltype
,point
,line
,lsed
,box
,path
,polygon
,circle
,cidr
,inet
,macaddr
,bit varyong
,arrays
,complex types
Les bases de données ne reconnaissent pas tous ces types de champs. Ne vous inquietez pas, indiquez le type que vous voulez, et jDao utilisera le type qui correspond le mieux pour la base de donnée utilisée. Par exemple, si vous indiquez bytea
, c'est un type utilisé par postgresql. Si vous utilisez mysql, jDao utilisera en fait le type longblob
.
Les valeurs des champs seront convertis dans le type PHP le plus approprié, la plupart du temps, une chaine.
Pour les champs auto incrementés, vous pouvez indiquer le type serial
ou autoincrement
. Mais dans certain cas, il est préférable d'indiquer le type d'entier (integer
, tinyint
..) et de mettre un attribut autoincrement
à true
. Sur certaines bases, on peut associer une séquence à un champ. L'attribut sequence
doit alors indiquer son nom.
Les attributs updatepattern
, insertpattern
et selectpattern
permettent d'indiquer un "motif" à appliquer lors de la mise à jour, l'insertion ou la lecture de la valeur du champ dans la table. Ce motif doit en fait être une expression SQL, contenant éventuellement la chaîne "%s" qui sera remplacée par la valeur ou le nom du champ. Par défaut leurs valeurs vaut "%s". Si on indique une valeur vide, cela correspond à une opération nulle (le champ n'est pas lu, inséré ou mis à jour). Vous pouvez utiliser insertpattern
et selectpattern
sur des clés primaires, mais pas updatepattern
.
Exemple 1 ¶
Pour un champ qui contient une date de mise à jour, on pourra indiquer :
<property name="date_update" datatype="datetime" insertpattern="NOW()" updatepattern="NOW()" />
Ainsi chaque fois qu'un INSERT
ou un UPDATE
sera fait, la valeur insérée sera la date du jour (et non celle que l'on aurait indiquée dans la propriété date_update
du record).
Exemple 2 ¶
On peut aussi avoir une propriété qui ne correspond pas directement à un champ, mais qui soit le résultat d'une expression SQL. Dans ce cas, il faut désactiver l'insertion et la mise à jour.
<property name="identite" datatype="string" selectpattern="CONCAT(nom, ' ',prenom)" insertpattern="" updatepattern="" />
Attention, en ce qui concerne l'expression de selectPattern
:
- l'expression doit utiliser des champs d'une même table. Si le dao est basé sur plusieurs tables (ex : A et B, voir section suivante), il n'est pas possible que l'expression utilise à la fois des champs de la table A et de la table B
- si l'expression utilise des champs d'une table B qui ne soit pas la table principale, la propriété doit être attribué à cette table B, et non à la table principale. Ce qui veut dire que la propriété doit avoir un attribut
table
ayant pour valeur le nom/alias de cette table B.
Correspondance avec plusieurs tables ¶
On peut déclarer une table principale, mais aussi des tables annexes qui seraient liées à la table principale par des jointures. Il est utile, lorsque l'on veut récupérer un enregistrement, de récupérer en même temps des informations de tables annexes. Par exemple, si on veut récupérer un produit de la table "products", et en même temps le libellé de sa catégorie qui se trouve dans une table "category", on déclarera aussi la table "category". À noter que vous ne pourrez modifier que les données issues de la table principale quand vous voudrez mettre à jour un enregistrement.
Pour déclarer de telles tables étrangères, qui en toute logique sont liées à la table principale par des clés étrangères, il faut utiliser :
<foreigntable>
pour indiquer une table étrangère liée par une jointure normale.<optionalforeigntable>
pour indiquer une table étrangère liée par une jointure externe.
Exemple ¶
<primarytable name="p" realname="products" primarykey="id_product" />
<foreigntable name="cat" realname="categories" primarykey="id_cat" onforeignkey="id_cat" />
<optionalforeigntable name="man" realname="manufacturers" primarykey="id" onforeignkey="id_manufacturer" />
Comme pour la balise <primarytable>
, il y a les attributs name
, realname
et primarykey
. Il y a par contre un attribut supplémentaire, onforeignkey
, qui indique le nom du champ dans la table primaire, qui est la clé étrangère sur la table en question. Ainsi, avec l'exemple ci-dessus, jDao générera pour les requêtes de type SELECT
les clauses FROM
et WHERE
suivantes :
FROM products as p left join manufacturers as man on (p.id_manufacturer = man.id), categories as cat
WHERE cat.id_cat = p.id_cat
Indiquer des tables annexes n'a de sens que si vous voulez avoir une ou plusieurs propriétés correspondantes à leurs champs. Vous ajouterez donc autant de balise <property>
que vous voudrez. La seule différence est qu'il faut ajouter un attribut table
qui indique l'alias de la table dans laquelle se trouve le champ.
<property
name="libelle_categorie"
fieldname="label"
table="cat"
/>
Dans la propriété $libelle_categorie
du record, se trouvera la valeur du champ label
de la table categories
("cat" étant l'alias de cette table, comme il a été défini plus haut dans la balise foreigntable
).