Problèmes jointures PHP et SQL

nicosjack
Invité n'ayant pas de compte PHPfrance

06 mars 2014, 18:45

Bonjour,
Je suis entrain de développer une petite application web en PHP/SQL pour une boulangerie, permettant aux utilisateurs de réserver des petits pains pour le lendemain.
L'idée c'est que le client puisse voir la liste de tous les petits pains proposés, et puisse choisir pour chacun, la quantité désirée.
De l'autre côté, la boulangerie doit pouvoir afficher quels petits pains ont été commandés par quel client et dans quel quantité.
Pour cela j'ai créé une base de données dont voici la structure :
http://nicolasschiff.com/image/diagram.png

Ma question, c'est comment insérer une commande (et ses détails) en PHP dans les différentes tables de la base de données ? C'est à dire, savoir que le client N passe une commande N contenant les produits X, Y, Z avec leur quantité respective. Je ne sais pas comment faire techniquement pour lier les données ensemble et je ne sais pas comment écrire la requête...
Comme je débute en PHP, j'ai fais beaucoup d'essais avant de poster ce message, sans succès. J'ai tout de même une première requête qui à l'air de fonctionner, mais qui ne suffit pas à elle seule :
//On ajoute la commande dans la table commandes
$requete = $base->prepare('INSERT INTO commandes (date_commande, date_livraison, clients_id_clients) VALUES(NOW(), :date_livraison, :id_client)');
$requete->execute(array(
	'date_livraison' => $date_livraison,
	'id_client' => $_SESSION['id']
));

//On ajoute les détails de la commande dans la table detailscommande
//Et là je bloque...
Merci d'avance pour votre précieuse aide,

Nicolas

Mammouth du PHP | 504 Messages

07 mars 2014, 01:11

Slt,

En débutant en php, ça ne va pas etre si simple.

Déjà 2 solutions concernant les clients qui commandent: soit ils sont "one shot" (c'est a dire qu'il remplisse un formulaire clients et ils commandent), soit ce sont des clients "inscrits" donc loggué ?? là on ne sait pas vu que tu nous le dit pas.

Partons du principe qu'ils sont loggués, donc leur identifiant unique est id_client.

1/ il faut que tu utilises les sessions et donc les variables de sessions qui vont te permettre de stockées le "panier" de tes clients et leur id_unique.

2/ Ton client acccéde au formulaire de commande de produit tu as stocké son id unique dans ta session, tu stockes tes produits et le nombre de produits commandés dans une variable de sessions par produit.

3/ quand ton client valide la commande, tu envois tout dans ta base.

Donc on reprend:

Log du client (id_unique) -> page de commande produit (stockage en variables de sessions) -> page de récapitulatif de commande et validation finale (ou modif) (envoi des données dans ta base)

Regardes les tutos variable de sessions http://www.lephpfacile.com/cours/18-les-sessions

et crée un panier, c'est ce qu'il te faut: http://www.grafikart.fr/tutoriels/php/p ... ession-309

Bonne soirée :wink:

nicosjack
Invité n'ayant pas de compte PHPfrance

08 mars 2014, 13:56

Merci beaucoup pour ta réponse !

Alors effectivement les clients sont logués avec un identifiant. Par contre je ne pense pas avoir besoin de créer de panier, ni d'utiliser les sessions. Lorsque le client arrive sur la page principale, il a tout simplement un tableau avec la liste de tous les produits, et pour chacun il peut choisir la quantité avec un <input> de type number. Ensuite il clique sur le bouton "commander" qui a pour effet de valider le formulaire HTML et de tout stocker dans la base.

Mais c'est justement là mon problème, je ne sais pas comment construire la requête (avec PHP et SQL) pour ajouter les données dans la base, c'est à dire, pour qu'on ai l'id du client, de la commande, les produits et leur quantité, dans les différentes tables comme dessinées sur mon schéma.

Si j'arrive à faire ça, je devrais pouvoir finaliser toute mon application. Enfaite je dis que je suis débutant PHP parce que ça fait un an que j'en ai plus fait, mais j'ai quand même acquis de bonnes bases. Malheureusement au niveau des jointures et de mon problème ici, je suis un peu dépassé...

Eléphant du PHP | 243 Messages

08 mars 2014, 16:28

Bonjour,

Si l'utilisateur est logué, ce sera plus simple à réaliser.

Tout d'abord, ton système de session est-il au point ? L'utilisateur a-t-il un ID lorsqu'il est connecté ?
Si c'est le cas, dans ta page de commande, il suffira juste de placer tes inputs, d'insérer un bouton submit.
Côté php, il suffira de récupérer les valeurs envoyées, d'effectuer une connexion à ta table puis de les insérer.

Si tu as besoin de plus de renseignements, je reste dans le coin :)
"Nos études ont montré que la probabilité qu’un programme corrigé fonctionne comme avant la correction est seulement de cinquante pour cent"
~~Lorenzo Strigini

nicosjack
Invité n'ayant pas de compte PHPfrance

09 mars 2014, 11:05

Oui le système de session est au point, l'utilisateur a un ID en session lorsqu'il est connecté.
Sur ma page de commandes aucun problème, mon formulaire est en place avec mes inputs et bouton de validation.
La connexion a la base est bonne aussi, comme dit la seule chose que je n'arrive pas à faire, c'est construire la requête pour insérer les données dans la base.

J'arrive à remplir ma table "commandes" en mettant l'id du client, la date de commande et la date de livraison, mais comment remplir la table "detailscommande" en y mettant l'id de la commande (qui vient donc d'être généré par la première requete), l'id du client, chaque produit et sa quantité respective... ?

Merci d'avance

Mammouth du PHP | 571 Messages

09 mars 2014, 16:56

J'arrive à remplir ma table "commandes" en mettant l'id du client, la date de commande et la date de livraison, mais comment remplir la table "detailscommande" en y mettant l'id de la commande (qui vient donc d'être généré par la première requete), l'id du client, chaque produit et sa quantité respective... ?
On récupère le dernier id inséré avec la fonction lastInsertId() de PDO.

//On ajoute la commande dans la table commandes
$requete = $base->prepare('INSERT INTO commandes (date_commande, date_livraison, clients_id_clients) VALUES(NOW(), :date_livraison, :id_client)');
$requete->execute(array(
        'date_livraison' => $date_livraison,
        'id_client' => $_SESSION['id']
));

//id de la commande insérée
$requete ->lastInsertId(); 

Quant à insérer la quantité et l'id de chaque produit il te faut gérer un panier virtuel(comme on te l'a déjà dit plus haut). Un panier se base sur les sessions et il y a pléthore de tutos qui t'expliquent le principe.

Enfin pour éviter qu'une commande se retrouve accidentellement dans la BD sans les détails de la commande tu peux embarquer tes 2 requêtes d'insertion dans une transaction mysql.transaction

nicosjack
Invité n'ayant pas de compte PHPfrance

11 mars 2014, 10:57

Merci encore, j'ai effectivement suivi un très bon tutoriel qui m'a permis de réaliser correctement un panier (avec les sessions) et tout fonctionne bien :)
Maintenant malheureusement, le tutoriel s'arrête juste avant d'insérer les données dans la base et donc je bloque toujours au même endroit... Le formateur explique brièvement que l'on peut utiliser la fonction serialize() de PHP mais j'ai toujours du mal à comprendre comment insérer les données et faire en sorte qu'elles soient liées... :?

Mammouth du PHP | 504 Messages

11 mars 2014, 12:32

Slt,

Félicitations :wink: .

Quel tuto, tu as utilisé ? parce que sans le code, on va avoir du mal a te suivre. Donne le code de la page de confirmation de ton panier là ou arrive le recap de ta commande pour validation finale du client.

nicosjack
Invité n'ayant pas de compte PHPfrance

11 mars 2014, 15:17

Merci pour ta réponse, voici le tuto que j'ai suivi et appliqué en entier : http://www.grafikart.fr/tutoriels/php/p ... ession-309

Et voici le code de mon fichier panier.php qui affiche le récapitulatif de la commande :
<section id="main">
		<a href="index.php" title="Retour à la page d'accueil">Retour</a>
		<?php 
			$ids = array_keys($_SESSION['panier']);
		
			//On récupère tous les produits ajoutés au panier
			if(empty($ids)) {
				$produits = array();
			} else {
				$produits = $DB->query('SELECT * FROM produits WHERE id_produit IN ('.implode(',', $ids).')');
			}
		?>
		<form method="post" action="panier.php">
			<table>
				<thead>
					<tr>
						<th>Nom</th>
						<th>Prix</th>
						<th>Quantité</th>
						<th>Sous-total</th>
						<th>Actions</th>
					</tr>
				</thead>
				<tbody>
					<?php foreach($produits as $produit): ?>
					<tr>
						<td><?php echo $produit->nom_produit; ?></td>
						<td><?php echo number_format($produit->prix_unitaire, 2, ',', ' '); ?> €</td>
						<td><input class="spinner" type="number" name="panier[quantite][<?php echo $produit->id_produit; ?>]" value="<?php echo $_SESSION['panier'][$produit->id_produit]; ?>" /></td>
						<td><?php echo number_format($produit->prix_unitaire * $_SESSION['panier'][$produit->id_produit], 2, ',', ' '); ?> €</td>
						<td><a href="panier.php?delpanier=<?php echo $produit->id_produit; ?>" title="Supprimer le produit">Supprimer</a></td>
					</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
			<input type="submit" value="Recalculer" />
		</form>
		<strong>NOMBRE DE PRODUITS : <?php echo $panier->compter(); ?></strong>
		<strong>TOTAL : <?php echo number_format($panier->total(), 2, ',', ' '); ?> €</strong>
		<button>Valider la commande</button>
	</section>
Et voici le code du fichier panier.class.php qui est appelé dans le header de la page :
<?php

class panier {
	
	private $DB;
	
	//Constructeur
	public function __construct($DB) {
		
		//S'il n'existe aucune session, on l'a créer
		if(!isset($_SESSION)) {
			session_start();
		}
		
		//Si aucun panier n'existe, on le créer
		if(!isset($_SESSION['panier'])) {
			$_SESSION['panier'] = array();
		}
		$this->DB = $DB;
		
		if(isset($_GET['delpanier'])) {
			$this->del($_GET['delpanier']);
		}
		
		if(isset($_POST['panier']['quantite'])) {
			$this->recalc();	
		}
	}

	//Fonction permettant de recalculer le panier quand on change la quantité
	public function recalc() {
		foreach($_SESSION['panier'] as $product_id => $quantite) {
			if(isset($_POST['panier']['quantite'][$product_id])) {
				if(filter_var($_POST['panier']['quantite'][$product_id], FILTER_VALIDATE_INT) && $_POST['panier']['quantite'][$product_id] > 0) {
					$_SESSION['panier'][$product_id] = $_POST['panier']['quantite'][$product_id];
				}
				else {
					echo 'Attention, la quantité renseignée doit être un chiffre entier positif.';	
				}
			}
		}
	}
	
	//Fonction permettant de compter combien il y a de produits dans le panier
	public function compter() {
		return array_sum($_SESSION['panier']);
	}
	
	//Fonction permettant de calculer le prix total du panier
	public function total() {
		
		$total = 0;
		$ids = array_keys($_SESSION['panier']);
		
		//On récupère tous les produits ajoutés au panier
		if(empty($ids)) {
			$produits = array();
		} else {
			$produits = $this->DB->query('SELECT id_produit, prix_unitaire FROM produits WHERE id_produit IN ('.implode(',', $ids).')');
		}
		
		foreach($produits as $produit) {
			$total += $produit->prix_unitaire * $_SESSION['panier'][$produit->id_produit];	
		}
		return $total;
	}
	
	//Fonction permettant d'ajouter un produit au panier
	public function add($product_id) {
		
		//Si le panier contient déjà ce produit
		if(isset($_SESSION['panier'][$product_id])) {
			//On en ajoute un
			$_SESSION['panier'][$product_id]++;
		} else {
			//Sinon on en créer un
			$_SESSION['panier'][$product_id] = 1;	
		}
	}
	
	public function del($product_id) {
		unset($_SESSION['panier'][$product_id]);	
	}
}

?>

nicosjack
Invité n'ayant pas de compte PHPfrance

13 mars 2014, 11:39

Plus personne pour me répondre ?

Mammouth du PHP | 504 Messages

13 mars 2014, 13:01

Hello,

Il te suffit par exemple de créer une page de confirmation de commande (Votre commande a été prise en compte).

Cette page recupere les données posté par la page récapitulatif de la commande (les données en session) et cette page contient une requete d'insertion classique dans ta base. (mysql_query insert.....).

Je ne sais pas comment est faite ta base, et ce que tu recuperes exactement en sessions.

Pour avancer, creer cette page de validation, fait un echo de tes variables en sessions ( ce qui est validé par ton formulaire). à partir de cet echo, fait une requete classique d'insertion dans tes tables. Il se peut que tu ai plusieurs requetes puisque plusieurs tables.

Ne te préoccupe pas de la serialisation des données pour l'instant. (c'est un peu de l'optimisation et ça marche trés bien sans.)

Je reste dans le coin.

nicosjack
Invité n'ayant pas de compte PHPfrance

13 mars 2014, 15:28

Merci beaucoup pour ton aide :)

Voilà ce que contient ma variable de SESSION en faisant un var_dump($_SESSION) :
array
  'panier' => 
    array
      8 => string '3' (length=1)
      12 => string '6' (length=1)
      15 => string '2' (length=1)
  'id' => string '1' (length=1)
  'societe' => string 'ma-societe' (length=14)
8, 12 et 15 correspondent aux ID des produits ajoutés au panier.
3, 6, 2 correspondent à leur quantité respective.
'id' correspond à l'ID du client qui passe commande et 'société' correspond à son nom (pour afficher un message de bienvenue sur l'accueil)

Donc si je comprend bien, je vais plusieurs requêtes simples pour insérer mes données dans les tables ? Donc je n'ai pas de INNER JOIN, LEFT JOIN etc à utiliser ?

Mammouth du PHP | 571 Messages

13 mars 2014, 16:37

dans ta table commandedetails les champs commande_id_commande et produit_id_produit ne doivent pas être des clés primaires.cette table peut être dépourvue de clé primaire.
try {  

$base = new PDO( 'mysql:host=localhost;dbname=bbd;charset=utf8', 'user', 'password');
  $base->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Démarre explicitement une transaction
  $base->beginTransaction();

 //On ajoute la commande dans la table commandes
$requete = $base->prepare('INSERT INTO commandes (date_commande, date_livraison, clients_id_clients) VALUES(NOW(), :date_livraison, :id_client)');
$requete->execute(array(
        'date_livraison' => $date_livraison,
        'id_client' => $_SESSION['id']
));

//recuperation de l'id de la commande insérée
$commande_id = $requete ->lastInsertId(); 

//Insertion dans la table detailscommande
$req2 = $base->prepare("INSERT INTO detailscommande VALUES (:commande_id, :produit_id, :quantite )");

foreach($_SESSION['panier'] as $produit_id=> $quantite ){
 $req2->execute(array(
         ':commande_id' => $commande_id,
        ':produit_id' =>  $produit_id,
	':quantite' => $quantite,
	));

}
  $base->commit();//on valide la transaction=> insertion dans la table commande et detailscommande

}catch (PDOException $e) {
   // Si l'une des requête a echouée alors
      //on  annule les changements de toutes les requêtes faisant partie
      // de la transaction, même celles qui se sont bien déroulées.
  $dbh->rollBack();
   printf("erreur sql liée  à l'insertion jour: %s:", $e->getMessage());
}



nicosjack
Invité n'ayant pas de compte PHPfrance

14 mars 2014, 17:05

Merci beaucoup j'ai bien avancé grâce à ta réponse :D

Par contre j'ai encore un problème, j'obtiens l'erreur suivante :
Fatal error: Call to undefined method PDOStatement::lastInsertId() in C:\wamp\www\commande\panier.php on line 31
Voici la ligne 31 : $commande_id = $requete->lastInsertId();
Et voici le code complet :
				//On ajoute la commande dans la table commandes
				$requete = $DB->insert('INSERT INTO commandes (date_commande, date_livraison, clients_id_clients) VALUES(NOW(), :date_livraison, :id_client)', array(
				'date_livraison' => $date_livraison,
				'id_client' => $_SESSION['id']));
				
			
				//recuperation de l'id de la commande insérée
				$commande_id = $requete->lastInsertId();
Voici également le contenu de ma classe DB :
class DB {
	
	private	$host = 'localhost';
	private $username = 'root';
	private $password = '';
	private $database = 'schwoob';
	private $db;
	
	public function __construct($host = null, $username = null, $password = null, $database = null) {
		if($host != null) {
			$this->host = $host;
			$this->username = $username;
			$this->password = $password;
			$this->database = $database;
		}
		
		//Connexion à la BDD
		try {
			$this->db = new PDO('mysql:host='.$this->host.';dbname='.$this->database, $this->username, $this->password, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8', PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
		} catch(PDOException $e) {
			die('<h1>Impossible de se connecter à la base de données</h1>');
		}
	}
	
	//Fonction query permettant d'executer facilement des requêtes préparées en fonction de paramètres
	public function query($sql, $data = array()) {
		$requete = $this->db->prepare($sql);
		$requete->execute($data);
		return $requete->fetchAll(PDO::FETCH_OBJ);
	}
	public function insert($sql, $data = array()) {
		$requete = $this->db->prepare($sql);
		$requete->execute($data);
		return $requete;
	}
}

Mammouth du PHP | 571 Messages

14 mars 2014, 19:17

Fatal error: Call to undefined method PDOStatement::lastInsertId() in C:\wamp\www\commande\panier.php on line 31
il y a une coquille qui s'est glissée. en effet lastInsertId() appartient à PDO et non à PDOStatement.Pour récupérer le dernier id inséré on va modifier la méthode insert de la classe DB de telle sorte qu'elle renvoie l'id qui vient d'être inséré.

Pour l'insertion multiple dans la table detailscommande, on va ajouter une méthode dans DB qui permet juste de préparer une requête pour pouvoir par la suite boucler que sur le tableau des paramètres de la requête pour l'exécuter.
<?php

class DB {
       
        private $host = 'localhost';
        private $username = 'root';
        private $password = '';
        private $database = 'schwoob';
        private $db;
        
       
        public function __construct($host = null, $username = null, $password = null, $database = null) {
                if($host != null) {
                        $this->host = $host;
                        $this->username = $username;
                        $this->password = $password;
                        $this->database = $database;
                }
               
                //Connexion à la BDD
                try {
                        $this->db = new PDO('mysql:host='.$this->host.';dbname='.$this->database, $this->username, $this->password, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8', PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
                } catch(PDOException $e) {
                        die('<h1>Impossible de se connecter à la base de données</h1>');
                }
        }
       
        //Fonction query permettant d'executer facilement des requêtes préparées en fonction de paramètres
        public function query($sql, $data = array()) {
                $requete = $this->db->prepare($sql);
                $requete->execute($data);
                return $requete->fetchAll(PDO::FETCH_OBJ);
        }

	/*
	@return 0 or primary key
	*/
        public function insert($sql, $data = array()) {
                $requete = $this->db->prepare($sql);
                $reponse = $requete->execute($data);
                return (empty($reponse) ) ? 0 : $this->db->lastInsertId(); 
        }

	 public function insertMultiple( $sql){
	 $requete = $this->db->prepare($sql);
	return $requete;
	}

	

	
}


et dans le panier.php

//require_once 'DB.php';
//$DB = new DB();


  //On ajoute la commande dans la table commandes
 $requete = $DB->insert('INSERT INTO commandes (date_commande, date_livraison, clients_id_clients) 
			VALUES(NOW(), :date_livraison, :id_client)', 
			array(
                                ':date_livraison' => $date_livraison,
                                ':id_client' => $_SESSION['id'])
			     );
//la méthode $DB->insert() renvoie le dernier id de la commande inserée 
// sinon elle renvoie 0 en cas d'échec d'insertion dans la table commande
if(  $requete  !==0 )     //l'id de la commande inserée est-il different de 0?                                    
 //recuperation de l'id de la commande insérée
 $commande_id = $requete;
else
echo "Impossible d'inserer dans la table commande";


 //Insertion dans la table detailscommande
$prepare = $DB->insertMultiple("INSERT INTO detailscommande VALUES (:commande_id, :produit_id, :quantite )" );
foreach($_SESSION['panier'] as $produit_id=> $quantite ){
 $prepare->execute(array(
         ':commande_id' => $commande_id,
        ':produit_id' =>  $produit_id,
        ':quantite' => $quantite,
        ));

}//foreach