REST api in laravel

REST api in Laravel: Demo 2

Verder kunnen we onze Laravel website uitbreiden met een eigen REST API gedefinieerd in Laravel zelf (of in een andere Laravel applicatie die op een andere server draait)

Hiervoor maken we dan wel een LaravelUser model aan omdat we nu effectief de Laravel database gaan gebruiken: php artisan make:model LaravelUser -mcf. Met dit commando wordt er ook automatisch een migrationTable en Controller aangemaakt.

In de controller gaan we nu dan de functionaliteit van onze REST API programmeren, volledig analoog aan onze Flask implementatie. Alleen moeten we nu PHP specifieke syntax gebruiken:

Klik hier om de code te zien/verbergen voor `app/Http/LaravelUserController.php`

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\LaravelUser;

class LaravelUserController extends Controller
{
    // Get all laravelUsers
    public function getLaravelUsers(Request $request)
    {
        $pwd = $request->query('pwd');
        if ($pwd == 'mypassword') {
            $laravelUsers = LaravelUser::all();
            return response()->json($laravelUsers, 200);
        } else {
            return response()->json(['message' => 'Wrong password mate'], 403);
        }
    }

    // Get a single laravelUser by ID
    public function getLaravelUser($id)
    {
        $laravelUser = LaravelUser::find($id);
        if ($laravelUser) {
            return response()->json($laravelUser, 200);
        } else {
            return response()->json(['message' => 'User not found'], 404);
        }
    }

    // Create a new laravelUser
    public function createLaravelUser(Request $request)
    {
        $name = $request->input('name');
        $email = $request->input('email');

        if (!$name || !$email) {
            return response()->json(['error' => 'Name and email required'], 400);
        }

        try {
            $laravelUser = new LaravelUser();
            $laravelUser->name = $name;
            $laravelUser->email = $email;
            $laravelUser->save();

            return response()->json(['message' => 'User created successfully'], 201);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

    // Delete a laravelUser
    public function deleteLaravelUser($id)
    {
        $laravelUser = LaravelUser::find($id);
        if (!$laravelUser) {
            return response()->json(['message' => 'User not found'], 404);
        }

        $laravelUser->delete();
        return response()->json(['message' => 'User deleted successfully'], 200);
    }
}

Ook het LaravelUser model moet nu correct zijn en bijhorende migration table om de objecten correct op te slaan in de database:

model: app/Models/LaravelUser.php

Klik hier om de code te zien/verbergen voor `app/Models/LaravelUser.php``

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class LaravelUser extends Model
{
    use HasFactory;

    // Specify the table name if it's not the plural of the model name
    protected $table = 'laravelUsers';

    // The attributes that are mass assignable
    protected $fillable = [
        'name',
        'email',
    ];

    // Disable timestamps if your table doesn't have created_at and updated_at columns
    public $timestamps = true;
}

migration database/migrations/XXXX_XX_XX_XXXXXX_create_laravel_users_table.php

De migration table zorgt hier weer voor de link tussen de php objecten en de database tabellen:

Klik hier om de code te zien/verbergen voor `database/migrations/XXXX_XX_XX_XXXXXX_create_laravel_users_table.php`

<?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()
    {
        Schema::create('laravelUsers', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamps();
        });
    }



    /**
     * Reverse the migrations.
     */
    public function down()
    {
        Schema::dropIfExists('laravelUsers');
    }
};

Routes: api.php

Ten slotte moeten we onze routes nog definiƫren in een nieuwe file routes/api.php. Op die manier kunnen we de endpoints bereiken via www.mijnlaravelwebsite.com/api/...

De file ziet er als volgt uit:

Klik hier om de code te zien/verbergen voor `routes/api.php`

<?php

use App\Http\Controllers\LaravelUserController;

Route::get('/laravelUsers', [LaravelUserController::class, 'getLaravelUsers']);
Route::get('/laravelUsers/{id}', [LaravelUserController::class, 'getLaravelUser']);
Route::post('/laravelUsers', [LaravelUserController::class, 'createLaravelUser']);
Route::delete('/laravelUsers/{id}', [LaravelUserController::class, 'deleteLaravelUser']);

We kunnen nu dan ook de users view in ons hoofdproject aanpassen en deze endpoints gebruiken in plaats van de Flask endpoints: views/users.blade.php

Klik hier om de code te zien/verbergen voor `views/users.blade.php`

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laravel Users</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <h1 class="my-4">Laravel Users</h1>

    <!-- Display Users -->
    <h2>All Users</h2>
    <table class="table table-striped" id="usersTable">
        <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
            <th>Actions</th>
        </tr>
        </thead>
        <tbody>
        </tbody>
    </table>

    <!-- Add New User -->
    <h2>Add New User</h2>
    <form id="createUserForm">
        <div class="mb-3">
            <label for="name" class="form-label">Name</label>
            <input type="text" class="form-control" id="name" name="name" required>
        </div>
        <div class="mb-3">
            <label for="email" class="form-label">Email</label>
            <input type="email" class="form-control" id="email" name="email" required>
        </div>
        <button type="submit" class="btn btn-primary">Create User</button>
    </form>

    <!-- Error or Success Messages -->
    <div id="message" class="mt-4"></div>
</div>

<script>
    // Function to fetch users and populate the table
    function fetchUsers() {
        fetch('/api/laravelUsers?pwd=mypassword')
            .then(response => response.json())
            .then(users => {
                const usersTable = document.getElementById('usersTable').getElementsByTagName('tbody')[0];
                usersTable.innerHTML = '';  // Clear existing rows

                users.forEach(user => {
                    const row = usersTable.insertRow();
                    row.insertCell(0).textContent = user.id;
                    row.insertCell(1).textContent = user.name;
                    row.insertCell(2).textContent = user.email;

                    const actionsCell = row.insertCell(3);
                    const deleteButton = document.createElement('button');
                    deleteButton.textContent = 'Delete';
                    deleteButton.className = 'btn btn-danger btn-sm';
                    deleteButton.onclick = () => deleteUser(user.id);
                    actionsCell.appendChild(deleteButton);
                });
            })
            .catch(error => console.error('Error fetching users:', error));
    }

    // Function to delete a user
    function deleteUser(id) {
        fetch(`/api/laravelUsers/${id}`, {
            method: 'DELETE'
        })
        .then(response => response.json())
        .then(data => {
            document.getElementById('message').innerHTML = `<div class="alert alert-success">${data.message}</div>`;
            fetchUsers();  // Refresh the user list
        })
        .catch(error => {
            console.error('Error deleting user:', error);
            document.getElementById('message').innerHTML = `<div class="alert alert-danger">Failed to delete user</div>`;
        });
    }

    // Handle form submission for creating a new user
    document.getElementById('createUserForm').addEventListener('submit', function (event) {
        event.preventDefault();

        const formData = {
            name: document.getElementById('name').value,
            email: document.getElementById('email').value,
        };

        fetch('/api/laravelUsers', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(formData)
        })
        .then(response => response.json())
        .then(data => {
            document.getElementById('message').innerHTML = `<div class="alert alert-success">${data.message}</div>`;
            document.getElementById('createUserForm').reset();  // Clear form
            fetchUsers();  // Refresh the user list
        })
        .catch(error => {
            console.error('Error creating user:', error);
            document.getElementById('message').innerHTML = `<div class="alert alert-danger">Failed to create user</div>`;
        });
    });

    // Fetch users on page load
    fetchUsers();
</script>
</body>
</html>

Een laatste belangrijke aanpassing die we moeten maken is aan het Laravel project laten weten dat we extra api endpoint hebben toegevoegd. Dit doe je door de file app/Providers/AppServiceProvider.php aan te passen:

Klik hier om de code te zien/verbergen voor `app/Providers/AppServiceProvider.php`

<?php

namespace App\Providers;


use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));
    }
}

Factory and Seeders

Je kan Laravel nu ook gebruiken om automatisch wat dummy data te genereren in de database. Hierover vind je meer op volgende website: FSWEB of via deze video

In het de opgeloste demo in de student-repository werden ook al factories en seeders voorzien. Die worden dankzij de entrypoint.sh dan ook opgeroepen bij het starten van de container.

Enable CORS

  1. Voeg volgende module toe aan het project: composer require fruitcake/laravel-cors
  2. Pas volgende file aan of maak hem aan als hij nog niet bestaat: config/cors.php
Klik hier om de code te zien/verbergen voor `config/cors.php`

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Laravel CORS Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*'],  // Geeft aan voor welke routes CORS ingeschakeld moet worden

    'allowed_methods' => ['*'], // Toegestane HTTP-methoden zoals GET, POST, PUT, DELETE. '*' staat voor alle methoden

    'allowed_origins' => ['*'], // Toegestane origins. '*' betekent dat elke origin is toegestaan

    'allowed_origins_patterns' => [], // Regex-patronen voor meer specifieke origin matching

    'allowed_headers' => ['*'], // Headers die zijn toegestaan in CORS-verzoeken

    'exposed_headers' => [], // Headers die zichtbaar zijn in de client (bijvoorbeeld 'Authorization')

    'max_age' => 0, // Hoe lang de resultaten van een preflight request worden gecached (in seconden)

    'supports_credentials' => false, // Als je cookies of authorization headers toestaat, zet deze op true

];

  1. Voeg api: __DIR__.'/../routes/api.php', aan de bootstrap/app.php file
Klik hier om de code te zien/verbergen voor `bootstrap/app.php`

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

Opdracht: werk nu je eigen Rest api in Laravel uit en maak een gepaste frontend.