[RESOLU] Est-ce que ce modèle de base de donnée est correct ?

Eléphant du PHP | 61 Messages

16 déc. 2020, 13:14

Hello tout le monde,

Je voudrais créer un site qui rassemble tous les lieux les plus chouettes à découvrir sur notre petite planète :-)

voici une image du dit projet: https://drive.google.com/file/d/1sgz1na ... sp=sharing

Je voudrais créer 3 filtres.

Le premier « thématique » changerait la thématique du sujet.
Ici ce sont des « voyages » mais ca pourrait être autre chose (les meilleurs vins, fromages, etc.).

Le deuxième lié à la thématique du premier serait « pays »

Le troisième serait « sorte » (volcan, desert,…).

Sur base des filtres (ou non) le site renvoie une série de vignettes de résultats.
Chaque vignette à un nom (lieu), un drapeau, nom d'un pays et d'un lieu précis.

Je réfléchis à comment créer la base de données en SQL pour ca.

J'arrive à un truc du genre.
Selon vous est-ce correcte ?
Est-ce que ca a un intérêt de split éventuellement les tables ?

https://drive.google.com/file/d/19UfdwK ... p=sharing

Merci de votre feedback !!!!

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

16 déc. 2020, 19:04

Salutations !

Si un "lieu" ne sera forcément situé que dans un seul pays, n'est-il pas possible qu'il puisse être associé à plusieurs thèmes ou sortes ? Si la réponse est non, alors une seule table est effectivement suffisante pour consigner toutes les informations d'un lieu. En revanche si un lieu propose à la fois du vin et du fromage, il faut pouvoir y accéder en choisissant l'un ou l'autre de ces thème et il faut donc associer ton lieu à 0 ou N thèmes.

Pour cela, une seconde table est préférable dans laquelle tu pourras spécifier l'id du lieu et le thème. Le lien se fait par une jointure entre les deux tables. Par exemple, une table "lieu_theme" contenant uniquement l'id du lieu et le libellé du thème associé. Ainsi, si le lieu "Coteaux du Lyonnais" a pour ID 7 dans ta table principale, tu pourrais avoir dans cette table un enregistrement "7 ; Fromages" et un enregistrement "7 ; Vins"

Idéalement tu pourrais aussi avoir une table "theme" (ID, THEME) qui contiendrait tes thèmes : "1 ; Voyage", "2 ; Fromages", "3 ; Vins". Tu pourrais ainsi facilement créer de nouveaux thèmes en les ajoutant dans cette table. Et la table "lieu_theme" (ID_LIEU, ID_THEME) ne comporterait que les id des deux tables. Dans le cas des Coteaux, il y aurait ainsi deux enregistrements : " 7 ; 2 " et " 7 ; 3 ".

Là encore une jointure entre les 3 tables permet de recouper toutes les informations.

Même principe si ton lieu peut être associé à différentes sortes : est-ce qu'une rivière souterraine (si tant est que tu es ce type de lieu) rentrerait dans la catégorie rivière ou caverne ou les deux ? :)

L'autre gros avantage de placer tes thèmes dans une autre table, c'est que tu peux facilement les lister pour les proposer comme filtre.

Tu peux également créer une table pour consigner les différents pays (et les proposer facilement en recherche). La différence, c'est qu'un lieu n'est associé qu'à un seul pays, il n'y a donc pas besoin de table lieu_pays. Il suffit simplement que dans la table des lieux, il y ait l'id du pays auquel il est associé plutôt que son libellé :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 61 Messages

16 déc. 2020, 19:19

Ouawww un TOUT GRAND merci pour ta réponse ! :D

En fait, chaque thème sera indépendant donc j'aurais pas ce soucis.
Jusqu'à ce que... :|

Donc tu me conseillerais de séparer les tables en autant de filtres qui existent ? (THEME, PAYS, SORTBY)

J'ai une autre question.
Je sais pas si je dois faire un nouveau poste ou pas mais bon... c'est lié à ce projet :

Pour le moment j'arrive à ceci:



Code:

Code : Tout sélectionner

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="style.css" /> <title>Visit Earth</title> </head> <body> <div id="bloc_page"> <header> <p><img src="img/logo.png" alt="logo" id="logo"></p> <h1>Découvrez les meilleures choses à voir et à faire dans les Monde. </h1><br /> </header> <div id="filters"> <div class="filters">Filtres </div> <form> <select name="lieux"> <option value="lieux" selected>Lieux à découvrir</option> <option value="autre">Autre</option> </select> <select name="pays"> <option value="afghanistan" selected>afghanistan</option> <option value="alabama">alabama</option> <option value="australie">Australie</option> <option value="belgique">Belgique</option> </select> <select name="tri"> <option value="montagne" selected>Montagnes</option> <option value="volcan">Volcans</option> <option value="desert">Déserts</option> <option value="plage">Plages</option> </select> </form> </div> <br> <br /> **** ENDROITS À DÉCOUVRIR <br><br /> <div class="vignette"> <?php try { // On se connecte à MySQL $bdd = new PDO('mysql:host=localhost;dbname=visitearth;charset=utf8', 'root', 'root'); } catch(Exception $e) { // En cas d'erreur, on affiche un message et on arrête tout die('Erreur : '.$e->getMessage()); } // Si tout va bien, on peut continuer // On récupère tout le contenu de la table Lieux $reponse = $bdd->query('SELECT Flag, Lieu, Pays, Ville FROM Lieux WHERE ID=1'); // On affiche chaque entrée une à une while ($donnees = $reponse->fetch()) { ?> <p> <?php echo $donnees['Flag']; ?><br /> <?php echo $donnees['Lieu']; ?><br /> <?php echo $donnees['Pays']; ?><br /> <?php echo $donnees['Ville']; ?><br /> </p> <?php } $reponse->closeCursor(); // Termine le traitement de la requête ?> </div> </body> </html>
Visuellement ca donne ca : https://drive.google.com/file/d/1HtW95U ... sp=sharing

Mais je ne sais pas comment dire à l'ordinateur que je veux créer des vignettes.
Que chaque vignette possède :
- un "flag" (une image via une url)
- Un nom de Lieu
- Un nom de Pays
- un nom de Ville

J'ai réussi à créer une vignette mais je ne voit pas comment faire la suite.
J'imagine que je dois créer une boucle.
Dans la boucle ? :roll:
Et lui dire de stopper à 16 et si l'utilisateur veut en afficher 16 supplémentaire il cliquera sur un bouton.

PS: je me doute bien que les filtres ne sont pas bons non plus. je vais justement faire appel à mes tables comme tu le suggères ! :D

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

16 déc. 2020, 19:55

Je pense effectivement que c'est mieux de les séparer dès maintenant... c'est un peu plus long à mettre en place, mais c'est à mon avis bénéfique pour la suite. Si tu veux ajouter un thème, pas besoin de toucher au code, de recharger le fichier sur le serveur etc. un simple ajout en base et hop, ton theme est dispo :)

Au final tu n'aurais que 6 tables :
- Lieux (ID, Flag, Lieu, ID_Pays, Ville), ta table principale avec l'ID du pays plutôt que son libellé
- Pays (ID_Pays, Libelle), liste des pays
- Themes (ID_Theme, Libelle), liste des themes
- Tris (ID_Tri, Libelle), liste des "tris" (à voir si tu n'as pas un nom plus explicite pour désigner ce qu'elle contient ?)
- Lieux_Theme (ID_Lieu, ID_Theme), lien entre les lieux et les thèmes (même s'il n'y en a qu'un au début)
- Lieux_Tris (ID_Lieu, ID_Tri), lien entre les lieux et les tris

Dans ton code, les "select" pourraient être alimenter dynamiquement :
<select name="pays">
<?php
$requete = 'SELECT id_pays, libelle FROM pays ORDER by libelle';
$reponse = $bdd->query($requete);
while ($donnees = $reponse->fetch()) {
  echo '<option value="' . $donnees['id_pays'] . '">' . $donnees['libelle'] . '</option>';
}
?>
</select>
...
Sinon pour ton second problème, c'est à cause du "WHERE ID=1" dans ta requête. Tu as bien la boucle et tout ce qui faut pour afficher les résultats, mais tu ne récupère qu'un seul enregistrement de la base. Enlève le WHERE et remplace le par "LIMIT 0, 16". Tu pourras ainsi afficher les 16 premiers enregistrements

En intégrant des variables php dans ta requête tu pourras ajouter le WHERE pour filtrer les résultats avec les filtres et changer le "0" du LIMIT en 1, 2, 3... pour indiquer la page à afficher (LIMIT 0, 16 affiche les 16 premiers enregistrements, LIMIT 1, 16 affiche les 16 suivants, etc.)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 61 Messages

17 déc. 2020, 10:35

Super @Ryle !
J'ai compris le point.
Merci pour le SELECT automatique ca va rendre le code plus propre.
Je vais essayer d'implémenter tout ça :)
Encore un GRAND merci !

Eléphant du PHP | 61 Messages

17 déc. 2020, 11:32

@Ryle, quand j'essaie de "simplifier" le code du SELECT avec la "caténation" ca ne fonctionne pas.
Je comprends pas l'erreur.
La logique est un peu différente de ce que tu as fait mais c'est parce que je vois les besoins business derrières ;)

Code : Tout sélectionner

<div class="vignette"> <?php try { // On se connecte à MySQL $bdd = new PDO('mysql:host=localhost;dbname=visitearth;charset=utf8', 'root', 'root'); } catch(Exception $e) { // En cas d'erreur, on affiche un message et on arrête tout die('Erreur : '.$e->getMessage()); } // Si tout va bien, on peut continuer // On récupère tout le contenu de la table Lieux <select name="contenu_vignette"> <?php $reponse = $bdd->query('SELECT flag, pays, lieu_id FROM pays, lieux'); // On affiche chaque entrée une à une while ($donnees = $reponse->fetch()) { echo '<option value="' . $donnees['flag'] . '">' . $donnees['pays'] . '">' . $donnees['lieu_id '] . '</option>' } ?> </select> </div>
Et les images de Php My Admin:

https://drive.google.com/file/d/10qMHe7 ... sp=sharing
https://drive.google.com/file/d/12sbrDk ... sp=sharing
https://drive.google.com/file/d/1BURdLp ... sp=sharing
https://drive.google.com/file/d/1BlO-rx ... sp=sharing
https://drive.google.com/file/d/1LljzFu ... sp=sharing

Mammouth du PHP | 2703 Messages

17 déc. 2020, 15:25

<select name="pays">
<?php
$requete = 'SELECT id_pays, libelle FROM pays ORDER by libelle';
$reponse = $bdd->query($requete);
while ($donnees = $reponse->fetch()) {
echo '<option value="' . $donnees['id_pays'] . '">' . $donnees['libelle'] . '</option>';
}
?>
</select>

remplace
<select name="pays">
<option value="afghanistan" selected>afghanistan</option>
<option value="alabama">alabama</option>
<option value="australie">Australie</option>
<option value="belgique">Belgique</option>
</select>

après, faire une requête qui retournera toujours la même chose, c'est discutable d'un point de vue consommation de ressources du serveur.

pas
<p>
<?php echo $donnees['Flag']; ?><br />
<?php echo $donnees['Lieu']; ?><br />
<?php echo $donnees['Pays']; ?><br />
<?php echo $donnees['Ville']; ?><br />
</p>

Eléphant du PHP | 61 Messages

17 déc. 2020, 15:55

@Or1, c'est ce que j'avais fait mais apparemment c'est mieux de faire la boucle pour la performance.
J'aimerais résoudre mon problème dans la caténation.
Merci.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

17 déc. 2020, 20:43

Alors pour les performance, afficher un code en html en dur, sera toujours plus performant que générer ce même code avec du php (une étape en moins), mais aussi beaucoup plus contraignant, parce que pour pouvoir le modifier ou le faire évoluer, il faut avoir accès au serveur et connaître un peu le code.

Mais je suis d'accord avec toi or1, la liste des pays évolue assez peu et pourrait être en dur. Avantage de les avoir en base, c'est que le filtre peut ne proposer que les pays pour lesquels il y a un résultat (et ainsi avoir une liste de pays dynamique quand même ;))

@jeremygoldyn : je t'avoue que je suis un perdu avec les copies d'écran de tes tables, j'ai l'impression qu'il te manque quelques éléments.
Dans ta table lieux, la colonne pays devrait être remplacée par une colonne pays_id qui serait de type int. Celle-ci indiquer l'identifiant du pays que l'on va retrouver dans la table pays.
Dans ta table pays, tu aurais alors une colonne pays_id (même nom et même type que dans l'autre table, elle contient le même type d'information) et "nom" contiendra le libellé du pays.

Au niveau de la jointure tu aurais alors une requête de ce type :
SELECT lieux.lieu_id, lieux.nom, lieux.pays_id, pays.pays_id, pays.nom FROM lieux LEFT JOIN pays ON lieux.pays_id = pays.pays_id
Ou plus simplement / proprement :
SELECT l.lieu_id, l.nom, l.pays_id, p.pays_id, p.nom FROM lieux l LEFT JOIN pays p ON l.pays_id = p.pays_id
- Concrètement, on va chercher dans la table "lieux" (à laquelle on donne un alias L pour simplifier l'écriture) les champs "lieu_id", "nom" (du lieu) et "pays_id" et dans la table "pays" (alias p), le "pays_id" et "nom" (du pays).
- Dans la mesure où des champs ont le même nom dans différentes tables, il est important de toujours préfixer celui-ci par le nom de la table (ou l'alias) sinon MySQL ne saura pas auquel tu fais référence et te diras que tu as un nom de colonne ambiguë dans ta requête.
- Le fait de préciser la condition de jointure, permet d'associer le bon pays à chaque lieu (on ne retourne que les lieux et les pays pour lesquels pays_id est identique)

Au niveau de la requête, vu que le pays_id doit être identique, il est inutile de le récupérer deux fois. Il est également préférable de donner des alias différents aux colonnes qui ont le même nom histoire de s'y retrouver :
SELECT l.lieu_id, l.nom AS nom_lieu, l.pays_id, p.nom AS nom_pays FROM lieux l LEFT JOIN pays p ON l.pays_id = p.pays_id
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 61 Messages

17 déc. 2020, 22:05

Salut Ryle, Or

Bon les gars c'est mon tout premier code de ma vie à part les TP de OpenClassroom :lol:
Un grand merci à vous de votre aide.
J'essaie de comprendre :)

Je suis pour la version dynamique.
Je sais pas de quoi on parle en terme de performance mais bon je suppose que c'est pas le jour et la nuit non plus.

@Ryle,

Je ne veux pas mettre de pays_id car tous les nom des pays dans le monde sont distincts.
Du coup pas besoin d'un "id" en plus.

Ok pour la nomenclature simplifiée mais je vais essayer déjà en "normal" :lol:

Tous les noms sont différents yes.

POUR LE MOMENT je suis bloquer dans ma boucle.
Je vais d'abord essayer de résoudre ca.

DONC, est-ce que pour toi, tu vois un problème dans le code ici:

Code : Tout sélectionner

<div class="vignette"> <?php try { $bdd = new PDO('mysql:host=localhost;dbname=visitearth;charset=utf8', 'root', 'root'); } catch(Exception $e) { die('Erreur : '.$e->getMessage()); } <select name="contenu_vignette"> <?php $reponse = $bdd->query('SELECT flag, pays, lieu_id FROM pays, lieux'); while ($donnees = $reponse->fetch()) { echo '<option value="' . $donnees['flag'] . '">' . $donnees['pays'] . '">' . $donnees['lieu_id '] . '</option>' } ?> </select> </div> </body> </html>
Merci.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

18 déc. 2020, 12:07

Effectivement, le nom du pays est unique et pourrait servir de clé, mais je pense quand même que c'est une bonne habitude à prendre quand on travaille avec une base de données. Il y a quand même quelques avantages, même s'ils ne sont pour le moment pas évident à discerner ou ne s'appliquent pas dans l'immédiat à ton projet :)

Pour te donner un exemple simple, au lieu d'avoir dans ta table lieux :
Yosemite Park ; USA
Yellowstone Park ; USA
Grand Canyon ; USA
Il est préférable d'avoir
Yosemite Park ; 8
Yellowstone Park ; 8
Grand Canyon ; 8
Et une seconde table avec
8 ; USA
Tu peux ainsi plus facilement changer le libellé (le jour où tu préfères que le pays s'appelle Etats-Unis) puisqu'il suffit de le changer à un seul endroit pour que la modification soit répercutée partout. Tu peux aussi facilement ajouter une colonne à ta seconde table pour avoir le libellé dans d'autre langue, ajouter le drapeau du pays, etc.

Ça n'est pas des plus important pour un projet perso, mais je pense néanmoins que ça reste une bonne habitude à prendre ;)


Pour en revenir à ton code, si tu as une table pays qui contient les noms des pays, c'est celle-ci qu'il faut interroger pour alimenter ton filtre :
<?php
$reponse = $bdd->query('SELECT nom FROM pays ORDER BY nom');
while ($donnees = $reponse->fetch()) {
  echo '<option value="' . $donnees['nom'] . '">' . $donnees['nom'] . '</option>';
}
?>
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 61 Messages

18 déc. 2020, 16:23

Hello @Ryle,

Je comprends bien le premier point.
Mais ici je ferais sans car comme tu dis c'est juste un projet perso et ok dans ce cas-ci.

J'ai réussi à avoir un résultat avec le code "simplifié" (la boucle) :D

Bon je dois revoir la BD mais si tu regardes l'image suivante : https://drive.google.com/file/d/1UZ06GB ... sp=sharing
Ca explique VRAIMENT mon problème que j'explique par rapport au CSS.

Ici j'ai créé 2 entrées dans sql: Belgique, France.
Top, il va chercher les infos dans sql et les affichent dans ma div.
MAIS comment je fais dans le code pour dire que je veux 16 cases (vignettes) sur la page et que France ait dans la case numéro 2 .

Merci de me répondre à ca.
Le reste je peux gérer.

Ca serait vraiment sympa !!!
MERCI !!!

Mammouth du PHP | 2703 Messages

18 déc. 2020, 16:35

MAIS comment je fais dans le code pour dire que je veux 16 cases (vignettes) sur la page
la réponse a déjà été donnée, dans cette discussion.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

18 déc. 2020, 18:07

Il y a deux choses à distinguer.

Au niveau de ta requête, tu peux utiliser LIMIT pour ne récupérer que 16 lieux à la fois et jouer sur la pagination comme indiquée plus haut. Sauf bien sûr que s'il n'y a que 2 lieux en base, même si tu mets une limite à 16 résultat, il ne pourra pas t'en remonter plus que ce qu'il trouve ;)

Ensuite il y a la partie affiche de ces 2 ou 16 enregistrement.

Le plus simple c'est de commencer avec le code html/css que tu veux obtenir pour une seule vignette. Par exemple :
<div style="float: left; width:25%">
  <p>Nom du lieu</p>
  <img src="illustration.png" alt="" />
</div>
Tu remplace ensuite les éléments dynamique avec du php
<div style="float: left; width:25%">
  <p><?php echo $donnees['Lieu']; ?></p>
  <img src="<?php echo $donnees['Flag']; ?>" alt="" />
</div>
Enfin, tu place le tout dans ta boucle
<?php while ($donnees = $reponse->fetch()) { ?>
<div style="float: left; width:25%">
  <p><?php echo $donnees['Lieu']; ?></p>
  <img src="<?php echo $donnees['Flag']; ?>" alt="" />
</div>
<?php } ?>
Et normalement, la magie devrait opérer :) (bon avec 2 lieux seulement au début, mais c'est justement qu'un début ;)) et tu pourras ajuster en css, en fonction du nombre de vignette que tu veux par ligne, leur taille...
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 61 Messages

19 déc. 2020, 18:48

Super merci beaucoup @Ryle !!!!
Je vais travailler à tout ca et je reviens quand j'ai fini pour te montrer :)

@Or, sorry mais c'est pas constructif de dire que ca a été dit.
Je suis un DEBUTANT.
Envoyer des morceaux de code sans explication c'est pas très utile.
Mais c'est super sympa d'essayer de m'aider quand même :)

Allais hop j'y vais, j'ai du taf sur la planche ! :)