Learn Laravel 8 Api Sqlite Traversy

03 Apr 2022 - nicolas

tuto traversy media

create project

composer create-project laravel/laravel:^8.0 myAppNameHere
cd myAppNameHere
php artisan serve

database SQLite

set .env file:

DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306

create file database/database.sqlite

shell commands:

sed -i 's/DB_CONNECTION=.*/DB_CONNECTION=sqlite/' .env
touch database/database.sqlite

model

create the model and migration files:

php artisan make:model Product --migration

update the migration file in laravel-sanctum-api/database/migrations/2022_09_16_210904_create_products_table.php to add columns to the model:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('slug');
            $table->string('description')->nullable();
            $table->decimal('price', 5, 2);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}

execute the migration to update the database:

php artisan migrate

get requests using routes/api.php

update laravel-sanctum-api/routes/api.php:

<?php

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// endpoint GET /api/products
Route::get('/products', function () {
    return Product::all();
});

post requests routes/api.php

add fillable property to the model for POST requests:

<?php

namespace App\Models;

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

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'slug',
        'description',
        'price',
    ];
}

update laravel-sanctum-api/routes/api.php:

Illuminate\Support\Facades\Route::post('/products', function () {
    return \App\Models\Product::create([
        'name' => 'product name 1',
        'slug' => 'product-slug-1',
        'description' => 'product description 1',
        'price' => '123.45',
    ]);
});

now using request data:

Illuminate\Support\Facades\Route::post('/products', function (Request $request) {
    return \App\Models\Product::create($request->all());
});

now adding validation:

Illuminate\Support\Facades\Route::post('/products', function (Request $request) {
    $request->validate([
        'name' => 'required',
        'slug' => 'required',
        'price' => 'required',
    ]);

    return \App\Models\Product::create($request->all());
});

refactor using a controller

create controller with blank CRUD methods (--api):

php artisan make:controller ProductController --api

refactor routes:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;

Route::get('/products', [ProductController::class, 'index']);
Route::get('/products/{id}', [ProductController::class, 'show']);
Route::post('/products', [ProductController::class, 'store']);

or use the Route::Ressource method:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;

Route::resource('products', ProductController::class);

update the controller file:

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Product::all();
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'slug' => 'required',
            'price' => 'required',
        ]);

        return Product::create($request->all());
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        return Product::find($id);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $product = Product::find($id);
        $product->update($request->all());

        return $product;
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        return Product::destroy($id);
    }
}

add a search endpoint

add route in laravel-sanctum-api/routes/api.php:

Route::get('/products/search/{name}', [ProductController::class, 'search']);

controller:

    /**
     * @param  string $name
     * @return \Illuminate\Http\Response
     */
    public function search($name)
    {
        $product = Product::where('name', 'like', "%{$name}%")->get();

        return Product::where('name', 'like', "%{$name}%")->get();
    }

add sanctum authentication

doc

composer require laravel/sanctum:^2.9 # for laravel 8
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

update laravel-sanctum-api/app/Http/Kernel.php file:

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

protect a route:

Route::group(['middleware' => 'auth:sanctum'], function () {
    Route::get('/easter-egg', function () {
        return 'hello';
    });
});

create register route

create a Controller:

php artisan make:controller AuthController
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $fields = $request->validate([
            'name' => 'required|string',
            'email' => 'required|string|unique:users,email',
            'password' => 'required|string|confirmed',
        ]);

        $user = User::create([
            'name' => $fields['name'],
            'email' => $fields['email'],
            'password' => bcrypt($fields['password']),
        ]);

        $token = $user->createToken('myapptoken')->plainTextToken;

        $response = [
            'user' => $user,
            'token' => $token,
        ];

        return response($response, 201);
    }
}

add a public register route in laravel-sanctum-api/routes/api.php:

Route::post('/register', [AuthController::class, 'register']);

usage:

  1. send a POST request to get a token:

     {
         "name": "lara clette",
         "email": "lara@clette.com",
         "password": "hello",
         "password_confirmation": "hello"
     }
    
  2. add a header in requests on protected routes:

     Authorization Bearer: tokenHere
    

create logout route

update the controller:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    //
    // ...
    //

    public function logout(Request $request)
    {
        auth()->user()->tokens()->delete();

        return [
            'message' => 'Logged out.',
        ];
    }
}

add a protected register route in laravel-sanctum-api/routes/api.php:

Route::post('/logout', [AuthController::class, 'logout']);

create login route

update the controller:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
    //
    // ...
    //

    public function login(Request $request)
    {
        $fields = $request->validate([
            'email' => 'required|string',
            'password' => 'required|string',
        ]);

        // check email
        $user = User::where('email', $fields['email'])->first();

        // check password
        if (null === $user || false === Hash::check($fields['password'], $user->password)) {
            return response([
                'message' => 'Bad creds'
            ], 401);
        }

        $token = $user->createToken('myapptoken')->plainTextToken;

        $response = [
            'user' => $user,
            'token' => $token,
        ];

        return response($response, 201);
    }
}

add a public register route in laravel-sanctum-api/routes/api.php:

Route::post('/logout', [AuthController::class, 'logout']);