Laravel

Un framework qui rend heureux

Voir cette catégorie
Vers le bas
Shopping : les données 2/2
Vendredi 8 mai 2020 17:18

Dans le précédent article on a mis en place les migrations, les modèles et les factories pour gérer les données de la boutique. Nous allons à présent établir les relations entre les tables avec Eloquent. Il nous faudra aussi créer quelques accessors et mutators pour simplifier le code. On terminera en définissant la population pour avoir nos données d'exemple.

Vous pouvez télécharger un ZIP du projet ici.

Les relations

User

Un utilisateur a des adresses et des commandes :
public function addresses()
{
    return $this->hasMany(Address::class);
}

public function orders()
{
    return $this->hasMany(Order::class);
}

Order

Une commande a plusieurs adresses et plusieurs produits, d'autre part elle appartient à un utilisateur et un état. Enfin elle a une information de paiement :
public function adresses()
{
    return $this->hasMany(OrderAddress::class);
}

public function products()
{
    return $this->hasMany(OrderProduct::class);
}

public function state()
{
    return $this->belongsTo(State::class);
}

public function user()
{
    return $this->belongsTo(User::class);
}

public function payment_infos()
{
    return $this->hasOne(Payment::class);
}
On met aussi en place quelques accesseurs :
public function getPaymentTextAttribute($value)
{
    $texts = [
      'carte' => 'Carte bancaire',
      'virement' => 'Virement',
      'cheque' => 'Chèque',
      'mandat' => 'Mandat administratif',
    ];

    return $texts[$this->payment];
}

public function getTotalOrderAttribute()
{
    return $this->total + $this->shipping;
}

public function getTvaAttribute()
{
    return $this->tax > 0 ? $this->total / (1 + $this->tax) * $this->tax : 0;
}

public function getHtAttribute()
{
    return $this->total / (1 + $this->tax);
}
On verra leur utilité plus tard.

Address

Une adresse appartient à un pays :
public function country()
{
    return $this->belongsTo(Country::class);
}

Country

Un pays a plusieurs plages de poids (et réciproquement), adresses et adresses de commande :
public function ranges()
{
    return $this->belongsToMany(Range::class, 'colissimos')->withPivot('id', 'price');
}

public function addresses()
{
    return $this->hasMany(Address::class);
}

public function order_addresses()
{
    return $this->hasMany(OrderAddress::class);
}
On récupère les frais de port dans la table pivot colissimos.

OrderAddress

Une adresse de commande appartient à un pays :
public function country()
{
    return $this->belongsTo(Country::class);
}
On part du principe que les pays sont stables dans la base.

Range

Les plages de poids sont en relation n:n avec les pays avec colissimos comme pivot :
public function countries()
{
    return $this->belongsToMany(Country::class, 'colissimos')->withPivot('price');
}

State

Un état a plusieurs commandes :
public function orders()
{
    return $this->hasMany(Order::class);
}

Synthèse

Voici un schéma global pour visualiser ces relations :

La population

Il ne nous reste plus qu'à coder la population dans la classe DatabaseSeeder :
<?php

use Illuminate\Database\Seeder;
use App\Models\ { User, Address, Country, Product, Colissimo, Range, State, Shop, Page, Order };
use Illuminate\Support\Str;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {        
        Country::insert([
            ['name' => 'France', 'tax' => 0.2],
            ['name' => 'Belgique', 'tax' => 0.2],
            ['name' => 'Suisse', 'tax' => 0],
            ['name' => 'Canada', 'tax' => 0],
        ]);

        Range::insert([
            ['max' => 1],
            ['max' => 2],
            ['max' => 3],
            ['max' => 100],
        ]);

        Colissimo::insert([
            ['country_id' => 1, 'range_id' => 1, 'price' => 7.25],
            ['country_id' => 1, 'range_id' => 2, 'price' => 8.95],
            ['country_id' => 1, 'range_id' => 3, 'price' => 13.75],
            ['country_id' => 1, 'range_id' => 4, 'price' => 0],
            ['country_id' => 2, 'range_id' => 1, 'price' => 15.5],
            ['country_id' => 2, 'range_id' => 2, 'price' => 17.55],
            ['country_id' => 2, 'range_id' => 3, 'price' => 22.45],
            ['country_id' => 2, 'range_id' => 4, 'price' => 0],
            ['country_id' => 3, 'range_id' => 1, 'price' => 15.5],
            ['country_id' => 3, 'range_id' => 2, 'price' => 17.55],
            ['country_id' => 3, 'range_id' => 3, 'price' => 22.45],
            ['country_id' => 3, 'range_id' => 4, 'price' => 0],
            ['country_id' => 4, 'range_id' => 1, 'price' => 27.65],
            ['country_id' => 4, 'range_id' => 2, 'price' => 38],
            ['country_id' => 4, 'range_id' => 3, 'price' => 55.65],
            ['country_id' => 4, 'range_id' => 4, 'price' => 0],
        ]);

        State::insert([
            ['name' => 'Attente chèque', 'slug' => 'cheque', 'color' => 'blue', 'indice' => 1],
            ['name' => 'Attente mandat administratif', 'slug' => 'mandat', 'color' => 'blue', 'indice' => 1],
            ['name' => 'Attente virement', 'slug' => 'virement', 'color' => 'blue', 'indice' => 1],
            ['name' => 'Attente paiement par carte', 'slug' => 'carte', 'color' => 'blue', 'indice' => 1],
            ['name' => 'Erreur de paiement', 'slug' => 'erreur', 'color' => 'red', 'indice' => 0],
            ['name' => 'Annulé', 'slug' => 'annule', 'color' => 'red', 'indice' => 2],
            ['name' => 'Mandat administratif reçu', 'slug' => 'mandat_ok', 'color' => 'green', 'indice' => 3],
            ['name' => 'Paiement accepté', 'slug' => 'paiement_ok', 'color' => 'green', 'indice' => 4],
            ['name' => 'Expédié', 'slug' => 'expedie', 'color' => 'green', 'indice' => 5],
            ['name' => 'Remboursé', 'slug' => 'rembourse', 'color' => 'red', 'indice' => 6],
        ]);

        factory(User::class, 20)
          ->create()
          ->each(function ($user) {
              $user->addresses()->createMany(
                  factory(Address::class, mt_rand(2, 3))->make()->toArray()
              );
        });

        $user = User::find(1);
        $user->admin = true;
        $user->save();

        factory(Product::class, 6)->create();

        factory(Shop::class)->create();

        $items = [
            ['livraisons', 'Livraisons'],
            ['mentions-legales', 'Mentions légales'],
            ['conditions-generales-de-vente', 'Conditons générales de vente'],
            ['politique-de-confidentialite', 'Politique de confidentialité'],
            ['respect-environnement', 'Respect de l\'environnement'],
            ['mandat-administratif', 'Mandat administratif'],
        ];

        foreach($items as $item) {
            factory(Page::class)->create([
                'slug' => $item[0],
                'title' => $item[1],
            ]);
        }

        factory(Order::class, 30)
          ->create()
          ->each(function ($order) {
              $address = $order->user->addresses()->take(1)->get()->makeHidden(['id', 'user_id'])->toArray();
              $order->adresses()->create($address[0]);
              if(mt_rand(0, 1)) {
                  $address = $order->user->addresses()->skip(1)->take(1)->get()->makeHidden(['id', 'user_id'])->toArray();
                  $address[0]['facturation'] = false;
                  $order->adresses()->create($address[0]);                 
              } 
              $countryId = $address[0]['country_id'];
              $total = 0;
              $product = Product::find(mt_rand(1, 3));
              $quantity = mt_rand(1, 3);
              $price = $product->price * $quantity;
              $total = $price; 
              $order->products()->create(
                  [
                      'name' => $product->name,
                      'total_price_gross' => $price,
                      'quantity' => $quantity,
                  ]
              );
              if(mt_rand(0, 1)) {
                  $product = Product::find(mt_rand(4, 6));
                  $quantity = mt_rand(1, 3);
                  $price = $product->price * $quantity;
                  $total += $price;
                  $order->products()->create(
                      [
                          'name' => $product->name,
                          'total_price_gross' => $price,
                          'quantity' => $quantity,
                      ]
                  ); 
              }
              if($order->payment === 'carte' && $order->state_id === 8) {
                  $order->payment_infos()->create(['payment_id' => (string) Str::uuid()]);
              }
              $order->tax = $countryId > 2 ? 0 : .2;
              $order->total = $total;
              $order->save();
        });
    }
}
On préserve un minimum de cohérence sans trop alourdir le code. On crée ainsi :
  • 4 pays
  • 4 plages de poids
  • des frais de ports par pays et par plage
  • 10 états de commande
  • 20 utilisateurs
  • 6 produits
  • une boutique
  • 6 pages d'information
  • 30 commandes

La migration

On arrive à l'instant de vérité en lançant les migrations et la population :
php artisan migrate --seed
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.44 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.47 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.3 seconds)
Migrating: 2020_05_07_213250_create_countries_table
Migrated:  2020_05_07_213250_create_countries_table (0.22 seconds)
Migrating: 2020_05_07_213940_create_addresses_table
Migrated:  2020_05_07_213940_create_addresses_table (1.61 seconds)
Migrating: 2020_05_08_111839_create_products_table
Migrated:  2020_05_08_111839_create_products_table (0.21 seconds)
Migrating: 2020_05_08_113300_create_states_table
Migrated:  2020_05_08_113300_create_states_table (0.26 seconds)
Migrating: 2020_05_08_113820_create_orders_table
Migrated:  2020_05_08_113820_create_orders_table (1.53 seconds)
Migrating: 2020_05_08_115032_create_order_addresses_table
Migrated:  2020_05_08_115032_create_order_addresses_table (2 seconds)
Migrating: 2020_05_08_115544_create_order_products_table
Migrated:  2020_05_08_115544_create_order_products_table (1.15 seconds)
Migrating: 2020_05_08_120042_create_ranges_table
Migrated:  2020_05_08_120042_create_ranges_table (0.24 seconds)
Migrating: 2020_05_08_120616_create_colissimos_table
Migrated:  2020_05_08_120616_create_colissimos_table (1.69 seconds)
Migrating: 2020_05_08_120946_create_shops_table
Migrated:  2020_05_08_120946_create_shops_table (0.24 seconds)
Migrating: 2020_05_08_122452_create_pages_table
Migrated:  2020_05_08_122452_create_pages_table (0.44 seconds)
Migrating: 2020_05_08_122809_create_payments_table
Migrated:  2020_05_08_122809_create_payments_table (1.05 seconds)
Migrating: 2020_05_08_123334_create_notifications_table
Migrated:  2020_05_08_123334_create_notifications_table (1.03 seconds)
Database seeding completed successfully.
 

Si vous n'otenez pas ce résultat c'est que vous avez oublié quelque chose en cours de route !

Si on regarde les utilisateurs créés on voit que le premier est administrateur :

Les images

Chaque produit a une image pour le visualiser. Pour stocker ces images on crée un dossier public/images avec un sous-dossier thumbs pour disposer d'une image en plus faible définition pour la page d'accueil des produits :

Ces images sont dans le fichier téléchargeable du projet.

Conclusion

On a maintenant notre schéma en place et des données pour pouvoir s'amuser. Dans le prochain article on commencera à coder la boutique !



Par bestmomo

Nombre de commentaires : 36