Skip to main content

Laravel

Requirements

  • Ubuntu 24.04

  • PHP 8

    sudo apt install -y php8.3 php8.3-mysql
    
  • Laravel 10

  • composer

    sudo apt install -y composer
    
  • mariadb

    sudo apt install -y mariadb-server
    

Basics

Overview

Laravel is a PHP framework.
It uses MVC architecture.

MVC-Process

    Model
      Model data View
        Present to users Controller
          Manipulate Model and interact with users

          Request Lifecycle

          public/index.php
          HTTP / Console Kernels
          Service Providers / Service Container
          Routing
          Middleware
          View
          Response

          php-laravel-request-lifecycle.jpg

          Service Container

          The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

          Service Provider

          Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel's core services, are bootstrapped via service providers.

          But, what do we mean by "bootstrapped"? In general, we mean registering things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application.

          Facade

          Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades which provide access to almost all of Laravel's features.

          Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. It's perfectly fine if you don't totally understand how facades work - just go with the flow and continue learning about Laravel.

          Example

          • Setup Apache2

            sudo vim /etc/apache2/apache2.conf
            
            # <Directory /var/www/Websites>
            #     Options Indexes FollowSymLinks
            #     AllowOverride All
            #     Require all granted
            # </Directory>
            
            sudo a2enmod rewrite
            sudo systemctl reload apache2
            
            # Create virtual host config file.
            sudo vim /etc/apache2/sites-available/shop.conf
            
            # <VirtualHost *:80>
            # 	  DocumentRoot /var/www/Websites/shop/public
            # </VirtualHost>
            
            sudo systemctl reload apache2
            
          • Create project

            • Try to create a simple CRUD (create, read, update, delete) example.
            cd /var/www/Websites/
            sudo -u www-data composer create-project laravel/laravel:10 shop
            
            cd /var/www/Websites/shop/
            

            Edit environment variables

            # Create database user and database in advanced.
            # Set database basic information.
            sudo -u www-data vim .env
            sudo systemctl reload apache2
            

            Create model (with migration) & controller

            # Create model with controller and migration.
            sudo -u www-data php artisan make:model -c -m Product
            

            Define model

            sudo -u www-data vim app/Models/Product.php
            
            File Content
            <?php
            
            namespace App\Models;
            
            use Illuminate\Database\Eloquent\Factories\HasFactory;
            use Illuminate\Database\Eloquent\Model;
            
            class Product extends Model
            {
              use HasFactory;
            
              protected $fillable = ['title', 'descript', 'price'];
            }
            

            Define migration according to model

            sudo -u www-data vim database/migrations/2024_09_09_082148_create_products_table.php
            
            File Content
            <?php
            
            use Illuminate\Database\Migrations\Migration;
            use Illuminate\Database\Schema\Blueprint;
            use Illuminate\Support\Facades\Schema;
            
            return new class extends Migration
            {
              /**
               * Run the migrations.
               */
              public function up(): void
              {
                Schema::create('products', function (Blueprint $table) {
                  $table->id();
                  $table->text('title');
                  $table->text('descript');
                  $table->unsignedInteger('price');
                  $table->timestamps();
                });
              }
            
              /**
               * Reverse the migrations.
               */
              public function down(): void
              {
                Schema::dropIfExists('products');
              }
            };
            

            Do migrate to create table in database

            sudo -u www-data php artisan migrate
            sudo -u www-data php artisan migrate:status
            

            Define controller

            sudo -u www-data vim app/Http/Controllers/ProductController.php
            
            File Content
            <?php
            
            namespace App\Http\Controllers;
            
            use Illuminate\Http\Request;
            use App\Models\Product;
            
            class ProductController extends Controller
            {
                public function index()
                {
                    // Fetch all products from DB.
                    $products = Product::all();
            
                    return view('product.list', ['products' => $products]);
                }
            
                public function show(int $product_id)
                {
                    return view('product.show', ['product' => Product::find($product_id)]);
                }
            
                public function create()
                {
                    return view('product.add');
                }
            
                public function store(Request $request)
                {
                    $newProduct = Product::create([
                        'title' => $request->title,
                        'descript' => $request->descript,
                        'price' => $request->price
                    ]);
            
                    return redirect('/products');
                }
            
                public function edit(int $product_id)
                {
                    return view('product.edit', ['product' => Product::find($product_id)]);
                }
            
                public function update(int $product_id, Request $request)
                {
                    Product::find($product_id)->update([
                        'title' => $request->title,
                        'descript' => $request->descript,
                        'price' => $request->price
                    ]);
            
                    return redirect('/products/' . $product_id);
                }
            
                public function destroy(int $product_id)
                {
                    Product::find($product_id)->delete();
                    return redirect('/products');
                }
            }
            
            File Content (with validate)
            <?php
            
            namespace App\Http\Controllers;
            
            use Illuminate\Http\Request;
            use App\Models\Product;
            use Illuminate\Support\Facades\Validator;
            
            class ProductController extends Controller
            {
                public function index(Request $request)
                {
                    // Fetch all products from DB.
                    $products = Product::all();
            
                    return view('product.list', ['products' => $products, 'request' => $request]);
                }
            
                public function create()
                {
                    return view('product.add');
                }
            
                public function store(Request $request)
                {
                    $validator = ProductController::validate_($request);
            
                    if($validator->fails())
                    {
                        return redirect()->back()->withErrors($validator)->withInput();
                    }
            
                    $newProduct = Product::create([
                        'title' => $request->title,
                        'descript' => $request->descript,
                        'price' => $request->price
                    ]);
            
                    return redirect('/products');
                }
            
                public function show(int $product_id)
                {
                    return view('product.show', ['product' => Product::find($product_id)]);
                }
            
                public function edit(int $product_id)
                {
                    return view('product.edit', ['product' => Product::find($product_id)]);
                }
            
                public function update(int $product_id, Request $request)
                {
                    $validator = ProductController::validate_($request);
            
                    if($validator->fails())
                    {
                        return redirect()->back()->withErrors($validator)->withInput();
                    }
            
                    Product::find($product_id)->update([
                        'title' => $request->title,
                        'descript' => $request->descript,
                        'price' => $request->price
                    ]);
            
                    return redirect('/products/' . $product_id);
                }
            
                public function destroy(int $product_id)
                {
                    Product::find($product_id)->delete();
                    return redirect('/products');
                }
            
                private static function validate_(Request& $request)
                {
                    return Validator::make($request->all(), [
                        'title' => 'required|string|max:255',
                        'descript' => 'required|string|max:1000',
                        'price' => 'required|numeric|min:0'
                    ]);
                }
            }
            

            Define view

            sudo -u www-data mkdir resources/views/product
            
            sudo -u www-data vim resources/views/product/list.blade.php
            
            File Content
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product List</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        table
                        {
                            border-collapse: collapse;
                        }
            
                        th, td
                        {
                            padding: 5px 10px;
                            border: 1px solid #000000;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product List</h1>
            
                        @if ($products->isEmpty())
                            <p>No content available.</p>
                        @else
                            <table class="table table-bordered">
                                <thead>
                                    <tr>
                                        <th>ID</th>
                                        <th>Title</th>
                                        <th>Description</th>
                                        <th>Price</th>
                                        <th>Actions</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    @foreach ($products as $product)
                                        <tr>
                                            <td>{{ $product->id }}</td>
                                            <td><a href="/products/{{ $product->id }}">{{ $product->title }}</a></td>
                                            <td>{{ $product->descript }}</td>
                                            <td>{{ $product->price }}</td>
                                            <td>
                                                <a href="/products/{{ $product->id }}/edit" class="btn btn-warning btn-sm">Edit</a>
                                                <form action="/products/{{ $product->id }}" method="post" style="display:inline;">
                                                    @csrf
                                                    <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this product?')">Delete</button>
                                                </form>
                                            </td>
                                        </tr>
                                    @endforeach
                                </tbody>
                            </table>
                        @endif
            
                        <br />
            
                        <a href="/products/create" class="btn btn-primary mb-3">Add a Product</a>
                    </div>
                </body>
            </html>
            
            sudo -u www-data vim resources/views/product/show.blade.php
            
            File Content
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product Show</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        table
                        {
                            border-collapse: collapse;
                        }
            
                        table th,
                        table td
                        {
                            padding: 5px 10px;
                            border: 1px solid #000000;
                            text-align: left;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product Show</h1>
            
                        <table>
                            <tr>
                                <th>ID</th>
                                <td>{{ $product->id }}</td>
                            </tr>
            
                            <tr>
                                <th>Title</th>
                                <td>{{ $product->title }}</td>
                            </tr>
            
                            <tr>
                                <th>Description</th>
                                <td>{{ $product->descript }}</td>
                            </tr>
            
                            <tr>
                                <th>Price</th>
                                <td>{{ $product->price }}</td>
                            </tr>
                        </table>
            
                        <br />
            
                        <a href="/products">Back to list</a>
                    </div>
                </body>
            </html>
            
            sudo -u www-data vim resources/views/product/add.blade.php
            
            File Content
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product Add</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        input
                        {
                            margin: 5px 0px;
                            padding: 5px;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product Add</h1>
            
                        <form method="post" action="/products/create">
                            @csrf
                            <input type="text" name="title" placeholder="Title" /><br />
                            <input type="text" name="descript" placeholder="Description" /><br />
                            <input type="text" name="price" placeholder="Price" /><br />
            
                            <input type="submit" value="Add" />
                        </form>
                    </div>
            
                    <br />
            
                    <a href="/products">Back to list</a>
                </body>
            </html>
            
            File Content (with validate)
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product Add</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        input
                        {
                            margin: 5px 0px;
                            padding: 5px;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product Add</h1>
            
                        @if($errors->any())
                            <ul>
                                @foreach($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        @endif
            
                        <form method="post" action="/products/create">
                            @csrf
                            <input type="text" name="title" placeholder="Title" value="{{ old('title') }}" /><br />
                            <input type="text" name="descript" placeholder="Description" value="{{ old('descript') }}" /><br />
                            <input type="text" name="price" placeholder="Price" value="{{ old('price') }}" /><br />
            
                            <input type="submit" value="Add" />
                        </form>
                    </div>
            
                    <br />
            
                    <a href="/products">Back to list</a>
                </body>
            </html>
            
            sudo -u www-data vim resources/views/product/edit.blade.php
            
            File Content
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product Edit</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        input
                        {
                            margin: 5px 0px;
                            padding: 5px;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product Edit</h1>
            
                        <form method="post" action="/products/{{ $product->id }}/edit">
                            @csrf
                            <input type="text" name="title" placeholder="Title" value="{{ $product->title }}" /><br />
                            <input type="text" name="descript" placeholder="Description" value="{{ $product->descript }}" /><br />
                            <input type="text" name="price" placeholder="Price" value="{{ $product->price }}" /><br />
            
                            <input type="submit" value="Edit" />
                        </form>
                    </div>
            
                    <br />
            
                    <a href="/products">Back to list</a>
                </body>
            </html>
            
            File Content (with validate)
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>Product Edit</title>
                    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
            
                    <style>
            
                        input
                        {
                            margin: 5px 0px;
                            padding: 5px;
                        }
            
                    </style>
                </head>
                <body>
                    <div class="container">
                        <h1>Product Edit</h1>
            
                        @if($errors->any())
                            <ul>
                                @foreach($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        @endif
            
                        <form method="post" action="/products/{{ $product->id }}/edit">
                            @csrf
                            <input type="text" name="title" placeholder="Title" value="{{ $product->title }}" /><br />
                            <input type="text" name="descript" placeholder="Description" value="{{ $product->descript }}" /><br />
                            <input type="text" name="price" placeholder="Price" value="{{ $product->price }}" /><br />
            
                            <input type="submit" value="Edit" />
                        </form>
                    </div>
            
                    <br />
            
                    <a href="/products">Back to list</a>
                </body>
            </html>
            

            Define router

            sudo -u www-data vim routes/web.php
            
            File Content
            <?php
            
            use Illuminate\Support\Facades\Route;
            use App\Http\Controllers\ProductController;
            
            /*
            |--------------------------------------------------------------------------
            | Web Routes
            |--------------------------------------------------------------------------
            |
            | Here is where you can register web routes for your application. These
            | routes are loaded by the RouteServiceProvider and all of them will
            | be assigned to the "web" middleware group. Make something great!
            |
            */
            
            Route::get('/', function () { return view('welcome'); });
            Route::get('/products', [ProductController::class, 'index']);
            Route::get('/products/{id}', [ProductController::class, 'show']);
            Route::get('/products/create', [ProductController::class, 'create']);
            Route::post('/products/create', [ProductController::class, 'store']);
            Route::get('/products/{id}/edit', [ProductController::class, 'edit']);
            Route::post('/products/{id}/edit', [ProductController::class, 'update']);
            Route::post('/products/{id}', [ProductController::class, 'destroy']);
            

            Reference

            PHP Laravel 5 Project Example for Beginners Building Your First Laravel 10 CRUD Application in Under 30 Minutes laravel-best-practices Mapping between ROUTE and REQUEST
            Method Path Function Form Description
            GET /products index() UI List all
            GET /products/{product} show() UI Show one
            GET /products/create create() UI Add one
            POST /products/create store() API Add one
            GET /products/{product}/edit edit() UI Edit one
            POST /products/{product}/edit update() API Edit one
            POST /products/{product} destroy() API Delete one