8768 sujets

Développement web côté serveur, CMS

Bonjour,

Je m'explique:
d'une manière générale, dans PHP MySql lorsque l'on créé des enregistrement qui font intervenir plusieurs tables, comment procéder:
Imaginons une table avec des enregistrements 'voiture' et une table avec des enregistrements 'image'.
Une voiture peut être associée à plusieurs images donc dans la table image il y aura une clé étrangère qui pointe vers une clé primaire de la table voiture. Cette clé étrangère sera l'id de la voiture correspondante à ces images.
Si on créé d'abord la voiture et si on ajoute les images ensuite, dans un autre formulaire en éditant la voiture, ça va, on peut connaitre l'id de la voiture fraichement créée et on peut l'associer aux image que l'on rajoute.

Mais si on veut tout créer d'un coup, dans un seul formulaire?
- on crée la voiture
- on veut créer les images dans la foulées mais on a besoin de l'identifiant de la voiture
- on pourrait le récupérer avec des commandes du style lastInsertId()

de quelle manière l'obtenir avec certitude cet id, par exemple si plusieurs utilisateurs saisissent des voitures, comment être sur qu'un id que l'on récupérerait serait celui qui concerne vraiment la voiture sur laquelle on serait en train de vouloir rajouter des photos ?

C'est un cas de figure classique, existe t-il des modèles pour ce genre d'opération?
Modifié par lionel_css3 (03 Feb 2018 - 16:39)
lionel_css3 a écrit :
Bonjour,
Je m'explique:
d'une manière générale, dans PHP MySql lorsque l'on créé des enregistrement qui font intervenir plusieurs tables, comment procéder:
Imaginons une table avec des enregistrements 'voiture' et une table avec des enregistrements 'image'.
Une voiture peut être associée à plusieurs images donc dans la table image il y aura une clé étrangère qui pointe vers une clé primaire de la table voiture. Cette clé étrangère sera l'id de la voiture correspondante à ces images.
Si on créé d'abord la voiture et si on ajoute les images ensuite, dans un autre formulaire en éditant la voiture, ça va, on peut connaitre l'id de la voiture fraichement créée et on peut l'associer aux image que l'on rajoute.
Mais si on veut tout créer d'un coup, dans un seul formulaire?
- on crée la voiture
- on veut créer les images dans la foulées mais on a besoin de l'identifiant de la voiture
- on pourrait le récupérer avec des commandes du style lastInsertId()
de quelle manière l'obtenir avec certitude cet id, par exemple si plusieurs utilisateurs saisissent des voitures, comment être sur qu'un id que l'on récupérerait serait celui qui concerne vraiment la voiture sur laquelle on serait en train de vouloir rajouter des photos ?
C'est un cas de figure classique, existe t-il des modèles pour ce genre d'opération?

Bonjour,
Perso, cela fait belle lurette que je n'utilise plus les ID auto-incrémentés dans mes développements SGBD.
A la place, j'identifie chaque enregistrement via un GUID en guise de clé primaire. Ceci me permet de connaître mon identifiant très en amont du processus, pour chaque partie de la transaction.
Dès lors, les formulaires de saisie arrivent pré remplis et les dépendances sont résolues. Le champ correspondant à la clé primaire est visible de l'utilisateur ou non, selon les besoins définis pour l'application.
La seule chose dont on doive s'assurer est que l'enregistrement de référence (la voiture dans ton cas) a bien été stocké en base (principe de la transaction : tout ou rien), sinon tu auras de images orphelines.
sepecat a écrit :

Bonjour,
Perso, cela fait belle lurette que je n'utilise plus les ID auto-incrémentés dans mes développements SGBD.
A la place, j'identifie chaque enregistrement via un GUID en guise de clé primaire. Ceci me permet de connaître mon identifiant très en amont du processus, pour chaque partie de la transaction.
Dès lors, les formulaires de saisie arrivent pré remplis et les dépendances sont résolues. Le champ correspondant à la clé primaire est visible de l'utilisateur ou non, selon les besoins définis pour l'application.
La seule chose dont on doive s'assurer est que l'enregistrement de référence (la voiture dans ton cas) a bien été stocké en base (principe de la transaction : tout ou rien), sinon tu auras de images orphelines.


Je suis désolé mais je n'ai absolument rien compris à ta réponse Smiley decu
lionel_css3 a écrit :
Je suis désolé mais je n'ai absolument rien compris à ta réponse Smiley decu

Pas de problème...
Nous sommes d'accord que chaque enregistrement est caractérisé par une clé primaire unique pour l'ensemble de la table. Cette clé primaire est ensuite utilisée par les enregistrements dépendants comme clé étrangère pour "lier" les enregistrements en question (l'image dans ton exemple) avec l'enregistrement de référence (la voiture).
De façon classique, on laisse le soin au SGBD (MySQL, Oracle, etc.) d'attribuer automatiquement la valeur de la clé primaire lors de l'enregistrement en base. Ceci fonctionne bien mais présente, en effet, l'inconvénient de ne connaître ladite valeur de clé qu'a posteriori, comme tu le soulignes.
Pour ma part, je trouve deux inconvénients à ce système :
a) son caractère différé
b) le fait que si un jour je dois fusionner deux bases de structure identique (et cela arrive en milieu professionnel), je risque un conflit de clés puisque deux enregistrements pourront se retrouver avec une valeur de clé primaire identique
A contrario, j'utilise ce qu'on appelle un GUID (Globally Unique ID) qui est bien connu dans les principaux langages (ex. UUID sous Java). Il s'agit d'une séquence de caractères comme celle-ci : 3A84523D-7E2B-4B47-A33F-001A5AB8EDE2, générée automatiquement par le système à partir de différents éléments (heure, carte réseau, etc.) et dont la probabilité pour tomber sur deux valeurs identiques est suffisamment proche du zéro pour être négligée.
Ainsi, si je crée deux voitures A avec une PK valant 02E38FF2-7D72-44C5-BA85-AE7DF1C4EE32 et B avec une PK valant 02E38FF2-7D72-44C5-BA85-AE7DF1C4EE32, j'ai deux enregistrements parfaitement distincts, y compris si je les réimporte dans une base contenant déjà des voitures.
A partir de là, lorsque j'ai besoin de faire saisir une voiture par l'utilisateur, le formulaire délivré par la page HTML est déjà pré-renseigné avec le GUID de cette voiture, lequel sera ensuite repris depuis le POST du formulaire et passé comme valeur de clé primaire lors de l'insertion en base de données.
Un GUID dans sa forme alphanumérique occupe 36 caractères mais peut aussi être stocké sous une forme BigDecimal (en Java) ce qui réduit l'occupation en base. C'est un peu plus que si tu utilises une valeur auto incrémentée (généralement de type entier), mais vu les avantages je préfère, et de loin, cette approche.

EDIT : Je ne connais pas PHP en pratique, mais sauf erreur il semble qu'il existe une instruction dans ce langage permettant de créer des GUID (com_create_guid).
Modifié par sepecat (03 Feb 2018 - 18:26)
Ah d'accord, je comprends mieux maintenant, je ne connaissais pas cette façon de procéder et effectivement dans ce cas on crée sa propre clé avant et effectivement dans ce cas ça semble imparable.
Il faut donc ne pas choisir l'incrémentation auto de la clé primaire dans la table ça semble aller de soi.
Je vais creuser le truc, je te remercie pour ton aide Smiley smile
lionel_css3 a écrit :
Ah d'accord, je comprends mieux maintenant, je ne connaissais pas cette façon de procéder et effectivement dans ce cas on crée sa propre clé avant et effectivement dans ce cas ça semble imparable.
Il faut donc ne pas choisir l'incrémentation auto de la clé primaire dans la table ça semble aller de soi.
Je vais creuser le truc, je te remercie pour ton aide Smiley smile

En effet, l'auto-incrémentation est une possibilité offerte par le SGBD, très largement utilisée, mais ce n'est pas une obligation en soi et tu peux tout à fait déclarer un champ de type chaîne alphanumérique renseigné à ta guise.
Bien entendu, il t'appartient alors d'avoir la certitude que les valeurs que tu y stockeras seront uniques.
Un GUID répond de façon intrinsèque à ce besoin.
Par contre on ne peut plus utiliser l'index pour faire une requête triée sur l'index décroissant afin de lister les derniers enregistrements en 1er, il faudra rajouter un camp date....
Modérateur
a écrit :

de quelle manière l'obtenir avec certitude cet id, par exemple si plusieurs utilisateurs saisissent des voitures, comment être sur qu'un id que l'on récupérerait serait celui qui concerne vraiment la voiture sur laquelle on serait en train de vouloir rajouter des photos ?


Si la solution de Sepecat fonctionne, avec les auto-increments aussi:

lastInsertId() s'appuie sur LAST_INSERT_ID de mysql, qui garde un index par connexion
Autrement dit il te renverra toujours le dernier ID créé dans cette session (peu importe ce que font d'autres utilisateurs dans d'autres sessions).
kustolovic a écrit :

lastInsertId() s'appuie sur LAST_INSERT_ID de mysql, qui garde un index par connexion
Autrement dit il te renverra toujours le dernier ID créé dans cette session (peu importe ce que font d'autres utilisateurs dans d'autres sessions).


mais en principe le dernier index disponible par auto-incrementation est le même pour tout le monde? je comprends pas
Modérateur
Oui, mais le système mémorise les ID insérés par connexion.

En cas de requête «imbriquée» par plusieurs utilisateurs:

- Tu insères un élément voiture qui reçoit l'ID 12
- Un autre utilisateur insère un élément voiture qui reçoit l'ID 13
- Tu demandes le lastInsertId qui te retourne 12, car c'est le dernier de TA connexion.
- L'autre utilisateur demandes le lastInsertId qui lui retourne 13

a écrit :
For LAST_INSERT_ID(), the most recently generated ID is maintained in the server on a per-connection basis. It is not changed by another client. It is not even changed if you update another AUTO_INCREMENT column with a nonmagic value (that is, a value that is not NULL and not 0). Using LAST_INSERT_ID() and AUTO_INCREMENT columns simultaneously from multiple clients is perfectly valid. Each client will receive the last inserted ID for the last statement that client executed.

Modifié par kustolovic (05 Feb 2018 - 15:30)
Meilleure solution
Ah merci pour la précision je ne connaissais pas ce détail et en fait ça semble logique que cela fonctionne ainsi Smiley smile
Salut,

Perso j'insiste sur la notion de transaction que Sepecat a mentionné. C'est un concept assez important et je dirais même fondamental. J'ai pas pris la peine de te chercher un lien pour te documenter, mais tu devrais pas avoir de peine à trouver ça.
Anymah a écrit :
Salut,

Perso j'insiste sur la notion de transaction que Sepecat a mentionné. C'est un concept assez important et je dirais même fondamental. J'ai pas pris la peine de te chercher un lien pour te documenter, mais tu devrais pas avoir de peine à trouver ça.


J'ai essayé une fois et je n'ai pas réussi à faire marcher.
La plupart des exemples que j'avais trouvés étaient dans la console MySql alors que moi je veux exécuter un script Php.
Modérateur
Les transactions, les locks et toussa, c'est effectivement très utile et indispensable pour certaines applications.

Cependant ça n'apporte rien pour régler le problème précis évoqué ici, et c'est très certainement bien trop lourd pour certaines applications où les risques d'accès concurrents sont très faibles et les conséquences en cas de problèmes aussi.