8768 sujets

Développement web côté serveur, CMS

Bonjour,

je suis actuellement en train de coder un site décrivant les données techniques d'un ensemble de produits et j'aimerai organiser correctement ma base de donnée.

Dans un premier temps, j'ai utilisé une table unique pour tester les requêtes :
A un produit on lui associe une liste d'éléments simples (nom, présentation, fiche technique, schéma...) à champ unique. Si l'élément est présent dans la table, on l'affiche dans un onglet, sinon il est masqué.

Désormais je désire permettre à un produit de posséder plusieurs fiches techniques, schémas, ... J'ai donc décidé de modifier ma base de donnée (ça revient à transformer la relation 0-1 à 0-n).

Plusieurs problématiques, pour la suite on considérera un lien PDF contenue dans la table Fiche_Technique pour l'exemple. Elle contient donc deux champs : le nom et l'url.

1. Sachant qu'un produit a entre 0 et N liens PDF et qu'un lien PDF est associé à un et un seul produit, quelle méthode considérer ?

- créer une table de jointure (nommons là P_FT pour Produit/Fiche_technique) contenant une clef primaire composée de deux éléments : foreign_P et foreign_FT (égaux aux ID des tables respectives).
- rajouter un champ dans chaque élément (Fiche_technique ici) nommé foreign_P, égal au seul ID du produit contenant ce lien PDF.
http://i.imgur.com/AihBB0g.png
Ci-dessus sur l'image, à gauche une table de jointure, à droite l'ajout d'un champ dans Fiche_technique. Je n'ai pas affiché les relations qui sont citées plus haut :
-> Un produit contient 0..n fiches techniques
-> Une fiche technique appartient à 1..1 produit

2. Au niveau de la manière d'écrire correctement les requêtes SQL j'ai cru comprendre qu'il était préférable d'utiliser INNER JOIN plutôt que WHERE lorsqu'il s'agit de jointures, c'est bien cela ? Contrairement à l'image les champs foreign_P et foreign_FT définissant la Primary Key de la table de jointure doivent être de type "INT" tout simplement et non "Auto_increment" ?
Ensuite, pour renommer une table j'écrivais d'habitude "FROM Table1 AS t1,Table2 AS t2" : l'écriture "FROM Table1 t1, Table2 t2" fonctionne aussi ? Laquelle est la plus "propre" d'utilisation ?

L'utilisateur doit pouvoir modifier la BDD en rajoutant des fiches techniques à un produit : dans le cas de la table de jointure la requête INSERT INTO semble plus compliquée puisque l'on doit modifier deux tables (Fiche_Technique et PFT) au lieu d'une (Fiche_Technique).
Au niveau de l'insert de donnée on aurait ça ? Quelles possibilités perdons-nous en n'utilisant pas de table de jointure ?

INSERT INTO Fiche_technique (nom,url,foreign_P)
VALUES ('texte à afficher','url du PDF',$id_P);

$id_P étant une variable PHP contenant l'ID (ou le nom) du produit en question.


Au niveau de la requête cela donnerait donc ceci ?

SELECT FT.nom, FT.url
FROM Fiche_technique FT
INNER JOIN Produit P
ON id_P = foreign_P;


Édition : ce modèle logique de données relationnel est-il valide pour gérer un ensemble de produits possédant chacun des caractéristiques différentes ?
http://i.imgur.com/dfBYx4G.png
Modifié par Ara (09 Jul 2015 - 14:00)
Salut,

Pour la question 1 je pense que la solution de droite est la mieux, même si je pige pas pourquoi on stock le nom du produit dans la fiche mais bon.

Pour la question 2, je suis pas sur que cela change grand chose de faire un inner join ou un where. Personnellement j'ai tendance à mettre des where partout plutôt que des inner/outer/left... join, quand je relis du code sql alors que j'en ai pas fais depuis un moment ça m’évite d'avoir à me souvenir les différences entre chaque...
Il faut voir si c'est des auto incrémente de int ou de long, pour que cela correspond ensuite pour le type int pour les clés étrangères. Auto incrémente permet de faire des inserts sans avoir à gérer la valeur de la clé primaire (en gros ça permet d’éviter d'avoir à faire un select max(id)+1 from tatable à chaque insert)
Modifié par mathieu1004 (09 Jul 2015 - 11:52)
Salut, merci de la réponse rapide.

Un élément de Fiche_Technique est un lien PDF, il contient donc un nom (celui affiché) et une url (le lien vers lequel on est renvoyé quand on clique sur le nom). Un produit représente en fait une famille de produits et chacun possède sa fiche technique, d'où l'intérêt d'avoir un champ "nom" dans la Fiche_Technique.

Je sais globalement comment fonctionne Auto_increment (même si il m'avait semblé que Phpmyadmin enregistrait la prochaine valeur de Auto_increment à chaque INSERT, plutôt que de chercher la plus grande valeur déjà existante, la différence étant que si on supprime la dernière ligne (disons id=7) la valeur sera non pas de 6+1=7 mais toujours 7+1=8). Je me demandais plutôt si je devais écrire moi-même les Foreign Key ou si cela pouvait se faire automatiquement.

Plus précisément, je travaille actuellement sur Wordpress et je gère ma base de donnée à l'aide des fonctions existantes. Sans rentrer dans les détails pour toute modification de type CREATE TABLE et UPDATE, on utilise une fonction dbDelta() qui ne gère pas l'attribut FOREIGN KEY. Les tables natives de Wordpress n'ont pas de structure nécessitant de Foreign Key et cette propriété n'a pas été implantée : Source

Je pense que le plus simple (avec la deuxième solution) est de simuler la Foreign Key (qui n'est pas géré par la BDD de Wordpress) en créant un champ dans chaque table schema/video/... (foreign_xx) qui serait égal à l'id du bon produit. Cela est-il une méthode assez "propre" de procéder dans le contexte où je ne peux pas définir officiellement le champ comme étant une Foreign Key ? Est ce que cela peut amener des erreurs et si oui lesquels ?

Au niveau de la création des tables j'ai quelque chose comme ça :
$sql = "CREATE TABLE $table_produit (
		id_P bigint(20) unsigned NOT NULL AUTO_INCREMENT,
		nom_P text  NOT NULL,
		desc_P longtext,
		PRIMARY KEY  (id_P)
	) /* ... */ "
dbDelta($sql);

$sql = "CREATE TABLE $table_fiche_technique (
		id_FT bigint(20) unsigned NOT NULL AUTO_INCREMENT,
		nom_FT text NOT NULL,
		url_FT text NOT NULL,
		foreign_FT text NOT NULL,
		PRIMARY KEY  (id_FT)
	) /* ... */ "
dbDelta($sql);
Je compte simuler les clefs étrangères lors de mes requêtes avec une condition "WHERE foreign_xx = nom_P". Techniquement une clef étrangère renvoie vers une clef principale id_P mais nom_P est unique (il contient le nom du produit) et il ne devrait pas y avoir de soucis.

Cela peut-il engendrer des soucis sachant que le champ nom_P est unique mais pas la clé principale ? Qu'apporte en plus le fait qu'un champ ait l'attribut "Foreign Key" par rapport à si on le simule dans nos jonctions ?
Modifié par Ara (09 Jul 2015 - 15:44)
Lorsque l'on fait des clé étrangère en base de données, cela permet d'assurer une cohérence de la donnée (qui est obligatoire pour une base de donnée sinon ca serait vite le bazar..)

Dans le cas d'une clé étrangère, si elle est indiqué, cela rajoute une contrainte .Cela implique que ta bdd ne te laissera pas ajouter une fiche technique avec une clé étrangère qui n'existe pas dans la table produit.
Si tu ne precise pas une clé etrangere, dans l'absolu cela ne te change rien, mais cela sera a toi de faire gaffe que l'identifiant que tu as mis existe bien dans l'autre table ( que cela soit le nom du produit ou son id ca change pas grand chose, generalement on prefere prendre l'id)

Cela implique un certain nombre de précaution a prendre, aussi bien dans le cas d'un ajout de fiche technique, mais aussi dans le cas d'une modification/suppression de produit. Avec des contraintes de clé étrangère, il me semble qu'il est possible de faire des 'modifications en cascades' pour modifier/supprimer tous les éléments qui pointent sur l’élément modifié/supprimé.
Typiquement : Vu que tu veux prendre le nom ( je recommande pas trop ca moi hein ^^ ) tu choisi de changer le nom d'un produit-> hop tu perds tes liens vers ce produit car cela ne correspond plus, et la base de donnée ne sera pas capable de dire attention tu va perdre des liens en faisant ça car elle n'a pas connaissance de l'existence du lien entre les 2.
Dans le cas ou c'est un identifiant, ca arrive dans le cas ou on fait une renumerautation des ids des données (on a supprimé beaucoup de ligne, c'est moche les trous, hop on remet les ids qui se suivent et hop on a pas fait gaffe les autres ont pas changé on a perdu tous les liens vers les produits, ceux qui existent sont tous devenu faux)

Du coup si tu ne peux pas faire de clé étrangère, il faudra certainement que tu fasse des 'triggers' si je me souviens bien (en gros quand je vais lancé tel modif sur une table, le trigger va dire a attention il faut faire ces modifs sur les autres tables)


Edit : Pour les auto incremente j'aurai tendance à le laisser gérer ça tranquillou, à partir sur le principe du 'on s'en fou si il y a quelques trous dans les id', et une fois de temps à autre si tu fais pas mal de delete, tu feras une modif de tous les ids pour que ça suivent de nouveau. ( en faisant gaffe du coup pour les clés étrangères ^^)
Modifié par mathieu1004 (09 Jul 2015 - 16:42)
mathieu1004 a écrit :
Lorsque l'on fait des clé étrangère en base de données, cela permet d'assurer une cohérence de la donnée (qui est obligatoire pour une base de donnée sinon ca serait vite le bazar..)

Je pense que tu parles de clef primaire ici puisque certaines bases de données ne possèdent tout simplement pas de clef étrangère (comme MyISAM par exemple, potentielle raison pour laquelle Wordpress n'en possède pas).

L'intérêt de la clef étrangère est donc avant tout d'assurer une sécurité au niveau de la BDD lorsque l'on insert ou modifie une colonne. En parallèle à cette base de donnée je code actuellement un plugin permettant de faire des modifications à l'aide de formulaires (il est protégé d'éventuelles injections SQL et seul l'admin peut accéder à ce formulaire de toute manière) : tous les champs ne peuvent pas être modifiés (par exemple l'id, le nom de produit et la clef étrangère) ce qui assure que les liens entre les produits et leurs articles soient corrects.
Pour permettre à l'admin de modifier le nom du produit je pense remettre la clef étrangère sur l'ID qui ne devra pas bouger du coup.
Des sauvegardes régulières de la BDD seront demandées afin d'avoir un back-up, sachant toutefois que les données ne sont pas censées bouger beaucoup.

Je vais m'informer sur ces triggers histoire de voir si et comment je peux les utiliser : après l'initialisation de la BDD toutes les modifications doivent se passer depuis le formulaire sécurisé et si on doit utiliser un trigger c'est peut-être que j'ai mal fait mon boulot quelque part !
Modifié par Ara (10 Jul 2015 - 09:36)
Non non je parlais bien des clés étrangères qui permettent d'assurer de la cohérence dans tes données.
Exemple simple pour ton plugin :
Disons qu'il existe 50 produit avec des ids de 1 à 50.
Ton admin veut ajouter une fiche technique au dernier produit qu'il vient de crée grâce a ton plugin.
Ton plugin va présenter 3 champs a remplir pour une fiche technique : le nom , l'url , l'identifiant du produit.

Et bien si par exemple il se trompe et marque 60 au lieu de 50 , sans clé étrangère, il n'y aura aucune erreur de retourné. Cela va donc être a toi de mettre en place la vérification dans ton plugin afin de gérer si le numéro qu'il a mis est bien un numéro d'identifiant existant ou pas.
Si il y avait une clé étrangère, tu n'aurai qu'a gérer les retours d'erreur de ta base de données qui n'autoriserai pas l'ajout d'un élément avec une clé étrangère qui n'existe pas dans produit.

En gros : les clés étrangères ne sont pas une obligation, mais en étant bien gérées, elles permettent de gagner du temps pour certaines opérations (particulièrement ajout dans la table ou il y a la clé étrangère, modification/suppression dans la table qui est référencé par la clé étrangère)

Après il faut y faire attention, en étant mal géré ça peut faire des gros blocages.. exemple con :système de site fermé qui fonctionne par invitation: pour qu'un utilisateur puisse s'enregistrer il doit indiqué l'identifiant de l'utilisateur qui l'a invité -> logiquement je met une clé étrangère sur le champ id_parrain qui renvoie vers l'id d'un autre utilisateur, et qui doit donc être valide. Problème a l'utilisation .. je peux pas ajouter de 1er utilisateur car la clé étrangère m’empêche d'ajouter un utilisateur si je ne fourni pas un numéro d’identifiant qui existe.
Donc bon la c'est assez simple a voir, mais quand tu as des clé étrangères qui partent un peu dans tous les sens sur plusieurs tables, tu as vite fais de faire un truc qui semble super logique, mais qui a l'utilisation t’empêche de faire un ajout dans toutes les tables ^^. (ça se corrige assez simplement en désactivant la contrainte pour pouvoir faire le 1er ajout mais bon ^^)
Sinon pourquoi ne pas simplement utiliser ce qui est disponible de base dans WordPress ? avec les possibilitées de l'outil ? Ce que tu décris comme fonctionnement est réalisable sans même toucher à la
base de donnée.

Avantages :
- simple & user-friendly
- efficace
- pas de prise de tête avec SQL et des MLD
- parfaitement compatible avec wordpress
J'ai cherché des plugins permettant de gérer sa base de donnée mais je n'ai rien trouvé de convainquant et c'est pourquoi j'ai décidé de créer moi-même un plugin pour gérer la base de donnée via une liste de champs à remplir (user-friendly). D'après ce que tu dis je suis sans doute passé à côté d'un mécanisme de base permettant de le faire, est ce que tu peux m'en dire plus là dessus (ou un lien tout simplement) ?
Ça m'intéresserait de voir la méthode "classique" liée à Wordpress. Les données seraient stockées dans les tables natives de WP du coup ?

Vu la quantité de données (sur mon schéma cela représente 5-6 tables) je vois mal comment les stocker dans celles par défaut puisque aucune ne me semblait dédié à ce genre de chose.
Le tutoriel Openclassroom indique comment procéder en rajoutant un champ dans wp_options mais cela ne concerne que peu de données (l'url et le nom de mémoire). J'ai pensé qu'il serait plus "propre" de créer des tables directement reliées à mon plugin et voulais savoir comment les structurer au mieux, d'où mon sujet.
Dit moi si je me trompe, mais je crois comprendre que tu souhaites gérer des produits avec des caractéristiques techniques. Un manière simple de le faire est d'utiliser les custom post types. Codex WordPress CPT

Ca te permettra de créer dans l'admin un objet "produit". Pour ajouter les caractéristiques techniques, tu peux créer ce qui s'appelle des meta box et l'excellent ACF advancedcustomfields te permettra de faire ceci très facilement en te permettant d'ajouter des champs administrables à ce nouvel objet "produit".
De plus, en utilisant cette méthode, tu n'auras pas les problèmes d'éventuelle pagination et d'affichage dans WordPress que tu pourrais avoir avec ton plugin n'utilisant pas WordPress.
Tout est géré de manière transparente dans la bdd de WordPress.
Tu peux t'aider pour créer tes custom posts et autres loops avec cet outil : http://generatewp.com/
Modifié par edenpulse (12 Jul 2015 - 11:05)