Slide 1

Slide 1 text

What's new in Laravel 9

Slide 2

Slide 2 text

About me — ! Christian Leo-Pernold — "#$%&'()*⛰,-. — / @mazedlx — ⭐ github.com/mazedlx — 1 mazedlx.net — 2 gusch.fredl.at 2/40

Slide 3

Slide 3 text

Laravel 9 Released on 2022-02-08 3/40

Slide 4

Slide 4 text

Minimum PHP Version 8.0 4/40

Slide 5

Slide 5 text

Controller Route Groups 5/40

Slide 6

Slide 6 text

// Laravel 8 use App\Http\Controllers\CustomerController; Route::get('customers', [CustomerController::class, 'index']); Route::get('customers/create', [CustomerController::class, 'create']); Route::post('customers', [CustomerController::class, 'store']); Route::get('customers/{customer}', [CustomerController::class, 'show']); Route::get('customers/{customer}/edit', [CustomerController::class, 'edit']); Route::patch('customers/{customer}', [CustomerController::class, 'update']); 6/40

Slide 7

Slide 7 text

// Laravel 9 use App\Http\Controllers\CustomerController; Route::controller(CustomerController::class)->group(function() { Route::get('customers', 'index'); Route::get('customers/create', 'create'); Route::post('customers', 'store'); Route::get('customers/{customer}', 'show'); Route::get('customers/{customer}/edit', 'edit'); Route::patch('customers/{customer}', 'update'); }); 7/40

Slide 8

Slide 8 text

Be!er php artisan route:list 8/40

Slide 9

Slide 9 text

// Laravel 8 $ php artisan route:list +--------+----------+----------------------------------+-----------------------+------------------------------------------------------+---------------------------------------------------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------+-----------------------+------------------------------------------------------+---------------------------------------------------------+ | | GET|HEAD | / | episodes.index | App\Http\Livewire\Episodes\Index | web | | | GET|HEAD | backstage | backstage | App\Http\Livewire\Backstage | web | | | | | | | App\Http\Middleware\Authenticate | | | | | | | Spatie\ResponseCache\Middlewares\DoNotCacheResponse | | | GET|HEAD | episodes/create | episodes.create | App\Http\Livewire\Episodes\Create | web | | | | | | | App\Http\Middleware\Authenticate | | | | | | | Spatie\ResponseCache\Middlewares\DoNotCacheResponse | | | | | | | Illuminate\Auth\Middleware\Authorize:create,App\Episode | | | GET|HEAD | episodes/{episode}/edit | episodes.edit | App\Http\Livewire\Episodes\Edit | web | +--------+----------+----------------------------------+-----------------------+------------------------------------------------------+---------------------------------------------------------+ 9/40

Slide 10

Slide 10 text

// Laravel 8 $ php artisan route:list +--------+----------+----------------------------------+-----------------------+------------------------------------------------------ +---------------------------------------------------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------+-----------------------+------------------------------------------------------ +---------------------------------------------------------+ | | GET|HEAD | / | episodes.index | App\Http\Livewire\Episodes\Index | web | | | GET|HEAD | backstage | backstage | App\Http\Livewire\Backstage | web | | | | | | | App\Http\Middleware\Authenticate | | | | | | | Spatie\ResponseCache\Middlewares\DoNotCacheResponse | | | GET|HEAD | episodes/create | episodes.create | App\Http\Livewire\Episodes\Create | web | | | | | | | App\Http\Middleware\Authenticate | | | | | | | Spatie\ResponseCache\Middlewares\DoNotCacheResponse | | | | | | | Illuminate\Auth\Middleware\Authorize:create,App\Episode | | | GET|HEAD | episodes/{episode}/edit | episodes.edit | App\Http\Livewire\Episodes\Edit | web | +--------+----------+----------------------------------+-----------------------+------------------------------------------------------ +---------------------------------------------------------+ 10/40

Slide 11

Slide 11 text

// Laravel 8 $ php artisan route:list -c +----------+----------------------------------+------------------------------------------------------+ | Method | URI | Action | +----------+----------------------------------+------------------------------------------------------+ | GET|HEAD | / | App\Http\Livewire\Episodes\Index | | GET|HEAD | backstage | App\Http\Livewire\Backstage | | GET|HEAD | episodes/create | App\Http\Livewire\Episodes\Create | | GET|HEAD | episodes/{episode}/edit | App\Http\Livewire\Episodes\Edit | | GET|HEAD | feed | App\Http\Controllers\FeedController | | GET|HEAD | livewire/livewire.js | Livewire\Controllers\LivewireJavaScriptAssets@source | | GET|HEAD | livewire/livewire.js.map | Livewire\Controllers\LivewireJavaScriptAssets@maps | | POST | livewire/message/{name} | Livewire\Controllers\HttpConnectionHandler | | GET|HEAD | livewire/preview-file/{filename} | Livewire\Controllers\FilePreviewHandler@handle | | POST | livewire/upload-file | Livewire\Controllers\FileUploadHandler@handle | | GET|HEAD | login | App\Http\Livewire\Auth\Login | | GET|HEAD | preview/{episode} | App\Http\Livewire\Episodes\Index | | GET|HEAD | {episode} | App\Http\Livewire\Episodes\Index | +----------+----------------------------------+------------------------------------------------------+ 11/40

Slide 12

Slide 12 text

// Laravel 9 $ php artisan route:list GET|HEAD posts ................................................ posts.index › PostController@index POST posts ................................................ posts.store › PostController@store GET|HEAD posts/create ....................................... posts.create › PostController@create GET|HEAD posts/{post} ........................................... posts.show › PostController@show PATCH posts/{post} ....................................... posts.update › PostController@update GET|HEAD posts/{post}/edit ...................................... posts.edit › PostController@edit POST posts/{post}/pin .......................................... posts.pin › PinPostController POST posts/{post}/publish .............................. posts.publish › PublishPostController POST posts/{post}/unpin .................................... posts.unpin › UnpinPostController 12/40

Slide 13

Slide 13 text

Anonymous Migration Classes 13/40

Slide 14

Slide 14 text

use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('tablename', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } public function down() { Schema::dropIfExists('tablename'); } }; 14/40

Slide 15

Slide 15 text

New Helper Functions 15/40

Slide 16

Slide 16 text

// str() - alias for Illuminate\Support\Str::of($string) str('hello')->append(' world!); // to_route() - equivalent to redirect()->route($route) return to_route('home'); 16/40

Slide 17

Slide 17 text

Refreshed Ignition Error Pages 17/40

Slide 18

Slide 18 text

18/40

Slide 19

Slide 19 text

19/40

Slide 20

Slide 20 text

20/40

Slide 21

Slide 21 text

Yes, of course there is a Dark Mode 21/40

Slide 22

Slide 22 text

22/40

Slide 23

Slide 23 text

Render Blade String 23/40

Slide 24

Slide 24 text

Route::get('/', function () { return Blade::render('{{ $greeting }}, World!', [ 'greeting' => 'Hello', ]); }); 24/40

Slide 25

Slide 25 text

Forced Scope Bindings 25/40

Slide 26

Slide 26 text

// Scopes the second Eloquent model // that it must be a child of the first Eleoquent model Route::get('users/{user}/posts/{post}', function (User $user, Post $post) { return $post; })->scopeBindings(); 26/40

Slide 27

Slide 27 text

Test Coverage 27/40

Slide 28

Slide 28 text

$ php artisan test --coverage Console/Kernel ............................................................................ 100.0 % Exceptions/Handler ........................................................................ 100.0 % Http/Controllers/Controller ............................................................... 100.0 % Http/Kernel ............................................................................... 100.0 % Http/Middleware/Authenticate ................................................................. 0.0 % Http/Middleware/EncryptCookies ............................................................ 100.0 % Http/Middleware/PreventRequestsDuringMaintenance .......................................... 100.0 % Http/Middleware/RedirectIfAuthenticated ...................................................... 0.0 % Http/Middleware/TrimStrings ............................................................... 100.0 % Http/Middleware/TrustHosts ................................................................... 0.0 % Http/Middleware/TrustProxies .............................................................. 100.0 % Http/Middleware/VerifyCsrfToken ........................................................... 100.0 % Models/User ............................................................................... 100.0 % Providers/AppServiceProvider .............................................................. 100.0 % Providers/AuthServiceProvider ............................................................. 100.0 % Providers/BroadcastServiceProvider ........................................................... 0.0 % Providers/EventServiceProvider ............................................................ 100.0 % Providers/RouteServiceProvider 51 ........................................................... 90.9 % Total Coverage .............................................................................. 57.7 % 28/40

Slide 29

Slide 29 text

$ php artisan test --coverage --min=90 Console/Kernel ............................................................................ 100.0 % Exceptions/Handler ........................................................................ 100.0 % Http/Controllers/Controller ............................................................... 100.0 % Http/Kernel ............................................................................... 100.0 % Http/Middleware/Authenticate ................................................................. 0.0 % Http/Middleware/EncryptCookies ............................................................ 100.0 % Http/Middleware/PreventRequestsDuringMaintenance .......................................... 100.0 % Http/Middleware/RedirectIfAuthenticated ...................................................... 0.0 % Http/Middleware/TrimStrings ............................................................... 100.0 % Http/Middleware/TrustHosts ................................................................... 0.0 % Http/Middleware/TrustProxies .............................................................. 100.0 % Http/Middleware/VerifyCsrfToken ........................................................... 100.0 % Models/User ............................................................................... 100.0 % Providers/AppServiceProvider .............................................................. 100.0 % Providers/AuthServiceProvider ............................................................. 100.0 % Providers/BroadcastServiceProvider ........................................................... 0.0 % Providers/EventServiceProvider ............................................................ 100.0 % Providers/RouteServiceProvider 51 ........................................................... 90.9 % Total Coverage .............................................................................. 57.7 % FAIL Code coverage below expected: 57.7 %. Minimum: 90.0 %. 29/40

Slide 30

Slide 30 text

Laravel Scout Database Engine 30/40

Slide 31

Slide 31 text

No need for Algolia or MeiliSearch* * depending on how many records you have 31/40

Slide 32

Slide 32 text

Full Text Indexing for MySQL and PostgreSQL 32/40

Slide 33

Slide 33 text

Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body')->fulltext(); $table->timestamps(); }); Post::whereFullText('body', 'search')->get(); 33/40

Slide 34

Slide 34 text

Enum A!ribute Casting 34/40

Slide 35

Slide 35 text

enum PostState: string { case Draft = 'draft'; case Published = 'published'; case Archived = 'archived'; } Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body')->fulltext(); //$table->enum('state', ['draft', 'published', 'archived']); $table->string('state')->default('draft'); $table->timestamps(); }); 35/40

Slide 36

Slide 36 text

class Post extends Model { protected $casts = [ 'state' => PostState::class, ]; } 36/40

Slide 37

Slide 37 text

Simplified Accessors and Mutators 37/40

Slide 38

Slide 38 text

// Laravel 8 class User extends Model { public function getFirstNameAttribute() { return ucfirst($this->first_name); } public function setFirstNameAttribute($value) { $this->attributes['first_name'] = strtolower($value); } } 38/40

Slide 39

Slide 39 text

// Laravel 9 use Illuminate\Database\Eloquent\Casts\Attribute; class User extends Model { public function firstName(): Attribute { return new Attribute( get: fn ($value) => ucfirst($value), set: fn ($value) => strtolower($value), ); } } 39/40

Slide 40

Slide 40 text

40/40