Laravel
Requirements
-
Ubuntu 24.04
-
PHP 8
sudo apt install -y php8.3 php8.3-mysql
-
Laravel 10
-
sudo apt install -y composer
-
mariadb
sudo apt install -y mariadb-server
Basics
Overview
Laravel is a PHP framework.
It uses MVC architecture.
- 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
- Laravel 10 official documents
- 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
No Comments