BDD : duplication d'enregistrement(s) dans une table

Résolu/Fermé
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 - 15 juil. 2022 à 13:52
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 - 16 juil. 2022 à 09:05

Bonjour à tous,

J'aimerai ajouter dans ma table 'compositions' un enregistrement qui serait en partie la recopie d'un autre.

En effet, un ou plusieurs contrats individuels (présents dans la table contrats) seraient neutralisés (passage d'un état 1 à 2) pour être ensuite centralisés, tout en conservant le détail initial (présents dans la table compositions) sur le compte d'un client centralisateur.

Voici mon code (dont la requête 4 de recopie ne fonctionne pas, les autres sont bonnes) que je poste en forum PHP plutôt qu'en forum BDD car j'aimerai que quelqu'un me dise aussi ce qu'il pense des mes boucles dans d'autres boucles !

if (isset($_POST['coche']) AND isset($_POST['client'])) {
	// Connexion à la base de données :
	require ("connexion.php");	

	// Recherche du dernier n° de facture dans la table "contrats" :
	// (si aucune facture n'est en bdd, on créé la facture n° 500)
	$requete1 = $bdd->query('SELECT IFNULL(MAX(contrats.facture),499) AS facture FROM contrats');
	$data = $requete1->fetch();
	$numero = $data['facture'] + 1;

	// On récupère le client centralisateur : 
	$centralisateur = $_POST['client'];

	// Récupération de la date du jour :
	$aujourdhui = date('Y/m/d');	

	// On créé une entête pour le client centralisateur :
	$requete2 = $bdd->prepare('INSERT INTO contrats(date_contrat, etat, facture, date_facture, id_client, id_batiment, date_debut, heure_debut, date_fin, heure_fin, adultes, enfants, bebes, animaux) VALUES(:date_contrat, :etat, :facture, :date_facture, :id_client, :id_batiment, :date_debut, :heure_debut, :date_fin, :heure_fin, :adultes, :enfants, :bebes, :animaux)');
	        
	$requete2->execute(array(
		'date_contrat' =>$aujourdhui,
	    'etat' =>3,
	    'facture' =>$numero,
	    'date_facture' =>$aujourdhui,
	    'id_client' =>$centralisateur,
	    'id_batiment' =>5,
	    'date_debut' =>NULL,
	    'heure_debut' =>NULL,
	    'date_fin' =>NULL,
	    'heure_fin' =>NULL,
	    'adultes' =>0,
	    'enfants' =>0,
	    'bebes' =>0,
	    'animaux' =>0
    ));
	// Récupération du dernier ID inséré dans la table contrats :
    $last_insert_id = $bdd->lastInsertId();

	// On récupère les numéros de contrats individuels choisis pour être facturés centralisés
	// Et on remplace leur état 1 par 2 :
	foreach ($_POST['coche'] as $value):
	    if (!empty($value)):
	        $requete3 = $bdd->prepare('UPDATE contrats SET etat = 2 WHERE id_contrat = :contrat');
	        $requete3->execute(array('contrat' => $value));

	        // On recopie les contenus des contrats des clients individuels sur le client centralisateur :
			$requete4 = $bdd->prepare('
				INSERT INTO compositions (id_contrat, id_prestation, descriptif, prix, quantites)
				SELECT id_contrat, id_prestation, descriptif, prix, quantites
				FROM compositions
				WHERE id_contrat = :contrat');
			$requete4->execute(array(
				'id_contrat' => $last_insert_id,
				'contrat' => $value));	     
		endif;
	endforeach;

	// Clôture des requètes : 
	$requete1->closeCursor();
	$requete2->closeCursor();
	$requete3->closeCursor();
	$requete4->closeCursor();
}

Je vous remercie d'avance pour votre aide sur ma requête 4 et sur vos conseils quant à la structure de mon code !

6 réponses

emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
16 juil. 2022 à 09:05

Après une bonne nuit de sommeil, quelques lignes de codes et boucles en plus, voici mon code qui fonctionne : 

if (isset($_POST['coche']) AND isset($_POST['client'])) {
	// Connexion à la base de données :
	require ("connexion.php");	

	// Recherche du dernier n° de facture dans la table "contrats" :
	// (si aucune facture n'est en bdd, on créé la facture n° 500)
	$requete1 = $bdd->query('SELECT IFNULL(MAX(contrats.facture),499) AS facture FROM contrats');
	$data = $requete1->fetch();
	$numero = $data['facture'] + 1;

	// On récupère le client centralisateur : 
	$centralisateur = $_POST['client'];

	// Récupération de la date du jour :
	$aujourdhui = date('Y/m/d');	

	// On créé une entête pour le client centralisateur :
	$requete2 = $bdd->prepare('INSERT INTO contrats(date_contrat, etat, facture, date_facture, id_client, id_batiment, date_debut, heure_debut, date_fin, heure_fin, adultes, enfants, bebes, animaux) VALUES(:date_contrat, :etat, :facture, :date_facture, :id_client, :id_batiment, :date_debut, :heure_debut, :date_fin, :heure_fin, :adultes, :enfants, :bebes, :animaux)');
	        
	$requete2->execute(array(
		'date_contrat' =>$aujourdhui,
	    'etat' =>3,
	    'facture' =>$numero,
	    'date_facture' =>$aujourdhui,
	    'id_client' =>$centralisateur,
	    'id_batiment' =>5,
	    'date_debut' =>$aujourdhui,
	    'heure_debut' =>NULL,
	    'date_fin' =>$aujourdhui,
	    'heure_fin' =>NULL,
	    'adultes' =>0,
	    'enfants' =>0,
	    'bebes' =>0,
	    'animaux' =>0
    ));
	// Récupération du dernier ID que l'on vient d'insérer dans la table contrats :
    $last_insert_id = $bdd->lastInsertId();

	// Récupération des numéros de contrats à centraliser pour être facturés
	// Et on remplace leur état 1 validé par 2 centralisé :
	foreach ($_POST['coche'] as $value):
	    if (!empty($value)):
	        $requete3 = $bdd->prepare('UPDATE contrats SET etat = 2 WHERE id_contrat = :contrat');
	        $requete3->execute(array('contrat' => $value));
	    endif;
	endforeach;

	foreach ($_POST['coche'] as $value):
		// Récupération des contenus des contrats individuels cochés :
		$requete4 = $bdd->prepare('
			SELECT *
			FROM compositions
			WHERE compositions.id_contrat = :contrat
			ORDER BY compositions.id_composition ASC');

		$requete4->execute(array('contrat' => $value));

		// Insertion dans la bdd des compositions en recopie des contrats cochés :
		while ($data_composition = $requete4->fetch()) {
			$requete5 = $bdd->prepare('INSERT INTO compositions(id_contrat, id_prestation, descriptif, prix, quantites) VALUES(:id_contrat, :id_prestation, :descriptif, :prix, :quantites)');
	        
	        $requete5->execute(array(
	        	'id_contrat' => $last_insert_id,
	            'id_prestation' => $data_composition['id_prestation'],
	            'descriptif' => $data_composition['descriptif'],
	            'prix' => $data_composition['prix'],
	            'quantites' => $data_composition['quantites']
	        ));
	    }
	endforeach;
	
	// Clôture des requètes : 
	$requete1->closeCursor();
	$requete2->closeCursor();
	$requete3->closeCursor();
	$requete4->closeCursor();
	$requete5->closeCursor();

	// Envoi à la page contrats : 
	header('Location: contrats.php');

Merci à vous deux pour vous être penché sur mon problème et pour avoir pris le temps de me lire !

1
jee pee Messages postés 39650 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 26 avril 2024 9 238
Modifié le 15 juil. 2022 à 14:16

Bonjour,

Quel est le message d'erreur ? Si tu n'en as pas c'est peut être que tu ne les gères pas dans ton source, c'est toujours une mauvaise idée de faire l'impasse sur cela.

Après si on regarde le sql

			INSERT INTO compositions (id_contrat, id_prestation, descriptif, prix, quantites)
				SELECT id_contrat, id_prestation, descriptif, prix, quantites
				FROM compositions
				WHERE id_contrat = :contrat');

si  id_contrat et/ou id_prestation sont des clés primaires de la table tu vas avoir une error duplicate, doublon.


0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
Modifié le 15 juil. 2022 à 14:39

Bonjour JeePee,

Oui tu as raison, j'ai oublié un copié/collé : 

Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/html/gite/gestion/facturation.php:83 Stack trace: #0 /var/www/html/gite/gestion/facturation.php(83): PDOStatement->execute() #1 {main} thrown in /var/www/html/gite/gestion/facturation.php on line 83

Par ailleurs, dans la table composition, id_contrat et id_prestation sont des clés étrangères... composition a sa propre id_composition, d'ailleurs, elle n’apparaît pas dans mon INSERT INTO

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
15 juil. 2022 à 14:53

Je pense que mon execute n'est pas bon, il ne reprend pas tous les champs de la table composition présents dans le prepare... Mais c'est là que je ne sais pas faire, je ne veux pas d'un INSERT traditionnel, je voudrais une copie des données de  'contrat' => $value

0
jordane45 Messages postés 38145 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 25 avril 2024 4 650
15 juil. 2022 à 18:42

Bonjour

Dans ron execute tu transmets deux variables.. alors que ta requête SQL n'en n' attend qu'une..

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > jordane45 Messages postés 38145 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 25 avril 2024
Modifié le 15 juil. 2022 à 18:52

Je viens d'écrire ça qui fonctionne pour l'entete et qui malheureusement vient de m'insérer plus de 1000 lignes dans compositions... J'ai du merdouiller dans les boucles !

if (isset($_POST['coche']) AND isset($_POST['client'])) {
	// Connexion à la base de données :
	require ("connexion.php");	

	// Recherche du dernier n° de facture dans la table "contrats" :
	// (si aucune facture n'est en bdd, on créé la facture n° 500)
	$requete1 = $bdd->query('SELECT IFNULL(MAX(contrats.facture),499) AS facture FROM contrats');
	$data = $requete1->fetch();
	$numero = $data['facture'] + 1;

	// On récupère le client centralisateur : 
	$centralisateur = $_POST['client'];

	// Récupération de la date du jour :
	$aujourdhui = date('Y/m/d');	

	// On créé une entête pour le client centralisateur :
	$requete2 = $bdd->prepare('INSERT INTO contrats(date_contrat, etat, facture, date_facture, id_client, id_batiment, date_debut, heure_debut, date_fin, heure_fin, adultes, enfants, bebes, animaux) VALUES(:date_contrat, :etat, :facture, :date_facture, :id_client, :id_batiment, :date_debut, :heure_debut, :date_fin, :heure_fin, :adultes, :enfants, :bebes, :animaux)');
	        
	$requete2->execute(array(
		'date_contrat' =>$aujourdhui,
	    'etat' =>3,
	    'facture' =>$numero,
	    'date_facture' =>$aujourdhui,
	    'id_client' =>$centralisateur,
	    'id_batiment' =>5,
	    'date_debut' =>NULL,
	    'heure_debut' =>NULL,
	    'date_fin' =>NULL,
	    'heure_fin' =>NULL,
	    'adultes' =>0,
	    'enfants' =>0,
	    'bebes' =>0,
	    'animaux' =>0
    ));
	// Récupération du dernier ID inséré dans la table contrats :
    $last_insert_id = $bdd->lastInsertId();

	// On récupère les numéros de contrats choisis pour être facturés
	// Et on remplace leur état 1 par 2 :
	foreach ($_POST['coche'] as $value):
	    if (!empty($value)):
	        $requete3 = $bdd->prepare('UPDATE contrats SET etat = 2 WHERE id_contrat = :contrat');
	        $requete3->execute(array('contrat' => $value));
	    endif;
	endforeach;

	// On recopie les contenus des contrats des clients individuels sur le client centralisateur :
	$requete4 = $bdd->prepare('
		SELECT *
		FROM compositions
		WHERE compositions.id_contrat = :contrat
		ORDER BY compositions.id_composition ASC');

	$requete4->execute(array('contrat' => $value));

	while ($data_composition = $requete4->fetch()) {
		$requete5 = $bdd->prepare('INSERT INTO compositions(id_contrat, id_prestation, descriptif, prix, quantites) VALUES(:id_contrat, :id_prestation, :descriptif, :prix, :quantites)');
        $requete5->execute(array(
        	'id_contrat' => $last_insert_id,
            'id_prestation' => $data_composition['id_prestation'],
            'descriptif' => $data_composition['descriptif'],
            'prix' => $data_composition['prix'],
            'quantites' => $data_composition['prix']
        ));
    }





	// Clôture des requètes : 
	$requete1->closeCursor();
	$requete2->closeCursor();
	$requete3->closeCursor();
	$requete4->closeCursor();
	$requete5->closeCursor();
}
0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024
15 juil. 2022 à 18:57

Quel couillon, j'ai écrit ligne 64 : 

'quantites' => $data_composition['prix'] au lieu de 
'quantites' => $data_composition['quantité']
0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024
15 juil. 2022 à 18:58

Quelle nouille, j'ai écrit 

'quantites' => $data_composition['prix'] au lieu de 
'quantites' => $data_composition['quantites']
0
jee pee Messages postés 39650 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 26 avril 2024 9 238
Modifié le 15 juil. 2022 à 16:06

La ligne 83 dans le source original, c'est bien la requete 4 ?

Id_composition est bien en auto_increment ?

L'erreur indique qu'il n'y a pas le bon nombre de valeurs. Tu devrait tester le sql pur dans la console mysql en mettant un n° de contrat à la place de la variable.

Si tu veux initialiser plus de champs, il faut les mettre dans la liste et dans le select. Mais en dupliquant tout, comment vas tu rattacher à un client centralisateur. C'est lors de l'insert qu'il faudrait le faire, après tu aura des lignes dupliquées.


0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
Modifié le 15 juil. 2022 à 17:01

La ligne 83 correspond bien à 'contrat' => $value));  // La fin de l'execute de ma requête 4... Les données transmises (id_contrat + contrat qui répond à WHERE id_contrat = :contrat) ne sont pas suffisantes...

L'id_composition actuel est 895, il est bien en auto increment

Les clients ne sont pas rattachés à un centralisateur, ce sont les contrats dont l'entête (table contrats) et la composition (table compositions) qui sont dupliqués et sur lesquel j'impose l'id_client du centralisateur... Je souhaite conserver la consultation des contrats des clients avant centralisation mais avec impossibilité de les modifier (etat :1 => etat :2)

En fait, je suis venu chercher ici de l'aide pour la syntaxe qui me permettrait de recopier des données se trouvant dans la table dans laquelle je veux justement insérer des données sans passer par des affectations de valeurs à des variables...

J'ai essayé de m'inspirer de ça https://www.developpez.net/forums/d954452/bases-donnees/mysql/debuter/dupliquer-enregistrement/ mais je n'y arrive pas

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
15 juil. 2022 à 16:59

Les requêtes 1, 2 et 3 fonctionnent parfaitement... Quand je vais dans PhpMyAdmin j'ai bien les contrats centralisés dont l'état est passé de 1 à 2 et dans la table contrats j'ai bien une entête créée à l'id_client du centralisateur.

Il faut maintenant que je finalise la requête 4 en allant chercher dans la table compositions et dans une boucle les id_contrats centralisés, et que je les recopie dans cette même table en leur affectant $last_insert_id comme id_contrat

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024
15 juil. 2022 à 17:31

J'oubliais un truc important : il faut obligatoirement que le traitement soit dans une boucle car un contrat peut avoir plusieurs prestations (plusieurs lignes dans compositions)

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
Modifié le 15 juil. 2022 à 17:11

Les contrats rattachés au centralisateur : 

Le centralisateur dans la table contrats :  

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024
15 juil. 2022 à 17:13

Le petit bonhomme vert c'est pour signaler un centralisateur, l'erreur dans la colonne TOTAL c'est parce que la requête 4 déconne !

0
jee pee Messages postés 39650 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 26 avril 2024 9 238
15 juil. 2022 à 18:09

Pour l'exemple dont tu t'es inspiré, tu n'as pas le soucis indiqué puisque tu as un id auto_increment.

Testes la requête sql directe avec le n° de contrat qu'il y a sur le cas "bonhomme vert".

Mais je ne comprends toujours cas comment dans composition, où tout est identique, tu va distinguer les lignes du client original des lignes du client centralisateur.


0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20
15 juil. 2022 à 18:35

Dans compositions, la clé étrangère id_contrat lie l'enregistrement avec l'id_contrat clé primaire de la table contrats qui possède son propre id_client. Le contrat 500 est ainsi lié au client 42 par exemple. Si le client 42 est dépendant d'un centralisateur, il est important que son contrat puisse encore être consultable sur la fiche client. Idem pour le client 54 qui a un contrat 527.

Du coup, le centralisateur 17 des clients 42 et 54 possède son propre contrat 685, avec son propre id_contrat 685 et sa composition est la recopie des contrats 500 et 527. 

C'est important de conserver les id_contrats 500 et 527 pour pouvoir les consulter dans les fiches des clients 42 et 54, mais il faut aussi consulter et imprimer la facture du contrat 685 sur la fiche du client 17 !

0
jee pee Messages postés 39650 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 26 avril 2024 9 238 > emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024
15 juil. 2022 à 19:08

Mais en passant par le centralisateyr, tu ne peux pas trouver les lignes de ses contrat puisque le N0 de contrat est le meme sur 2 enregistrements de composition.

0
emrh Messages postés 427 Date d'inscription mardi 9 décembre 2014 Statut Membre Dernière intervention 9 avril 2024 20 > jee pee Messages postés 39650 Date d'inscription mercredi 2 mai 2007 Statut Modérateur Dernière intervention 26 avril 2024
15 juil. 2022 à 19:21

J'y arrive avec mon code posté en #14 (correction en #16) mais j'ai dû mal construire ma boucle, je me retrouve avec le contrat du centralisateur composé de 4 x les éléments du clients pris en exemple..

0