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.png

  • 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

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

This is a simple CRUD (create, read, update, delete) 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

    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(Request $request, int $product_id)
        {
            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(Request $request, int $product_id)
        {
            $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>
    
                @if($product)
                    <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>
                @else
                    <p>Not a valid product ID.</p>
                @endif
    
                <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>
    
                @if($product)
                    <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>
                @else
                    <p>Not a valid product ID.</p>
                @endif
            </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
    
                @if($product)
                    <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>
                @else
                    <p>Not a valid product ID.</p>
                @endif
            </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