Welcome! This is a text-based course, but in the video above, you can see the overview of the project we're gonna build.
Before we start creating Livewire/Vue/React dynamic pages, let's build the back-end "core" base of our app: models, migrations, factories, seeders.
This is what we will have in the products
table, by the end of this lesson:
Then, we will create three branches from that base, one for each stack.
Building Laravel application
This step has nothing special - just run:
laravel new product-demo
Once this is done, we can start working on our Models.
We want to create migration/model/factory combinations for these objects:
- Product Categories
- Product Manufacturers
- Products
- Cart
So, let's do it, model by model.
Product Categories: Migration/Model/Factory
Let's start with the simple migration:
Migration
Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps();});
app/Models/Category.php
use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model; class Category extends Model{ use HasFactory; protected $fillable = ['name']; public function products(): HasMany { return $this->hasMany(Product::class); }}
database/factories/CategoryFactory.php
use Illuminate\Database\Eloquent\Factories\Factory; class CategoryFactory extends Factory{ public function definition(): array { return [ 'name' => $this->faker->text(20), ]; }}
That's it. Our simple categories are ready.
Product Manufacturers: Migration/Model/Factory
Similar steps here.
Again, we start with our migration:
Migration
Schema::create('manufacturers', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps();});
app/Models/Manufacturer.php
use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model; class Manufacturer extends Model{ use HasFactory; protected $fillable = ['name']; public function products(): HasMany { return $this->hasMany(Product::class); }}
database/factories/ManufacturerFactory.php
use Illuminate\Database\Eloquent\Factories\Factory; class ManufacturerFactory extends Factory{ public function definition(): array { return [ 'name' => $this->faker->text(20), ]; }}
Product: Migration/Model/Factory
Our Product table is a bit more complicated than the others, but nothing too hard:
Migration
Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->text('description'); $table->decimal('price'); $table->foreignId('category_id')->constrained(); $table->foreignId('manufacturer_id')->constrained(); $table->timestamps();});
But inside the Model, you will see us using a few things:
- A constant array to get a list of price groups. This could be a separate table in your application.
- A filter scope that contains different filter lists and returns a filtered products table.
And don't get scared. It looks complicated, but it's just a lot of ->when()
conditions.
Let's look at the code:
app/Models/Product.php
use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model; class Product extends Model{ use HasFactory; protected $fillable = ['category_id', 'name', 'description', 'price', 'manufacturer_id']; const PRICES = [ 'Less than 50', 'From 50 to 100', 'From 100 to 500', 'More than 500', ]; public function category(): BelongsTo { return $this->belongsTo(Category::class); } public function scopeWithFilters($query, $prices, $categories, $manufacturers) { return $query->when(count($manufacturers), function ($query) use ($manufacturers) { $query->whereIn('manufacturer_id', $manufacturers); }) ->when(count($categories), function ($query) use ($categories) { $query->whereIn('category_id', $categories); }) ->when(count($prices), function ($query) use ($prices){ $query->where(function ($query) use ($prices) { $query->when(in_array(0, $prices), function ($query) { $query->orWhere('price', '<', '50'); }) ->when(in_array(1, $prices), function ($query) { $query->orWhereBetween('price', ['50', '100']); }) ->when(in_array(2, $prices), function ($query) { $query->orWhereBetween('price', ['100', '500']); }) ->when(in_array(3, $prices), function ($query) { $query->orWhere('price', '>', '500'); }); }); }); }}
And lastly, we do need a Factory:
database/factories/ProductFactory.php
use App\Models\Product;use App\Models\Category;use App\Models\Manufacturer;use Illuminate\Database\Eloquent\Factories\Factory; class ProductFactory extends Factory{ public function definition(): array { return [ 'name' => $this->faker->word(), 'category_id' => Category::inRandomOrder()->first()->id, 'manufacturer_id' => Manufacturer::inRandomOrder()->first()->id, 'description' => $this->faker->paragraph(), 'price' => rand(10, 999), ]; }}
That's it, our products are ready.
Cart: Migration/Model
Last preparation step - our Cart table.
Note: Since it's a demo application, we skip the user_id
column, so adding to cart will be a bit "fake", just to focus on Livewire/Vue/React dynamic behavior. You should add the user_id
in real applications!
Migration
Schema::create('carts', function (Blueprint $table) { $table->id(); $table->foreignId('product_id')->constrained(); $table->timestamps();});
app/Models/Cart.php
use Illuminate\Database\Eloquent\Model; class Cart extends Model{ protected $fillable = ['product_id'];}
For the Cart, we don't need a Factory, because we don't seed any demo data for this table.
That's it! Our Cart is now ready to be used in our Demo.
Running Seeders
Of course, manually creating the data is tedious. That is why we had Factories in place. Now we can add a call to our seeder and get some rows into tables:
database/seeders/DatabaseSeeder.php
use App\Models\Category;use App\Models\Manufacturer;use App\Models\Product;use App\Models\User;use Illuminate\Database\Seeder; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; class DatabaseSeeder extends Seeder{ /** * Seed the application's database. */ public function run(): void { User::factory()->create([ 'name' => 'Test User', 'email' => 'admin@admin.com', ]); Category::factory(5)->create(); Manufacturer::factory(5)->create(); Product::factory(20)->create(); }}
Now, if you would run:
php artisan migrate --seed
You should see that there is data in all the tables.
That's it for our base application!
From here, we will start working with UI-based elements, as they all use the same "base application".
What's Next?
In all lessons, we will install Laravel Breeze with different stack choice. So expect to see these examples:
- Blade Stack with Livewire
- Inertia with Vue.js
- Inertia with React.js
In the final GitHub repository, all those three stacks will have separate branches, and direct links will be available after each of the following lessons.
No comments or questions yet...