Foreword: vue-router handover is different from traditional page handover. The switching between routes is actually the switching between components, not the real page switching. This will also lead to a problem, that is, when referring to the same component, it will lead to the component can not be updated, that is, the page in our mouth can not be updated.
I. Problem Presentation
Switching results in routing
At this time, it will be found that the value of the input tag does not change with the change of routing. No updates
II. Solutions
Add a different key value to <router-view:key="key"> </router-view> so that vue recognizes that this is a different <router-view>.
Switching results in routing
At this point, the routing will be updated. But this also means that each < router-view > needs to be bound to a key value. If I jump from page1 to page2 with different components, I really don't have to worry about component updates.
3. Solutions 2
Add a different V-IF value to <router-view v-if="router Alive"> </router-view> to destroy <router-link> and then recreate <router-link> to refresh the page.
Because router-link components have cancel click events, here. native is to trigger events in the component's native tag.
(2) The usage of this. $nextTick (()=> {}) is to wait for this.routerAlive = false; to execute this.routerAlive = true after triggering; thus to destroy and recreate.
Fourth, the extension of solution 2 triggers the refresh of routing within the routing.
Scheme 1, Scheme 2 updates the routing through the outside of the routing. What if you want to update the routing from the inside of the routing?
(1) When we click "jump to nick, do not refresh the routing", we will find that the value of input has not changed.
(2) When we click "jump to nick and refresh the routing", the value of input has changed.
(3) When we enter some values randomly in the input, and then click "jump to itself, do not refresh the routing", we will find that the contents of the input are not refreshed.
(4) Click refresh itself can trigger refresh routing, is it very practical?
<template><div><!-- 1. Create the button that will be clicked to select a file --><v-btncolor="primary"roundeddark:loading="isSelecting"@click="handleFileImport">
Upload File
</v-btn><!-- Create a File Input that will be hidden but triggered with JavaScript --><inputref="uploader"class="d-none"type="file"@change="onFileChanged"></div></template><script>exportdefault{
name:'Example',data(){return{
isSelecting:false,
selectedFile:null}},
methods:{handleFileImport(){this.isSelecting=true;// After obtaining the focus when closing the FilePicker, return the button state to normalwindow.addEventListener('focus',()=>{this.isSelecting=false},{ once:true});// Trigger click on the FileInputthis.$refs.uploader.click();},onFileChanged(e){this.selectedFile= e.target.files[0];// Do whatever you need with the file, liek reading it with FileReader},}}
Typically, we need to query multiple time from a filtered query. So, most of the time we use query() method,
let's write a query for getting today created active and inactive products
$query = Product::query();
$today = request()->q_date ?? today();
if($today){
$query->where('created_at', $today);
}
// lets get active and inactive products$active_products = $query->where('status', 1)->get(); // this line modified the $query object variable$inactive_products = $query->where('status', 0)->get(); // so here we will not find any inactive products
But, after getting $active products the$querywill be modified. So, $inactive_products will not find any inactive products from $query and that will return blank collection every time. Cause, that will try to find inactive products from $active_products ($query will return active products only).
For solve this issue, we can query multiple time by reusing this$queryobject. So, We need to clone this$querybefore doing any$querymodification action.
$active_products = (clone $query)->where('status', 1)->get(); // it will not modify the $query
Typically, we need to query multiple time from a filtered query. So, most of the time we use query() method,
let's write a query for getting today created active and inactive products
$query = Product::query();
$today = request()->q_date ?? today();
if($today){
$query->where('created_at', $today);
}
// lets get active and inactive products$active_products = $query->where('status', 1)->get(); // this line modified the $query object variable$inactive_products = $query->where('status', 0)->get(); // so here we will not find any inactive products
But, after getting $active products the$querywill be modified. So, $inactive_products will not find any inactive products from $query and that will return blank collection every time. Cause, that will try to find inactive products from $active_products ($query will return active products only).
For solve this issue, we can query multiple time by reusing this $query object. So, We need to clone this $query before doing any $query modification action.
$active_products = (clone $query)->where('status', 1)->get(); // it will not modify the $query$inactive_products = (clone $query)->where('status', 0)->get(); // so we will get inactive products from $query
Eloquent where date methods
In Eloquent, check the date with functions whereDay(), whereMonth(), whereYear(), whereDate() and whereTime().
If you want to increment some DB column in some table, just use increment() function. Oh, and you can increment not only by 1, but also by some number, like 50.
If your DB table doesn't contain timestamp fields created_at and updated_at, you can specify that Eloquent model wouldn't use them, with $timestamps = false property.
What if you’re working with non-Laravel database and your timestamp columns are named differently? Maybe, you have create_time and update_time. Luckily, you can specify them in the model, too:
If you want to generate some DB column value when creating record, add it to model's boot() method. For example, if you have a field "position" and want to assign the next available position to the new record (like Country::max('position') + 1), do this:
Use SQL raw queries like whereRaw() method, to make some DB-specific calculations directly in query, and not in Laravel, usually the result will be faster. Like, if you want to get users that were active 30+ days after their registration, here's the code:
If you're performing whereDate() and check today's records, you can use Carbon's now() and it will automatically be transformed to date. No need to do ->toDateString().
// Instead of$todayUsers = User::whereDate('created_at', now()->toDateString())->get();
// No need to convert, just use now()$todayUsers = User::whereDate('created_at', now())->get();
Grouping by First Letter
You can group Eloquent results by any custom condition, here's how to group by first letter of user's name:
Eloquent method find() may accept multiple parameters, and then it returns a Collection of all records found with specificied columns, not all columns of model:
// Will return Eloquent Model with first_name and email only$user = User::find(1, ['first_name', 'email']);
// Will return Eloquent Collection with first_name and email only$users = User::find([1,2,3], ['first_name', 'email']);
You can also find multiple records with whereKey() method which takes care of which field is exactly your primary key (id is the default but you may override it in Eloquent model):
$users = User::whereKey([1,2,3])->get();
Use UUID instead of auto-increment
You don't want to use auto incrementing ID in your model?
Migration:
Schema::create('users', function (Blueprint$table) {
// $table->increments('id');$table->uuid('id')->unique();
});
When doing Eloquent query, if you want to hide specific field from being returned, one of the quickest ways is to add ->makeHidden() on Collection result.
If you want to catch Eloquent Query exceptions, use specific QueryException instead default Exception class, and you will be able to get the exact SQL code of the error.
Don't forget that soft-deletes will exclude entries when you use Eloquent, but won't work if you use Query Builder.
// Will exclude soft-deleted entries$users = User::all();
// Will NOT exclude soft-deleted entries$users = User::withTrashed()->get();
// Will NOT exclude soft-deleted entries$users = DB::table('users')->get();
Good Old SQL Query
If you need to execute a simple SQL query, without getting any results - like changing something in DB schema, you can just do DB::statement().
If you need to check if the record exists, and then update it, or create a new record otherwise, you can do it in one sentence - use Eloquent method updateOrCreate():
If you have cache key like posts that gives collection, and you want to forget that cache key on new store or update, you can call static saved function on your model:
classPostextendsModel
{
// Forget cache key on storing or updatingpublicstaticfunctionboot()
{
parent::boot();
static::saved(function () {
Cache::forget('posts');
});
}
}
If you have input field which takes an array and you have to store it as a JSON, you can use $casts property in your model. Here images is a JSON attribute.
protected$casts = [
'images' => 'array',
];
So you can store it as a JSON, but when retrieved from DB, it can be used as an array.
Make a Copy of the Model
If you have two very similar Models (like shipping address and billing address) and you need to make a copy of one to another, you can use replicate() method and change some properties after that.
Sometimes we need to load a huge amount of data into memory. For example:
$orders = Order::all();
But this can be slow if we have really huge data, because Laravel prepares objects of the Model class. In such cases, Laravel has a handy function toBase()
$orders = Order::toBase()->get();
//$orders will contain `Illuminate\Support\Collection` with objects `StdClass`.
By calling this method, it will fetch the data from the database, but it will not prepare the Model class. Keep in mind it is often a good idea to pass an array of fields to the get method, preventing all fields to be fetched from the database.
Force query without $fillable/$guarded
If you create a Laravel boilerplate as a "starter" for other devs, and you're not in control of what THEY would later fill in Model's $fillable/$guarded, you may use forceFill()
$team->update(['name' => $request->name])
What if "name" is not in Team model's $fillable? Or what if there's no $fillable/$guarded at all?
$team->forceFill(['name' => $request->name])
This will "ignore" the $fillable for that one query and will execute no matter what.
3-level structure of parent-children
If you have a 3-level structure of parent-children, like categories in an e-shop, and you want to show the number of products on the third level, you can use with('yyy.yyy') and then add withCount() as a condition
When looking for a record, you may want to perform some actions if it's not found. In addition to ->firstOrFail() which just throws 404, you can perform any action on failure, just do ->firstOr(function() { ... })
// This worksif ( 0 === $model->where('status', 'pending')->count() ) {
}
// But since I don't care about the count, just that there isn't one// Laravel's exists() method is cleaner.if ( ! $model->where('status', 'pending')->exists() ) {
}
// But I find the ! in the statement above easily missed. The// doesntExist() method makes this statement even clearer.if ( $model->where('status', 'pending')->doesntExist() ) {
}
traitMultiTenantModelTrait
{
// This method's name is boot[TraitName]// It will be auto-called as boot() of Transaction/TaskpublicstaticfunctionbootMultiTenantModelTrait()
{
static::creating(function ($model) {
if (!$isAdmin) {
$isAdmin->created_by_id = auth()->id();
}
})
}
}
There are two common ways of determining if a table is empty in Laravel
There are two common ways of determining if a table is empty in Laravel. Calling exists() or count() directly on the model! One returns a strict true/false boolean, the other returns an integer which you can use as a falsy in conditionals.
publicfunctionindex()
{
if (\App\Models\User::exists()) {
// returns boolean true or false if the table has any saved rows
}
if (\App\Models\User::count()) {
// returns the count of rows in the table
}
}
// BelongsTo Default Models// Let's say you have Post belonging to Author and then Blade code:$post->author->name;
// Of course, you can prevent it like this:$post->author->name ?? ''// or
@$post->author->name// But you can do it on Eloquent relationship level:// this relation will return an empty App\Author model if no author is attached to the postpublicfunction author() {
return $this->belongsTo('App\Author')->withDefault();
}
// orpublicfunction author() {
return $this->belongsTo('App\Author')->withDefault([
'name' => 'Guest Author'
]);
}
The sole() method will return only one record that matches the criteria. If no such entry is found, then a NoRecordsFoundException will be thrown. If multiple records are found, then a MultipleRecordsFoundException will be thrown.
Sometimes you need to update the model without sending any events. We can now do this with the updateQuietly() method, which under the hood uses the saveQuietly() method.
To periodically clean models of obsolete records. With this trait, Laravel will do this automatically, only you need to adjust the frequency of the model:prune command in the Kernel class.
useIlluminate\Database\Eloquent\Model;
useIlluminate\Database\Eloquent\Prunable;
classFlightextendsModel
{
usePrunable;
/** * Get the prunable model query. * * @return \Illuminate\Database\Eloquent\Builder */publicfunctionprunable()
{
returnstatic::where('created_at', '<=', now()->subMonth());
}
}
Also, in the pruning method, you can set the actions that must be performed before deleting the model:
protectedfunctionpruning()
{
// Removing additional resources,// associated with the model. For example, files.Storage::disk('s3')->delete($this->filename);
}
The findOrFail method also accepts a list of ids. If any of these ids are not found, then it "fails". Nice if you need to retrieve a specific set of models and don't want to have to check that the count you got was the count you expected
User::create(['id' => 1]);
User::create(['id' => 2);
User::create(['id' => 3]);
// Retrives the user...$user = User::findOrFail(1);
// Throws a 404 because the user doesn't exist...User::findOrFail(99);
// Retrives all 3 users...$users = User::findOrFail([1, 2, 3]);
// Throws because it is unable to find *all* of the usersUser::findOrFail([1, 2, 3, 99]);
Prunable trait to automatically remove models from your database
New in Laravel 8.50: You can use the Prunable trait to automatically remove models from your database. For example, you can permanently remove soft deleted models after a few days.
classFileextendsModel
{
useSoftDeletes;
// Add Prunable traitusePrunable;
publicfunctionprunable()
{
// Files matching this query will be prunedreturnstatic::query()->where('deleted_at', '<=', now()->subDays(14));
}
protectedfunctionpruning()
{
// Remove the file from s3 before deleting the modelStorage::disk('s3')->delete($this->filename);
}
}
// Add PruneCommand to your schedule (app/Console/Kernel.php)$schedule->command(PruneCommand::class)->daily();
Under the hood, the withAvg/withCount/withSum and other methods in Eloquent use the 'withAggregate' method. You can use this method to add a subselect based on a relationship
// Eloquent ModelclassPostextendsModel
{
publicfunctionuser()
{
return$this->belongsTo(User::class);
}
}
// Instead of eager loading all users...$posts = Post::with('user')->get();
// You can add a subselect to only retrieve the user's name...$posts = Post::withAggregate('user', 'name')->get();
// This will add a 'user_name' attribute to the Post instance:$posts->first()->user_name;
Using the something_at convention instead of just a boolean in Laravel models gives you visibility into when a flag was changed – like when a product went live.
// MigrationSchema::table('products', function (Blueprint$table) {
$table->datetime('live_at')->nullable();
});
// In your modelpublicfunctionlive()
{
return !is_null($this->live_at);
}
// Also in your modelprotected$dates = [
'live_at'
];
Retrieve the Query Builder after filtering the results
To retrieve the Query Builder after filtering the results: you can use ->toQuery(). The method internally use the first model of the collection and a whereKey comparison on the Collection models.
// Retrieve all logged_in users$loggedInUsers = User::where('logged_in', true)->get();
// Filter them using a Collection method or php filtering$nthUsers = $loggedInUsers->nth(3);
// You can't do this on the collection$nthUsers->update(/* ... */);
// But you can retrieve the Builder using ->toQuery()if ($nthUsers->isNotEmpty()) {
$nthUsers->toQuery()->update(/* ... */);
}
You can create custom casts to have Laravel automatically format your Eloquent model data. Here's an example that capitalises a user's name when it is retrieved or changed.
If you have a DB transaction and want to return its result, there are at least two ways, see the example
// 1. You can pass the parameter by reference$invoice = NULL;
DB::transaction(function () use (&$invoice) {
$invoice = Invoice::create(...);
$invoice->items()->attach(...);
})
// 2. Or shorter: just return trasaction result$invoice = DB::transaction(function () {
$invoice = Invoice::create(...);
$invoice->items()->attach(...);
return$invoice;
});
Remove several global scopes from query
When using Eloquent Global Scopes, you not only can use MULTIPLE scopes, but also remove certain scopes when you don't need them, by providing the array to withoutGlobalScopes() Link to docs
// Remove all of the global scopes...User::withoutGlobalScopes()->get();
// Remove some of the global scopes...User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
Order JSON column attribute
With Eloquent you can order results by a JSON column attribute
You can use value() method to get single column's value from the first result of a query
// Instead ofIntegration::where('name', 'foo')->first()->active;
// You can useIntegration::where('name', 'foo')->value('active');
// or this to throw an exception if no records foundIntegration::where('name', 'foo')->valueOrFail('active')';
You can assign a default model in belongsTo relationship, to avoid fatal errors when calling it like {{ $post->user->name }} if $post->user doesn't exist.
In Laravel you can Eager Load multiple levels in one statement, in this example we not only load the author relation but also the country relation on the author model.
$users = App\Book::with('author.country')->get();
Eager Loading with Exact Columns
You can do Laravel Eager Loading and specify the exact columns you want to get from the relationship.
$users = App\Book::with('author:id,name')->get();
You can do that even in deeper, second level relationships:
If you are updating a record and want to update the updated_at column of parent relationship (like, you add new post comment and want posts.updated_at to renew), just use $touches = ['post']; property on child model.
Never ever do $model->relationship->field without checking if relationship object still exists.
It may be deleted for whatever reason, outside your code, by someone else's queued job etc. Do if-else, or {{ $model->relationship->field ?? '' }} in Blade, or {{ optional($model->relationship)->field }}. With php8 you can even use the nullsafe operator {{ $model->relationship?->field) }}
Use withCount() to Calculate Child Relationships Records
If you have hasMany() relationship, and you want to calculate “children” entries, don’t write a special query. For example, if you have posts and comments on your User model, write this withCount():
If you want to load relationship data, you can specify some limitations or ordering in a closure function. For example, if you want to get Countries with only three of their biggest cities, here's the code.
For belongsTo relationship, instead of passing parent's ID when creating child record, use hasMany relationship to make a shorter sentence.
// if Post -> belongsTo(User), and User -> hasMany(Post)...// Then instead of passing user_id...Post::create([
'user_id' => auth()->id(),
'title' => request()->input('title'),
'post_text' => request()->input('post_text'),
]);
// Do thisauth()->user()->posts()->create([
'title' => request()->input('title'),
'post_text' => request()->input('post_text'),
]);
Rename Pivot Table
If you want to rename "pivot" word and call your relationship something else, you just use ->as('name') in your relationship.
If you have a belongsTo() relationship, you can update the Eloquent relationship data in the same sentence:
// if Project -> belongsTo(User::class)$project->user->update(['email' => 'some@gmail.com']);
Laravel 7+ Foreign Keys
From Laravel 7, in migrations you don't need to write two lines for relationship field - one for the field and one for foreign key. Use method foreignId().
// Before Laravel 7Schema::table('posts', function (Blueprint$table)) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
}
// From Laravel 7Schema::table('posts', function (Blueprint$table)) {
$table->foreignId('user_id')->constrained();
}
// Or, if your field is different from the table referenceSchema::table('posts', function (Blueprint$table)) {
$table->foreignId('created_by_id')->constrained('users', 'column');
}
Combine Two "whereHas"
In Eloquent, you can combine whereHas() and orDoesntHave() in one sentence.
If your Eloquent relationship names are dynamic and you need to check if relationship with such name exists on the object, use PHP function method_exists($object, $methodName)
$user = User::first();
if (method_exists($user, 'roles')) {
// Do something with $user->roles()->...
}
Pivot Table with Extra Relations
In many-to-many relationship, your pivot table may contain extra fields, and even extra relationships to other Model.
Then generate a separate Pivot Model:
php artisan make:model RoleUser --pivot
Next, specify it in belongsToMany() with ->using() method. Then you could do magic, like in the example.
// in app/Models/User.phppublicfunctionroles()
{
return$this->belongsToMany(Role::class)
->using(RoleUser::class)
->withPivot(['team_id']);
}
// app/Models/RoleUser.php: notice extends Pivot, not ModeluseIlluminate\Database\Eloquent\Relations\Pivot;
classRoleUserextendsPivot
{
publicfunctionteam()
{
return$this->belongsTo(Team::class);
}
}
// Then, in Controller, you can do:$firstTeam = auth()->user()->roles()->first()->pivot->team->name;
Load Count on-the-fly
In addition to Eloquent's withCount() method to count related records, you can also load the count on-the-fly, with loadCount():
// if your Book hasMany Reviews...$book = App\Book::first();
$book->loadCount('reviews');
// Then you get access to $book->reviews_count;// Or even with extra condition$book->loadCount(['reviews' => function ($query) {
$query->where('rating', 5);
}]);
Randomize Relationship Order
You can use inRandomOrder() to randomize Eloquent query result, but also you can use it to randomize the relationship entries you're loading with query.
// If you have a quiz and want to randomize questions...// 1. If you want to get questions in random order:$questions = Question::inRandomOrder()->get();
// 2. If you want to also get question options in random order:$questions = Question::with(['answers' => function($q) {
$q->inRandomOrder();
}])->inRandomOrder()->get();
Filter hasMany relationships
Just a code example from my project, showing the possibility of filtering hasMany relationships. TagTypes -> hasMany Tags -> hasMany Examples And you wanna query all the types, with their tags, but only those that have examples, ordering by most examples.
New whereBelongsTo() Eloquent query builder method
Laravel 8.63.0 ships with a new whereBelongsTo() Eloquent query builder method. Smiling face with heart-shaped eyes This allows you to remove BelongsTo foreign key names from your queries, and use the relationship method as a single source of truth instead!
The is() method of one-to-one relationships for comparing models
We can now make comparisons between related models without further database access.
// BEFORE: the foreign key is taken from the Post model$post->author_id === $user->id;
// BEFORE: An additional request is made to get the User model from the Author relationship$post->author->is($user);
// AFTER$post->author()->is($user);
If you want to update an existing pivot record on the table, use updateExistingPivot instead of syncWithPivotValues.
// MigrationsSchema::create('role_user', function ($table) {
$table->unsignedId('user_id');
$table->unsignedId('role_id');
$table->timestamp('assigned_at');
})
// first param for the record id// second param for the pivot records$user->roles()->updateExistingPivot(
$id, ['assigned_at' => now()],
);
For foreign key migrations instead of integer() use unsignedInteger() type or integer()->unsigned(), otherwise you may get SQL errors.
Schema::create('employees', function (Blueprint$table) {
$table->unsignedInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies');
// ...
});
You can also use unsignedBigInteger() if that other column is bigInteger() type.
Schema::create('employees', function (Blueprint$table) {
$table->unsignedBigInteger('company_id');
});
Order of Migrations
If you want to change the order of DB migrations, just rename the file's timestamp, like from 2018_08_04_070443_create_posts_table.php to2018_07_04_070443_create_posts_table.php (changed from 2018_08_04 to 2018_07_04).
They run in alphabetical order.
Migration fields with timezones
Did you know that in migrations there's not only timestamps() but also timestampsTz(), for the timezone?
Schema::create('employees', function (Blueprint$table) {
$table->increments('id');
$table->string('name');
$table->string('email');
$table->timestampsTz();
});
Also, there are columns dateTimeTz(), timeTz(), timestampTz(), softDeletesTz().
Database migrations column types
There are interesting column types for migrations, here are a few examples.
While creating migrations, you can use timestamp() column type with option useCurrent() and useCurrentOnUpdate(), it will set CURRENT_TIMESTAMP as default value.
If you want to check what migrations are executed or not yet, no need to look at the database "migrations" table, you can launch php artisan migrate:status command.
When typing make:migration command, you don't necessarily have to use underscore _ symbol between parts, like create_transactions_table. You can put the name into quotes and then use spaces instead of underscores.
// This works
php artisan make:migration create_transactions_table
// But this works too
php artisan make:migration "create transactions table"
If you're adding a new column to the existing table, it doesn't necessarily have to become the last in the list. You can specify after which column it should be created:
Schema::table('users', function (Blueprint$table) {
$table->string('phone')->after('email');
});
If you're adding a new column to the existing table, it doesn't necessarily have to become the last in the list. You can specify before which column it should be created:
Schema::table('users', function (Blueprint$table) {
$table->string('phone')->before('created_at');
});
If you want your column to be the first in your table , then use the first method.
Schema::table('users', function (Blueprint$table) {
$table->string('uuid')->first();
});
Also the after() method can now be used to add multiple fields.
Schema::table('users', function (Blueprint$table) {
$table->after('remember_token', function ($table){
$table->string('card_brand')->nullable();
$table->string('card_last_four', 4)->nullable();
});
});
Make migration for existing table
If you make a migration for existing table, and you want Laravel to generate the Schema::table() for you, then add "_in_xxxxx_table" or "_to_xxxxx_table" at the end, or specify "--table" parameter. php artisan change_fields_products_table generates empty class
When typing migrate --pretend command, you get the SQL query that will be executed in the terminal. It's an interesting way to debug SQL if necessary.
// Artisan command
php artisan migrate --pretend
Anonymous Migrations
The Laravel team released Laravel 8.37 with anonymous migration support, which solves a GitHub issue with migration class name collisions. The core of the problem is that if multiple migrations have the same class name, it'll cause issues when trying to recreate the database from scratch. Here's an example from the pull request tests:
You can add "comment" about a column inside your migrations
You can add "comment" about a column inside your migrations and provide useful information. If database is managed by someone other than developers, they can look at comments in Table structure before performing any operations.
$table->unsignedInteger('interval')
->index()
->comment('This column is used for indexing.')
You may check for the existence of a table or column using the hasTable and hasColumn methods:
if (Schema::hasTable('users')) {
// The "users" table exists...
}
if (Schema::hasColumn('users', 'email')) {
// The "users" table exists and has an "email" column...
}
Inside of foreach loop, check if current entry is first/last by just using $loop variable.
@foreach ($usersas$user)
@if ($loop->first)
This is the first iteration.
@endif@if ($loop->last)
This is the last iteration.
@endif
<p>This is user {{$user->id}}</p>
@endforeach
There are also other properties like $loop->iteration or $loop->count. Learn more on the official documentation.
Does view file exist?
You can check if View file exists before actually loading it.
if (view()->exists('custom.page')) {
// Load the view
}
You can even load an array of views and only the first existing will be actually loaded.
If you want to create a specific error page for some HTTP code, like 500 - just create a blade file with this code as filename, in resources/views/errors/500.blade.php, or 403.blade.php etc, and it will automatically be loaded in case of that error code.
View without controllers
If you want route to just show a certain view, don't create a Controller method, just use Route::view() function.
// Instead of thisRoute::get('about', 'TextsController@about');
// And thisclassTextsControllerextendsController
{
publicfunctionabout()
{
returnview('texts.about');
}
}
// Do thisRoute::view('about', 'texts.about');
Blade @auth
Instead of if-statement to check logged in user, use @auth directive.
Typical way:
@if(auth()->user())
// The user is authenticated.
@endif
Shorter:
@auth
// The user is authenticated.
@endauth
The opposite is @guest directive:
@guest
// The user is not authenticated.
@endguest
Two-level $loop variable in Blade
In Blade's foreach you can use $loop variable even in two-level loop to reach parent variable.
@foreach ($usersas$user)
@foreach ($user->postsas$post)
@if ($loop->parent->first)
This is first iteration of the parent loop.
@endif@endforeach@endforeach
Create Your Own Blade Directive
It’s very easy - just add your own method in app/Providers/AppServiceProvider.php. For example, if you want to have this for replace <br> tags with new lines:
<textarea>@br2nl($post->post_text)</textarea>
Add this directive to AppServiceProvider’s boot() method:
Use Laravel Blade-X variable binding to save even more space
// Using include, the old way
@include("components.post", ["title" => $post->title])
// Using Blade-X
<x-post link="{{ $post->title }}" />
// Using Blade-X variable binding
<x-post :link="$post->title" />
Automatically highlight nav links when exact URL matches, or pass a path or route name pattern. A Blade component with request and CSS classes helpers makes it ridiculously simple to show active/inactive state.
You can do Route model binding like Route::get('api/users/{user}', function (App\User $user) { … } - but not only by ID field. If you want {user} to be a username field, put this in the model:
If you want to specify additional logic for not-found routes, instead of just throwing default 404 page, you may create a special Route for that, at the very end of your Routes file.
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
Route::get('/home', 'HomeController@index');
Route::resource('tasks', 'Admin\TasksController');
});
// Some more routes....Route::fallback(function() {
return'Hm, why did you land here somehow?';
});
Route Parameters Validation with RegExp
We can validate parameters directly in the route, with “where” parameter. A pretty typical case is to prefix your routes by language locale, like fr/blog and en/article/333. How do we ensure that those two first letters are not used for some other than language?
If you have a set of routes related to a certain "section", you may separate them in a special routes/XXXXX.php file, and just include it in routes/web.php
Example with routes/auth.php in Laravel Breeze by Taylor Otwell himself:
Route::get('/', function () {
returnview('welcome');
});
Route::get('/dashboard', function () {
returnview('dashboard');
})->middleware(['auth'])->name('dashboard');
require __DIR__.'/auth.php';
Then, in routes/auth.php:
useApp\Http\Controllers\Auth\AuthenticatedSessionController;
useApp\Http\Controllers\Auth\RegisteredUserController;
// ... more controllersuseIlluminate\Support\Facades\Route;
Route::get('/register', [RegisteredUserController::class, 'create'])
->middleware('guest')
->name('register');
Route::post('/register', [RegisteredUserController::class, 'store'])
->middleware('guest');
// ... A dozen more routes
But you should use this include() only when that separate route file has the same settings for prefix/middlewares, otherwise it's better to group them in app/Providers/RouteServiceProvider:
publicfunctionboot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
// ... Your routes file listed next here
});
}
Translate Resource Verbs
If you use resource controllers, but want to change URL verbs to non-English for SEO purposes, so instead of /create you want Spanish /crear, you can configure it by using Route::resourceVerbs() method in App\Providers\RouteServiceProvider:
When using Resource Controllers, in routes/web.php you can specify ->names() parameter, so the URL prefix in the browser and the route name prefix you use all over Laravel project may be different.
So this code above will generate URLs like /p, /p/{id}, /p/{id}/edit, etc. But you would call them in the code by route('products.index'), route('products.create'), etc.
More Readable Route List
Have you ever run "php artisan route:list" and then realized that the list takes too much space and hard to read?
Here's the solution: php artisan route:list --compact
Then it shows 3 columns instead of 6 columns: shows only Method / URI / Action.
If you use resource controllers, but want to change URL verbs to non-English, so instead of /create you want Spanish /crear, you can configure it with Route::resourceVerbs() method.
In Resource Controllers, in routes/web.php you can specify ->names() parameter, so the URL prefix and the route name prefix may be different. This will generate URLs like /p, /p/{id}, /p/{id}/edit etc. But you would call them:
Override the route binding resolver for each of your models
You can override the route binding resolver for each of your models. In this example, I have no control over the @ sign in the URL, so using the resolveRouteBinding method, I'm able to remove the @ sign and resolve the model.
You can customize validation error messages per field, rule and language - just create a specific language file resources/lang/xx/validation.php with appropriate array structure.
'custom' => [
'email' => [
'required' => 'We need to know your e-mail address!',
],
],
Validate dates with "now" or "yesterday" words
You can validate dates by rules before/after and passing various strings as a parameter, like: tomorrow, now, yesterday. Example: 'start_date' => 'after:now'. It's using strtotime() under the hood.
If your validation rules depend on some condition, you can modify the rules by adding withValidator() to your FormRequest class, and specify your custom logic there. Like, if you want to add validation rule only for some user role.
If you want to change default validation error message for specific field and specific validation rule, just add a messages() method into your FormRequest class.
classStoreUserRequestextendsFormRequest
{
publicfunctionrules()
{
return ['name' => 'required'];
}
publicfunctionmessages()
{
return ['name.required' => 'User name should be real name'];
}
}
Prepare for Validation
If you want to modify some field before default Laravel validation, or, in other words, "prepare" that field, guess what - there's a method prepareForValidation() in FormRequest class:
By default, Laravel validation errors will be returned in a list, checking all validation rules. But if you want the process to stop after the first error, use validation rule called bail:
If you need to stop validation on the first error in FormRequest class, you can set stopOnFirstFailure property to true:
protected$stopOnFirstFailure = true;
Throw 422 status code without using validate() or Form Request
If you don't use validate() or Form Request, but still need to throw errors with the same 422 status code and error structure, you can do it manually throw ValidationException::withMessages()
if (! $user || ! Hash::check($request->password, $user->password)) {
throwValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
Rules depending on some other conditions
If your rules are dynamic and depend on some other condition, you can create that array of rules on the fly
With Rule::when() we can conditionally apply validation rules
Thanks to Rule::when() we can conditionally apply validation rules in laravel. In this example we validate the value of the vote only if the user can actually vote the post.
Validator::sometimes() method allows us to define when a validation rule should be applied
The laravel Validator::sometimes() method allows us to define when a validation rule should be applied, based on the input provided. The snippet shows how to prohibit the use of a coupon if the quantity of the purchased items is not enough.
You can enforce specific rules when validating user-supplied passwords by using the Password::defaults method. It includes options for requiring letters, numbers, symbols, and more.
when using Form Requests for validation, by default the validation error will redirect back to the previous page, but you can override it. Just define the property of $redirect or $redirectRoute. Link to docs
// The URI that users should be redirected to if validation fails./protected$redirect = '/dashboard';
// The route that users should be redirected to if validation fails.protected$redirectRoute = 'dashboard';
Mac validation rule
New mac_address validation rule added in Laravel 8.77
You can filter by NULL in Eloquent, but if you're filtering the collection further - filter by empty string, there's no "null" in that field anymore.
// This works$messages = Message::where('read_at is null')->get();
// Won’t work - will return 0 messages$messages = Message::all();
$unread_messages = $messages->where('read_at is null')->count();
// Will work$unread_messages = $messages->where('read_at', '')->count();
Use groupBy on Collections with Custom Callback Function
If you want to group result by some condition which isn’t a direct column in your database, you can do that by providing a closure function.
For example, if you want to group users by day of registration, here’s the code:
⚠️ Notice: it is done on a Collection class, so performed AFTER the results are fetched from the database.
Multiple Collection Methods in a Row
If you query all results with ->all() or ->get(), you may then perform various Collection operations on the same result, it won’t query database every time.
How to calculate the sum of all records when you have only the PAGINATED collection? Do the calculation BEFORE the pagination, but from the same query.
// How to get sum of post_views with pagination?$posts = Post::paginate(10);
// This will be only for page 1, not ALL posts$sum = $posts->sum('post_views');
// Do this with Query Builder$query = Post::query();
// Calculate sum$sum = $query->sum('post_views');
// And then do the pagination from the same query$posts = $query->paginate(10);
Serial no in foreach loop with pagination
We can use foreach collection items index as serial no (SL) in pagination.
it will solve the issue of next pages(?page=2&...) index count from continue.
Higher order collection methods
Collections have higher order methods, this are methods that can be chained , like groupBy() , map() ... Giving you a fluid syntax. This example calculates the price per group of products on an offer.
In addition to @can Blade directive, did you know you can check multiple permissions at once with @canany directive?
@canany(['update', 'view', 'delete'], $post)
// The current user can update, view, or delete the post
@elsecanany(['create'], \App\Post::class)
// The current user can create a post
@endcanany
More Events on User Registration
Want to perform some actions after new user registration? Head to app/Providers/EventServiceProvider.php and add more Listeners classes, and then in those classes implement handle() method with $event->user object
classEventServiceProviderextendsServiceProvider
{
protected$listen = [
Registered::class => [
SendEmailVerificationNotification::class,
// You can add any Listener class here// With handle() method inside of that class
],
];
Did you know about Auth::once()?
You can login with user only for ONE REQUEST, using method Auth::once(). No sessions or cookies will be utilized, which means this method may be helpful when building a stateless API.
if (Auth::once($credentials)) {
//
}
Change API Token on users password update
It's convenient to change the user's API Token when its password changes.
If you've defined your Gates but want to override all permissions for SUPER ADMIN user, to give that superadmin ALL permissions, you can intercept gates with Gate::before() statement, in AuthServiceProvider.php file.
// Intercept any Gate and check if it's super adminGate::before(function($user, $ability) {
if ($user->is_super_admin == 1) {
returntrue;
}
});
// Or if you use some permissions package...Gate::before(function($user, $ability) {
if ($user->hasPermission('root')) {
returntrue;
}
});
If you want to test email contents in your app but unable or unwilling to set up something like Mailgun, use .env parameter MAIL_DRIVER=log and all the email will be saved into storage/logs/laravel.log file, instead of actually being sent.
Preview Mailables
If you use Mailables to send email, you can preview the result without sending, directly in your browser. Just return a Mailable as route result:
Route::get('/mailable', function () {
$invoice = App\Invoice::find(1);
returnnewApp\Mail\InvoicePaid($invoice);
});
Preview Mail without Mailables
You can also preview your email without Mailables. For instance, when you are creating notification, you can specify the markdown that may be use for your mail notification.
Then you will receive an email with subject User Registration Email.
Send Notifications to Anyone
You can send Laravel Notifications not only to a certain user with $user->notify(), but also to anyone you want, via Notification::route(), with so-called "on-demand" notifications:
When creating Artisan command, you can ask the input in variety of ways: $this->confirm(), $this->anticipate(), $this->choice().
// Yes or no?if ($this->confirm('Do you wish to continue?')) {
//
}
// Open question with auto-complete options$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
// One of the listed options with default index$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
Maintenance Mode
If you want to enable maintenance mode on your page, execute the down Artisan command:
php artisan down
Then people would see default 503 status page.
You may also provide flags, in Laravel 8:
the path the user should be redirected to
the view that should be prerendered
secret phrase to bypass maintenance mode
status code during maintenance mode
retry page reload every X seconds
php artisan down --redirect="/" --render="errors::503" --secret="1630542a-246b-4b66-afa1-dd72a4c43515" --status=200 --retry=60
Before Laravel 8:
message that would be shown
retry page reload every X seconds
still allow the access to some IP address
php artisan down --message="Upgrading Database" --retry=60 --allow=127.0.0.1
When you've done the maintenance work, just run:
php artisan up
Artisan command help
To check the options of artisan command, Run artisan commands with --help flag. For example, php artisan make:model --help and see how many options you have:
Options:
-a, --all Generate a migration, seeder, factory, and resource controller for the model
-c, --controller Create a new controller for the model
-f, --factory Create a new factory for the model
--force Create the class even if the model already exists
-m, --migration Create a new migration file for the model
-s, --seed Create a new seeder file for the model
-p, --pivot Indicates if the generated model should be a custom intermediate table model
-r, --resource Indicates if the generated controller should be a resource controller
--api Indicates if the generated controller should be an API controller
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Exact Laravel version
Find out exactly what Laravel version you have in your app, by running command php artisan --version
Launch Artisan command from anywhere
If you have an Artisan command, you can launch it not only from Terminal, but also from anywhere in your code, with parameters. Use Artisan::call() method:
Sometimes you may wish to update a given model without dispatching any events. You may accomplish this using the updateQuietly method
Post::factory()->createOneQuietly();
Post::factory()->count(3)->createQuietly();
Post::factory()->createManyQuietly([
['message' => 'A new comment'],
['message' => 'Another new comment'],
]);
Useful for() method
The Laravel factory has a very useful for() method. You can use it to create belongsTo() relationships.
New in Laravel 8.49: Log::withContext() will help you to differentiate the Log messages between different requests. If you create a Middleware and set this context, all Log messages will contain that context, and you'll be able to search them easier.
If you use Eloquent API Resources to return data, they will be automatically wrapped in 'data'. If you want to remove it, add JsonResource::withoutWrapping(); in app/Providers/AppServiceProvider.php.
If you have API endpoint which performs some operations but has no response, so you wanna return just "everything went ok", you may return 204 status code "No content". In Laravel, it's easy: return response()->noContent();.
You can avoid N+1 queries in API resources by using the whenLoaded() method. This will only append the department if it’s already loaded in the Employee model. Without whenLoaded() there is always a query for the department
Don't forget to change APP_URL in your .env file from http://localhost to the real URL, cause it will be the basis for any links in your email notifications and elsewhere.
Not so much about Laravel, but... Never run composer update on production live server, it's slow and will "break" repository. Always run composer update locally on your computer, commit new composer.lock to the repository, and run composer install on the live server.
Composer: Check for Newer Versions
If you want to find out which of your composer.json packages have released newer versions, just run composer outdated. You will get a full list with all information, like this below.
phpdocumentor/type-resolver 0.4.0 0.7.1
phpunit/php-code-coverage 6.1.4 7.0.3 Library that provides collection, processing, and rende...
phpunit/phpunit 7.5.9 8.1.3 The PHP Unit Testing framework.
ralouphie/getallheaders 2.0.5 3.0.3 A polyfill for getallheaders.
sebastian/global-state 2.0.0 3.0.0 Snapshotting of global state
Auto-Capitalize Translations
In translation files (resources/lang), you can specify variables not only as :variable, but also capitalized as :VARIABLE or :Variable - and then whatever value you pass - will be also capitalized automatically.
If you want to have a current date without seconds and/or minutes, use Carbon's methods like setSeconds(0) or setMinutes(0).
// 2020-04-20 08:12:34echonow();
// 2020-04-20 08:12:00echonow()->setSeconds(0);
// 2020-04-20 08:00:00echonow()->setSeconds(0)->setMinutes(0);
// Another way - even shorterechonow()->startOfHour();
Single Action Controllers
If you want to create a controller with just one action, you can use __invoke() method and even create "invokable" controller.
If you have a callback function that you need to re-use multiple times, you can assign it to a variable, and then re-use.
$userCondition = function ($query) {
$query->where('user_id', auth()->id());
};
// Get articles that have comments from this user// And return only those comments from this user$articles = Article::with(['comments' => $userCondition])
->whereHas('comments', $userCondition)
->get();
Request: has any
You can check not only one parameter with $request->has() method, but also check for multiple parameters present, with $request->hasAny():
publicfunctionstore(Request$request)
{
if ($request->hasAny(['api_key', 'token'])) {
echo'We have API key passed';
} else {
echo'No authorization parameter';
}
}
Simple Pagination
In pagination, if you want to have just "Previous/next" links instead of all the page numbers (and have fewer DB queries because of that), just change paginate() to simplePaginate():
// Instead of $users = User::paginate(10);
// You can do this$users = User::simplePaginate(10);
Data Get Function
If you have an array complex data structure, for example a nested array with objects. You can use data_get() helper function retrieves a value from a nested array or object using "dot" notation and wildcard:
// We have an array
[
0 =>
['user_id' =>'some user id', 'created_at' => 'some timestamp', 'product' => {object Product}, etc],
1 =>
['user_id' =>'some user id', 'created_at' => 'some timestamp', 'product' => {object Product}, etc],
2 => etc
]
// Now we want to get all products ids. We can do like this:data_get($yourArray, '*.product.id');
// Now we have all products ids [1, 2, 3, 4, 5, etc...]
Blade directive to add true/false conditions
New in Laravel 8.51: @class Blade directive to add true/false conditions on whether some CSS class should be added. Read more in docs Before:
Jobs are discussed in the "Queues" section of the docs, but you can use Jobs without queues, just as classes to delegate tasks to. Just call $this->dispatchNow() from Controllers
If you want to generate some fake data, you can use Faker even outside factories or seeds, in any class. Keep in mind: to use it in production, you need to move faker from "require-dev" to "require" in composer.json
You can schedule things to run daily/hourly in a lot of different structures. You can schedule an artisan command, a Job class, an invokable class, a callback function, and even execute a shell script.
If you want to search Laravel Docs for some keyword, by default it gives you only the TOP 5 results. Maybe there are more? If you want to see ALL results, you may go to the Github Laravel docs repository and search there directly. https://github.com/laravel/docs
Filter route:list
New in Laravel 8.34: php artisan route:list gets additional flag --except-path, so you would filter out the routes you don't want to see. [See original PR](New in Laravel 8.34: php artisan route:list gets additional flag --except-path, so you would filter out the routes you don't want to see. See original PR
Blade directive for not repeating yourself
If you keep doing the same formatting of the data in multiple Blade files, you may create your own Blade directive. Here's an example of money amount formatting using the method from Laravel Cashier.
If you are not sure about the parameters of some Artisan command, or you want to know what parameters are available, just type php artisan help [a command you want].
Disable lazy loading when running your tests
If you don't want to prevent lazy loading when running your tests you can disable it
Using two amazing helpers in Laravel will bring magic results
Using two amazing helpers in Laravel will bring magic results... In this case, the service will be called and retried (retry). If it stills failing, it will be reported, but the request won't fail (rescue)
"upcomingInvoice" method in Laravel Cashier (Stripe)
You can show how much a customer will pay in the next billing cycle. There is a "upcomingInvoice" method in Laravel Cashier (Stripe) to get the upcoming invoice details.
There are multiple ways to return a view with variables
// First way ->with()returnview('index')
->with('projects', $projects)
->with('tasks', $tasks)
// Second way - as an arrayreturnview('index', [
'projects' => $projects,
'tasks' => $tasks
]);
// Third way - the same as second, but with variable$data = [
'projects' => $projects,
'tasks' => $tasks
];
returnview('index', $data);
// Fourth way - the shortest - compact()returnview('index', compact('projects', 'tasks'));
Schedule regular shell commands
We can schedule regular shell commands within Laravel scheduled command
Test that doesn't assert anything, just launch something which may or may not throw an exception
classMigrationsTestextendsTestCase
{
publicfunctiontest_successful_foreign_key_in_migrations()
{
// We just test if the migrations succeeds or throws an exception$this->expectNotToPerformAssertions();
Artisan::call('migrate:fresh', ['--path' => '/databse/migrations/task1']);
}
}
"Str::mask()" method
Laravel 8.69 released with "Str::mask()" method which masks a portion of string with a repeated character
classPasswordResetLinkControllerextendsController
{
publicfunctionsendResetLinkResponse(Request$request)
{
$userEmail = User::where('email', $request->email)->value('email'); // username@domain.com$maskedEmail = Str::mask($userEmail, '*', 4); // user***************// If needed, you provide a negative number as the third argument to the mask method,// which will instruct the method to begin masking at the given distance from the end of the string$maskedEmail = Str::mask($userEmail, '*', -16, 6); // use******domain.com
}
}
There is a method called macro on a lot of built-in Laravel classes. For example Collection, Str, Arr, Request, Cache, File, and so on. You can define your own methods on these classes like this:
Str::macro('lowerSnake', function (string$str) {
returnStr::lower(Str::snake($str));
});
// Will return: "my-string"Str::lowerSnake('MyString');
If you are running Laravel v8.70, you can chain can() method directly instead of middleware('can:..')
// instead ofRoute::get('users/{user}/edit', function (User$user) {
...
})->middleware('can:edit,user');
// you can do thisRoute::get('users/{user}/edit', function (User$user) {
...
})->can('edit''user');
// PS: you must write UserPolicy to be able to do this in both cases
You can use temporary download URLs for your cloud storage resources to prevent unwanted access. For example, when a user wants to download a file, we redirect to an s3 resource but have the URL expire in 5 seconds.
publicfunctiondownload(File$file)
{
// Initiate file download by redirecting to a temporary s3 URL that expires in 5 secondsreturnredirect()->to(
Storage::disk('s3')->temporaryUrl($file->name, now()->addSeconds(5))
);
}
Dealing with deeply-nested arrays can result in missing key / value exceptions. Fortunately, Laravel's data_get() helper makes this easy to avoid. It also supports deeply-nested objects.
Deeply-nested arrays are a nightmare when they may be missing properties that you need. In the example below, if either request, user or name are missing then you'll get errors.
$value = $payload['request']['user']['name']
Instead, use the data_get() helper to access a deeply-nested array item using dot notation.
$value = data_get($payload, 'request.user.name');
We can also avoid any errors caused by missing properties by supplying a default value.
You can customize how your exceptions are rendered by adding a 'render' method to your exception. For example, this allows you to return JSON instead of a Blade view when the request expects JSON.
Be careful when constructing your custom filtered queries using GET parameters
if (request()->has('since')) {
// example.org/?since=// fails with illegal operator and value combination$query->whereDate('created_at', '<=', request('since'));
}
if (request()->input('name')) {
// example.org/?name=0// fails to apply query filter because evaluates to false$query->where('name', request('name'));
}
if (request()->filled('key')) {
// correct way to check if get parameter has value
}
We often write if statements to check if a value is present on a request or not. You can simplify it with the whenFilled() helper.
publicfunctionstore(Request$request)
{
$request->whenFilled('status', function (string$status)) {
// Do something amazing with the status here!
}, function () {
// This it called when status is not filled
});
}
You can pass arguments to your middleware for specific routes by appending ':' followed by the value. For example, I'm enforcing different authentication methods based on the route using a single middleware.
If you need to grab something from the Laravel session, then forget it immediately, consider using session()->pull($value). It completes both steps for you.
New in this week's Laravel v8.77: $request->date() method. Now you don't need to call Carbon manually, you can do something like: $post->publish_at = $request->date('publish_at')->addHour()->startOfHour(); Link to full pr by @DarkGhostHunter
Use through instead of map when using pagination
When you want to map paginated data and return only a subset of the fields, use through rather than map. The map breaks the pagination object and changes it's identity. While, through works on the paginated data itself