How I built and designed MyWishlist - Part One
Tags: laravel, vue, database, tailwindcss
Published: 2024-05-09 14:32:29

The moment I finished Jeffrey Way's course called [Laravel 8 From Scratch](https://laracasts.com/series/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](https://wishlist.fabjolamemushaj.com/) 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:

```php
  Route::view('/', 'welcome');
```
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:

```php
//app/Models/Product.php

class Product extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}
```
This is the code for the Category model:

```php
//app/Models/Category.php

class Category extends Model
{
    use HasFactory;

    protected $guarded = [];

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


## Unit Tests

Since I saw a Jeffrey Way course called [Code Katas with PHPUnit](https://laracasts.com/series/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:

```php
// 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);
}
```

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

```php
// 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());
}
```


## 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
php artisan make:migration create_categories_table
```

This command will create the structure for the following code:

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

Also the Schema created for the Product:

```php
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();
        });
    }
};
```
Here we notice that the `id` column, which is the `primary key `in 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:

```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(),

        ];
    }
}
```
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.

```php 
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]);
    }
}
```
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 😊.