How I built and designed MyWishlist - Part One

jolamemushaj

jolamemushaj

Posted on May 9, 2024

How I built and designed MyWishlist - Part One

The moment I finished Jeffrey Way's course called Laravel 8 From Scratch, I thought that a good way to prove to myself that I understood how Laravel works was to try to create a project myself from scratch. This is where the idea of creating MyWishlist was born.

MyWishlist is a website where a user can add, edit or delete various products and links, which can be divided into categories.

Initially, I started it as a simple CRUD, but recently I came back to give it a more decent design (even though it's purple everywhere 😄) in order to have a more professional appearance. In MyWishlist, the user can add a product where he can specify the name, category to which this product belongs, size, color, price, currency in which the product is sold on the page where the user saw this product and a url that directs you to the page where the product is actually advertised.

The project started with the installation of Laravel and Tailwind. Later during the project I also installed Vue, to experiment a bit with the integration of Vue in an already started project, and to convert some of the blades into Vue components.

Routes

The first step for me was specifying the routes in web.php. The first route loads welcome.blade.php by using the uncommon Route::view() call:

  Route::view('/', 'welcome');
Enter fullscreen mode Exit fullscreen mode

Then I created a page layout using Laravel components, while styling with Tailwind.

Then I gradually continued adding Controllers, Models, Migrations, Factories and Seeders for Products and Categories as the two most important entities.

Models

While working with the backend, it is important to define the models and Eloquent relationships that exist between them. The first relationship was that a product belongs to a category and a category has many products.
This is the code for the Product model:

//app/Models/Product.php

class Product extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

This is the code for the Category model:

//app/Models/Category.php

class Category extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function products(): HasMany
    {
        return $this->hasMany(Product::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Unit Tests

Since I saw a Jeffrey Way course called Code Katas with PHPUnit, it stuck to me that a good developer should write tests. So I started writing the first test, where I tested if a product belongs to a category.

Below you will find the code for the test:

// tests/Units/ProductTest.php
public function test_a_product_belongs_to_a_category(): void
{
    // arrange
    $category = Category::factory()->create();
    $product = Product::factory()->create(['category_id' => $category->id]);

    // act
    $result = $product->category;

    // assert
    $this->assertInstanceOf(Category::class, $result);
    $this->assertEquals($category->id, $result->id);
}
Enter fullscreen mode Exit fullscreen mode

The second test was that a category has many products. Code below:

// tests/Units/CategoryTest.php
public function test_a_category_has_many_products(): void
{
    // Arrange
    $category = Category::factory()->create();
    $product1 = Product::factory()->create(['category_id' => $category->id]);
    $product2 = Product::factory()->create(['category_id' => $category->id]);
    $otherProduct = Product::factory()->create(['category_id' => Category::factory()->create()->id]);

    // Act
    $products = $category->products;

    // Assert
    $this->assertTrue($products->contains($product1));
    $this->assertTrue($products->contains($product2));
    $this->assertEquals(2, $products->count());
}
Enter fullscreen mode Exit fullscreen mode

Database Migrations

It is also very important to create a proper schema for the database following the principle to pick the smallest data type that will hold all of your data. Then the connection between the tables should be made through foreign keys and constraints. Instead of modifying the production database directly, we use migrations which must be stored in the database/migrations directory.

Here's how to create a database migration in Laravel:

php artisan make:migration create_categories_table
Enter fullscreen mode Exit fullscreen mode

This command will create the structure for the following code:

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });
    }
};
Enter fullscreen mode Exit fullscreen mode

Also the Schema created for the Product:

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->foreignId('category_id')->constrained()->cascadeOnDelete();
            $table->string('name');
            $table->string('color');
            $table->float('price');
            $table->string('currency');
            $table->string('size');
            $table->string('thumbnail')->nullable();
            $table->string('url');
            $table->timestamps();
        });
    }
};
Enter fullscreen mode Exit fullscreen mode

Here we notice that the id column, which is the primary keyin the categories table, serves as a foreign key in the products table, where it is renamed category_id. In this way, the tables are connected to each other.

Factories

Another important element, which helps preparing a local environment, are Factories. Through Factories we generate random database records, instead of manually specifying the value of each column. While working with MyWishlist I generated Factories mainly for Products, Categories and Websites.
Below you can find the code written in ProductFactory.php:

class ProductFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'category_id' => Category::factory(),
            'size' => fake()->randomDigit(),
            'price' => fake()->randomDigit(),
            'currency' => fake()->currencyCode(),
            'color' => fake()->colorName(),
            'url' => fake()->url(),

        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

The fake() function is a helper method which is used for generating fake data.

Seeders

Meanwhile Seeders are used to generate initial data that the app needs.

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $clothing = Category::factory()->create(['name' => 'Clothing']);
        $accessories = Category::factory()->create(['name' => 'Accessories']);
        $shoes = Category::factory()->create(['name' => 'Shoes']);

        Product::factory(15)->create(['category_id' => $clothing->id]);
        Product::factory(15)->create(['category_id' => $accessories->id]);
        Product::factory(15)->create(['category_id' => $shoes->id]);
    }
}
Enter fullscreen mode Exit fullscreen mode

As can be seen, inside the run() method I have created three categories Clothing, Accessories, and Shoes using the Category model factory. Each category is assigned a name using the create() method. Then, it creates 15 products for each category, using the Product model factory.

Until now, I have been writing for all the stuff related to Models and the Database. In the following post, I will write about the Controller logic, a Middleware I used to block editing, storing or deleting records when I deployed on the server, and how I converted Blade views into Vue components.

Thank you for making it this far 😊.

💖 💪 🙅 🚩
jolamemushaj
jolamemushaj

Posted on May 9, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related