0% ont trouvé ce document utile (0 vote)
32 vues21 pages

05 Orm

Ce document présente l'étape 5 d'un TP sur l'e-commerce utilisant Laravel 12, axé sur l'ORM, les modèles et les migrations. Il couvre la création et la configuration de la base de données, la gestion des modèles Eloquent, l'importation de données, ainsi que l'implémentation de fonctionnalités CRUD pour les produits. Des missions pratiques sont proposées pour tester et valider le fonctionnement de l'application.

Transféré par

imracraft.yt
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
32 vues21 pages

05 Orm

Ce document présente l'étape 5 d'un TP sur l'e-commerce utilisant Laravel 12, axé sur l'ORM, les modèles et les migrations. Il couvre la création et la configuration de la base de données, la gestion des modèles Eloquent, l'importation de données, ainsi que l'implémentation de fonctionnalités CRUD pour les produits. Des missions pratiques sont proposées pour tester et valider le fonctionnement de l'application.

Transféré par

imracraft.yt
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

TP “E-commerce” - Étape 5

ORM, modèles et migrations avec Laravel 12

Objectifs pédagogiques
Prérequis
Création et configuration de la base de données
Introduction aux migrations dans Laravel 12
Configuration du modèle Product
Modification du contrôleur ProductController
Mission 1: Importation des données existantes
Mission 2: Vérification du fonctionnement de l’application
Mission 3: Création du formulaire d’édition de produit
Mission 4: Ajout de la fonctionnalité de suppression
Annexe : Guide Eloquent ORM
Conclusion

TP “E-commerce” - Étape 5

ORM, modèles et migrations avec Laravel 12

Objectifs pédagogiques
Comprendre le fonctionnement des modèles Eloquent dans Laravel 12
Apprendre à créer et exécuter des migrations
Manipuler des enregistrements dans la base de données via un ORM
Gérer l’upload de fichiers de manière sécurisée
Implémenter un CRUD complet pour les produits

Prérequis
Avoir terminé les étapes 1 à 4 du TP “E-commerce”

Auteur: Steeve LEFORT <[email protected]> page 1 sur 21


Disposer d’un environnement de développement fonctionnel avec Laravel 12
Comprendre les bases du framework Laravel (routes, vues, contrôleurs)

Création et configuration de la base de données


1. Comprendre les différentes options de base de données

Laravel 12 supporte plusieurs systèmes de base de données :

MySQL 8.0+ (recommandé pour la production)


PostgreSQL 14.0+
SQLite 3.8.8+ (parfait pour le développement)
SQL Server 2017+

Pour ce TP, nous utiliserons SQLite par défaut pour sa simplicité, mais vous pouvez utiliser MySQL si
vous préférez.

2. Configuration avec SQLite (recommandé)

1. Assurez-vous que votre fichier .env est configuré correctement :

DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

2. Créez un fichier de base de données vide :

touch database/database.sqlite

3. Configuration avec MySQL (optionnel)

Si vous préférez utiliser MySQL :

1. Modifiez votre fichier .env :

Auteur: Steeve LEFORT <[email protected]> page 2 sur 21


DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_ecommerce
DB_USERNAME=root # À adapter selon votre configuration
DB_PASSWORD= # À adapter selon votre configuration

2. Créez la base de données dans MySQL via Laragon ou phpMyAdmin.

Introduction aux migrations dans Laravel 12


Les migrations dans Laravel sont comme un système de versioning pour votre base de données. Elles
permettent de :

Définir la structure de votre base de données en PHP


Partager cette structure avec votre équipe
Faciliter le déploiement sur différents environnements
Garder un historique des modifications

1. Création du modèle Product avec sa migration

Créons un modèle Product avec sa migration associée :

php artisan make:model Product -m

Cette commande génère :

Un modèle : app/Models/Product.php
Une migration :
database/migrations/XXXX_XX_XX_XXXXXX_create_products_table.php

2. Modification de la migration pour la table products

Ouvrez le fichier de migration créé et modifiez la méthode up() :

public function up(): void


{
Schema::create('products', function (Blueprint $table) {

Auteur: Steeve LEFORT <[email protected]> page 3 sur 21


$table->id(); // Identifiant unique et auto-
incrémenté
$table->string('name'); // Nom du produit
$table->string('image')->nullable(); // Chemin de l'image, peut être
null
$table->decimal('price', 10, 2); // Prix avec 10 chiffres dont 2
décimales
$table->integer('vat'); // Taux de TVA (%)
$table->text('description'); // Description du produit
$table->timestamps(); // Colonnes created_at et
updated_at
});
}

3. Exécution de la migration

Lancez la migration pour créer la table dans la base de données :

php artisan migrate

Si vous souhaitez voir l’état des migrations :

php artisan migrate:status

Configuration du modèle Product


Dans Laravel, un modèle représente une table de la base de données. Il permet d’interagir avec cette table
sans écrire de SQL.

1. Modification du modèle

Ouvrez app/Models/Product.php et ajoutez l’attribut $fillable pour définir les champs qui
peuvent être assignés en masse :

<?php

namespace App\Models;

Auteur: Steeve LEFORT <[email protected]> page 4 sur 21


use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model


{
use HasFactory;

/**
* Les attributs qui sont assignables en masse.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'price',
'vat',
'description',
'image'
];
}

2. Comprendre l’assignation de masse et la protection contre les vulnérabilités

L’attribut $fillable est important pour la sécurité :

Il spécifie quels champs peuvent être remplis via create() ou update()


Il protège contre les attaques de type “mass assignment”
Sans lui, un attaquant pourrait injecter des champs non prévus dans vos formulaires

Note importante : Une attaque par “mass assignment” se produit lorsqu’un utilisateur malveillant
ajoute des paramètres supplémentaires dans une requête HTTP. Par exemple, si votre modèle a un
champ ’isadmin’ qui n’est pas protégé et qu’un utilisateur ajoute is_admin=1 dans sa requête, il
pourrait s’attribuer des droits d’administrateur. L’attribut $fillable empêche ce type d’attaque en
limitant les champs qui peuvent être modifiés en masse.

Modification du contrôleur ProductController

Auteur: Steeve LEFORT <[email protected]> page 5 sur 21


Maintenant que nous avons notre modèle et notre table, adaptons notre contrôleur pour utiliser la base de
données au lieu des données statiques.

1. Importation du modèle

Ajoutez cette ligne en haut de votre ProductController.php :

use App\Models\Product;

2. Adaptation de la méthode store pour enregistrer en base de données

La méthode store doit maintenant créer une entrée dans la base de données :

/**
* Enregistre un nouveau produit
*/
public function store(ProductRequest $request)
{
// Validation déjà gérée par ProductRequest

// Créer un nouveau produit avec les données validées


$product = Product::create($request->except('image'));

// Gérer l'upload de l'image si elle existe


if ($request->hasFile('image') && $request->file('image')->isValid()) {
$image = $product->id . '.' . $request->image->extension();
$request->file('image')->move(public_path('images'), $image);
$product->image = $image;
$product->save();
} else {
// Utiliser une image par défaut
$product->image = 'placeholder.jpg';
$product->save();
}

return redirect('/')->with('success', 'Produit ajouté avec succès!');


}

Auteur: Steeve LEFORT <[email protected]> page 6 sur 21


Explication :

Product::create() crée une nouvelle entrée dans la table products


$request->except('image') prend toutes les données validées sauf l’image
public_path('images') renvoie le chemin complet vers le dossier public/images
La méthode move() déplace le fichier téléchargé vers sa destination finale
Nous utilisons l’ID du produit comme nom de fichier pour éviter les conflits

3. Modification de la méthode viewAll pour afficher les produits de la base de données

Modifiez la méthode qui affiche tous les produits :

/**
* Affiche tous les produits
*/
public function viewAll()
{
// Récupérer tous les produits de la base de données
$products = Product::all();

return view('products', ['products' => $products]);


}

4. Modification de la méthode viewProduct pour afficher un produit spécifique

/**
* Affiche les détails d'un produit
*/
public function viewProduct($id)
{
try {
$product = Product::findOrFail($id);
return view('product_details', ['product' => $product]);
} catch (\Exception $e) {
return view('error')->with('error', 'Produit non trouvé');
}
}

Auteur: Steeve LEFORT <[email protected]> page 7 sur 21


5. Adaptation de la méthode addToCart

/**
* Ajoute un produit au panier
*/
public function addToCart($id)
{
try {
// Vérifier que le produit existe
$product = Product::findOrFail($id);

// Récupérer le panier actuel ou créer un tableau vide


$cart = session('cart', []);

// Ajouter l'ID du produit au panier


$cart[] = $id;

// Sauvegarder le panier en session


session(['cart' => $cart]);

return redirect('/cart')->with('success', 'Produit ajouté au


panier');
} catch (\Exception $e) {
return redirect('/')->with('error', 'Produit introuvable');
}
}

6. Adaptation de la méthode viewCart

/**
* Affiche le contenu du panier
*/
public function viewCart()
{
// Récupérer le panier depuis la session
$cart = session('cart', []);

// Récupérer les produits correspondants aux IDs dans le panier

Auteur: Steeve LEFORT <[email protected]> page 8 sur 21


$cartItems = [];
$totalHT = 0;
$totalTVA = 0;
$totalTTC = 0;

foreach ($cart as $id) {


try {
$product = Product::findOrFail($id);
$cartItems[] = $product;

// Calculs
$totalHT += $product->price;
$tva = $product->price * ($product->vat / 100);
$totalTVA += $tva;
$totalTTC += $product->price + $tva;
} catch (\Exception $e) {
// Ignorer les produits qui n'existent plus
}
}

return view('cart', [
'cartItems' => $cartItems,
'totalHT' => $totalHT,
'totalTVA' => $totalTVA,
'totalTTC' => $totalTTC
]);
}

Mission 1: Importation des données existantes


Avant de tester notre application avec la base de données, importons les produits existants.

1. Création d’un seeder pour les produits

Laravel propose un mécanisme de “seeding” pour remplir la base de données avec des données de
test.

php artisan make:seeder ProductSeeder

Auteur: Steeve LEFORT <[email protected]> page 9 sur 21


Modifiez le fichier database/seeders/ProductSeeder.php :

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\Product;
use App\Models\ProductManager;

class ProductSeeder extends Seeder


{
/**
* Run the database seeds.
*/
public function run(): void
{
// Récupérer les produits depuis ProductManager
$staticProducts = ProductManager::getAllProducts();

// Les insérer dans la base de données


foreach ($staticProducts as $product) {
Product::create([
'name' => $product->name,
'image' => $product->image,
'price' => $product->price,
'vat' => $product->vat,
'description' => $product->description,
]);
}

$this->command->info('Produits importés avec succès !');


}
}

Explication: Un “seeder” est une classe qui permet de peupler votre base de données avec des
données de test ou initiales. C’est très utile pour:

Auteur: Steeve LEFORT <[email protected]> page 10 sur 21


Disposer rapidement d’un jeu de données pour développer et tester votre application
Préparer des données de démonstration
Initialiser votre base de données avec des données essentielles au fonctionnement de
l’application

2. Enregistrement du seeder

Modifiez database/seeders/DatabaseSeeder.php pour inclure notre seeder :

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder


{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
ProductSeeder::class,
]);
}
}

3. Exécution du seeder

php artisan db:seed

Ou pour ne lancer que le seeder de produits :

php artisan db:seed --class=ProductSeeder

Mission 2: Vérification du fonctionnement de l’application

Auteur: Steeve LEFORT <[email protected]> page 11 sur 21


Démarrez l’application et testez toutes les fonctionnalités :

composer run dev

Vérifiez les points suivants :

La page d’accueil affiche bien les produits de la base de données


La page de détail d’un produit fonctionne correctement
Le formulaire d’ajout de produit permet d’ajouter un nouveau produit en base de données
L’ajout au panier fonctionne comme avant

Si vous rencontrez des problèmes, n’hésitez pas à vérifier les logs d’erreur dans le fichier
storage/logs/laravel.log.

Mission 3: Création du formulaire d’édition de produit


Maintenant, ajoutons la fonctionnalité d’édition de produit.

1. Ajout des routes pour l’édition

Dans routes/web.php, ajoutez :

// Affichage du formulaire d'édition


Route::get('/product/edit/{id}', [ProductController::class, 'edit']);

// Traitement du formulaire d'édition


Route::post('/product/edit/{id}', [ProductController::class, 'update']);

2. Méthodes du contrôleur pour l’édition

Ajoutez ces méthodes à votre ProductController :

/**
* Affiche le formulaire d'édition d'un produit
*/
public function edit($id)
{
$product = Product::findOrFail($id);

Auteur: Steeve LEFORT <[email protected]> page 12 sur 21


return view('product.edit', ['product' => $product]);
}

/**
* Met à jour un produit existant
*/
public function update(ProductRequest $request, $id)
{
// Récupérer le produit
$product = Product::findOrFail($id);

// Mettre à jour les champs texte


$product->update($request->except('image'));

// Traiter la nouvelle image si fournie


if ($request->hasFile('image') && $request->file('image')->isValid()) {
// Supprimer l'ancienne image si ce n'est pas l'image par défaut
if ($product->image && $product->image != 'placeholder.jpg' &&
file_exists(public_path('images/' . $product->image))) {
unlink(public_path('images/' . $product->image));
}

// Enregistrer la nouvelle image


$image = $product->id . '.' . $request->image->extension();
$request->file('image')->move(public_path('images'), $image);
$product->image = $image;
$product->save();
}

return redirect('/')->with('success', 'Produit mis à jour avec succès');


}

Explication :

La fonction unlink() est une fonction PHP native qui supprime un fichier
Nous vérifions l’existence du fichier avec file_exists() avant de tenter de le supprimer pour
éviter les erreurs
Nous préservons l’image par défaut en vérifiant que ce n’est pas ’placeholder.jpg’

Auteur: Steeve LEFORT <[email protected]> page 13 sur 21


3. Création de la vue pour l’édition

Créez le fichier resources/views/product/edit.blade.php :

@extends('layouts.master')

@section('title', 'Modifier le produit')

@section('content')
<div class="container">
<h1>Modifier le produit: {{ $product->name }}</h1>

@if ($errors->any())
<div class="alert-error">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

<form action="{{ url('/product/edit/' . $product->id) }}" method="post"


enctype="multipart/form-data">
@csrf

<div class="form-group">
<label for="name">Nom du produit:</label>
<input type="text" id="name" name="name" value="{{ old('name',
$product->name) }}" required>
</div>

<div class="form-group">
<label for="price">Prix (HT):</label>
<input type="number" id="price" name="price" value="{{
old('price', $product->price) }}" step="0.01" min="0" required>
</div>

Auteur: Steeve LEFORT <[email protected]> page 14 sur 21


<div class="form-group">
<label for="vat">TVA (%):</label>
<input type="number" id="vat" name="vat" value="{{ old('vat',
$product->vat) }}" min="0" max="100" required>
</div>

<div class="form-group">
<label for="description">Description:</label>
<textarea id="description" name="description" rows="5" required>
{{ old('description', $product->description) }}</textarea>
</div>

<div class="form-group">
<label>Image actuelle:</label>
<img src="{{ asset('images/' . $product->image) }}" alt="{{
$product->name }}" style="max-width: 200px;">
</div>

<div class="form-group">
<label for="image">Nouvelle image (laisser vide pour conserver
l'image actuelle):</label>
<input type="file" id="image" name="image">
</div>

<button type="submit" class="btn-primary">Mettre à jour</button>


</form>
</div>
@endsection

4. Ajout des liens d’édition

Modifiez la vue products.blade.php pour ajouter des liens vers le formulaire d’édition :

@foreach($products as $product)
<div class="product-card">
<!-- Contenu existant -->

<div class="product-actions">

Auteur: Steeve LEFORT <[email protected]> page 15 sur 21


<a href="{{ url('/details/' . $product->id) }}" class="btn-
primary">Voir</a>
<a href="{{ url('/product/edit/' . $product->id) }}" class="btn-
secondary">Modifier</a>
</div>
</div>
@endforeach

Mission 4: Ajout de la fonctionnalité de suppression


Pour compléter notre CRUD (Create, Read, Update, Delete), ajoutons la possibilité de supprimer un
produit.

1. Ajout de la route pour la suppression

Dans routes/web.php, ajoutez :

// Suppression d'un produit


Route::get('/product/delete/{id}', [ProductController::class, 'delete']);

2. Méthode du contrôleur pour la suppression

Ajoutez cette méthode à votre ProductController :

/**
* Supprime un produit
*/
public function delete($id)
{
try {
// Récupérer le produit
$product = Product::findOrFail($id);

// Supprimer l'image associée (sauf si c'est l'image par défaut)


if ($product->image && $product->image != 'placeholder.jpg' &&
file_exists(public_path('images/' . $product->image))) {
unlink(public_path('images/' . $product->image));
}

Auteur: Steeve LEFORT <[email protected]> page 16 sur 21


// Supprimer le produit
$product->delete();

return redirect('/')->with('success', 'Produit supprimé avec


succès');
} catch (\Exception $e) {
return redirect('/')->with('error', 'Erreur lors de la suppression du
produit: ' . $e->getMessage());
}
}

3. Ajout du bouton de suppression dans la liste des produits

Modifiez à nouveau la vue products.blade.php pour ajouter un lien de suppression:

<div class="product-actions">
<a href="{{ url('/details/' . $product->id) }}" class="btn-
primary">Voir</a>
<a href="{{ url('/product/edit/' . $product->id) }}" class="btn-
secondary">Modifier</a>
<a href="{{ url('/product/delete/' . $product->id) }}" class="btn-danger"
onclick="return confirm('Êtes-vous sûr de vouloir supprimer ce
produit?');">Supprimer</a>
</div>

Explication : La fonction JavaScript confirm() affiche une boîte de dialogue avec un choix Oui/Non.
Si l’utilisateur clique sur “Annuler”, l’événement de clic est annulé et la suppression n’a pas lieu. C’est
une façon simple d’ajouter une confirmation avant suppression.

Précision: Une suppression est généralement associée au verbe HTTP `DELETE`. Cela nécessiterait
de créer autant de formulaires que de boutons de suppression. Pour simplifier le code, nous avons fait
le choix d’utiliser le verbe `GET`.

4. Ajout de styles CSS pour le bouton de suppression

Dans votre fichier public/css/styles.css, ajoutez un style pour le bouton de suppression :

Auteur: Steeve LEFORT <[email protected]> page 17 sur 21


.btn-danger {
background-color: #dc3545;
color: white;
padding: 8px 15px;
border: none;
border-radius: 4px;
text-decoration: none;
display: inline-block;
}

.btn-danger:hover {
background-color: #c82333;
}

.btn-secondary {
background-color: #6c757d;
color: white;
padding: 8px 15px;
border: none;
border-radius: 4px;
text-decoration: none;
display: inline-block;
}

.btn-secondary:hover {
background-color: #5a6268;
}

Annexe : Guide Eloquent ORM


Eloquent est l’ORM de Laravel. Il vous permet de travailler avec votre base de données comme si vous
manipuliez des objets PHP, sans écrire de SQL.

1. Récupération de données

// Tous les produits


$products = Product::all();

Auteur: Steeve LEFORT <[email protected]> page 18 sur 21


// Un produit spécifique par ID
$product = Product::find(1);

// Un produit avec erreur 404 si non trouvé


$product = Product::findOrFail(1);

// Le premier produit qui correspond à un critère


$cheapProduct = Product::where('price', '<', 50)->first();

// Tous les produits qui correspondent à un critère


$expensiveProducts = Product::where('price', '>', 100)->get();

// Chaînage de conditions
$products = Product::where('price', '>', 50)
->where('vat', 20)
->orderBy('name')
->get();

// Pagination
$paginatedProducts = Product::paginate(10);

2. Création de données

// Méthode 1: via create (assignation en masse)


$product = Product::create([
'name' => 'Nouveau produit',
'price' => 99.99,
'vat' => 20,
'description' => 'Description du produit',
'image' => 'default.jpg'
]);

// Méthode 2: via new et save


$product = new Product();
$product->name = 'Nouveau produit';
$product->price = 99.99;
$product->vat = 20;

Auteur: Steeve LEFORT <[email protected]> page 19 sur 21


$product->description = 'Description du produit';
$product->image = 'default.jpg';
$product->save();

3. Mise à jour de données

// Méthode 1: trouver puis modifier


$product = Product::find(1);
$product->price = 129.99;
$product->save();

// Méthode 2: mise à jour via update (assignation en masse)


$product->update([
'price' => 129.99,
'description' => 'Nouvelle description'
]);

// Méthode 3: mise à jour en masse de plusieurs enregistrements


Product::where('vat', 19)->update(['vat' => 20]);

4. Suppression de données

// Méthode 1: trouver puis supprimer


$product = Product::find(1);
$product->delete();

// Méthode 2: suppression directe


Product::destroy(1);

// Suppression multiple
Product::destroy([1, 2, 3]);

// Suppression conditionnelle
Product::where('price', '<', 10)->delete();

5. Requêtes avancées

Auteur: Steeve LEFORT <[email protected]> page 20 sur 21


// Comptage
$count = Product::where('price', '>', 100)->count();

// Moyenne
$avgPrice = Product::avg('price');

// Somme
$totalValue = Product::sum('price');

// Min/Max
$cheapest = Product::min('price');
$mostExpensive = Product::max('price');

// Combinaison de méthodes
$stats = Product::where('vat', 20)
->selectRaw('COUNT(*) as count, AVG(price) as avg_price')
->first();

Conclusion
Dans cette étape du TP, vous avez appris à :

Configurer une base de données dans Laravel 12


Créer des migrations et des modèles
Manipuler des données avec Eloquent ORM
Implémenter un CRUD complet (Create, Read, Update, Delete)
Gérer l’upload de fichiers

Ces compétences sont essentielles pour tout développeur Laravel et vous permettront de créer des
applications web robustes et évolutives.

Dans les prochaines étapes du TP, nous aborderons l’authentification avec Laravel Breeze, les middlewares
et les API REST.

Pour approfondir :

Documentation officielle Eloquent : https://laravel.com/docs/12.x/eloquent


Documentation sur les migrations : https://laravel.com/docs/12.x/migrations

Auteur: Steeve LEFORT <[email protected]> page 21 sur 21

Vous aimerez peut-être aussi