Laravel 8 New Features
Laravel Jetstream
Models Directory
Model Factory Classes
<?php namespace Database\Factories; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; class UserFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = User::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ 'name' => $this->faker->name, 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } }
HasFactory trait available on generated models, the model factory may be used like so:use App\Models\User; User::factory()->count(50)->create();
Since model factories are now simple PHP classes, state transformations may be written as class methods. In addition, you may add any other helper classes to your Eloquent model factory as needed.
For example, your User model might have a suspended state that modifies one of its default attribute values. You may define your state transformations using the base factory's state method. You may name your state method anything you like. After all, it's just a typical PHP method:
/** * Indicate that the user is suspended. * * @return \Illuminate\Database\Eloquent\Factories\Factory */ public function suspended() { return $this->state([ 'account_status' => 'suspended', ]); }After defining the state transformation method, we may use it like so:
use App\Models\User; User::factory()->count(5)->suspended()->create();
As mentioned, Laravel 8's model factories contain first class support for relationships. So, assuming our User model has a posts relationship method, we may simply run the following code to generate a user with three posts:
$users = User::factory() ->hasPosts(3, [ 'published' => false, ]) ->create();
Migration Squashing
php artisan schema:dump // Dump the current database schema and prune all existing migrations... php artisan schema:dump --prune
When you execute this command, Laravel will write a "schema" file to your database/schema directory. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute the schema file's SQL first. After executing the schema file's commands, Laravel will execute any remaining migrations that were not part of the schema dump.
Job Batching
use App\Jobs\ProcessPodcast; use App\Podcast; use Illuminate\Bus\Batch; use Illuminate\Support\Facades\Batch; use Throwable; $batch = Bus::batch([ new ProcessPodcast(Podcast::find(1)), new ProcessPodcast(Podcast::find(2)), new ProcessPodcast(Podcast::find(3)), new ProcessPodcast(Podcast::find(4)), new ProcessPodcast(Podcast::find(5)), ])->then(function (Batch $batch) { // All jobs completed successfully... })->catch(function (Batch $batch, Throwable $e) { // First batch job failure detected... })->finally(function (Batch $batch) { // The batch has finished executing... })->dispatch(); return $batch->id;
Improved Rate Limiting
use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; RateLimiter::for('global', function (Request $request) { return Limit::perMinute(1000); });
RateLimiter::for('uploads', function (Request $request) { return $request->user()->vipCustomer() ? Limit::none() : Limit::perMinute(100); });
Sometimes you may wish to segment rate limits by some arbitrary value. For example, you may wish to allow users to access a given route 100 times per minute per IP address. To accomplish this, you may use the by method when building your rate limit:
RateLimiter::for('uploads', function (Request $request) { return $request->user()->vipCustomer() ? Limit::none() : Limit::perMinute(100)->by($request->ip());});
Rate limiters may be attached to routes or route groups using the throttle middleware. The throttle middleware accepts the name of the rate limiter you wish to assign to the route:
Route::middleware(['throttle:uploads'])->group(function () { Route::post('/audio', function () { // }); Route::post('/video', function () { // }); });
Closure Dispatch / Chain
use Throwable;
dispatch(function () use ($podcast) {$podcast->publish(); })->catch(function (Throwable $e) { // This job has failed... });
Dynamic Blade Components
<x-dynamic-component :component="$componentName" class="mt-4" />