Laravel CRUD Routes
Verb | URI | Action | Route Name |
---|---|---|---|
GET | /photos | index | photos.index |
GET | /photos/create | create | photos.create |
POST | /photos | store | photos.store |
GET | /photos/ | show | photos.show |
GET | /photos/{photo}/edit | edit | photos.edit |
PUT/PATCH | /photos/ | update | photos.update |
DELETE | /photos/ | destroy | photos.destroy |
PHP native data types
Basic types
- int, integer , string, array-key, bool, boolean, true, false, null, float, double, scalar, array, iterable, callable, resource, void, object
Array to query string
use Illuminate\Support\Arr;
$filters = [
'location' => 'USA',
'date_range'=>[
'start' => '2023-01-01',
'end' => '2020-12-31'
],
'author'=>'John Doe'
];
$queryString = Arr::query($filters);
$url = 'https://example.com/?'.$queryString;
echo $url;
/** Output
https://example.com/?location=USA&date_range[start]=2023-01-01&date_range[end]=2020-12-31&author=John Doe
*/
Relationships cheat table
Laravel Relationship Type | From parent to child model | From child to parent model | Pivot Eloquent Model operations |
---|---|---|---|
HasOne/BelongsTo | $parent->child()->save($child) |
$child->parent()->associate($parent) |
N/A |
HasMany/BelongsTo | $parent->children()->save($child) |
$child->parent()->associate($parent) |
N/A |
BelongsToMany/BelongsToMany | $parent->children()->attach($childId,$pivotData) |
$child->parents()->attach($parentId,$pivotData) |
Pivot::create([...]) |
MorphToMany/MorphToMany | $parent->children()->attach($childId,$pivotData) |
$child->parents()->attach($parentId,$pivotData) |
Pivot::create([...]) |
Polymorphic (one-to-one) | $parent->image()->save($image) |
$image->imageable()->associate($parent) |
N/A |
Polymorphic (many-to-one) | $parent->comments()->save($comment) |
$comment->commentable()->associate($parent) |
N/A |
Polymorphic (many-to-many) | $parent->tags()->attach($tagId,$pivotData) |
$tag->taggable()->attach($parentId,$pivotdata) |
Pivot:::create([...]) |
Default values in migrations
$table->string('name')->default('Untitled');
$table->string('total')->default(new Expression('(quantity * price)'));
$table->timestamp('order_date')->default(new Expression('NOW()'));
$table->integer('discount')->default(new Expression('(CASE WHEN quantity >= 10 THEN 10 ELSE 0 END)'));
Tailwind merge to PHP
// circle.blade.php
<div {{ $attributes->mergeClasses('w-10 h-10 rounded-full bg-red-500') }}>
</div>
// view.blade.php
<x-circle class='bg-blue-500' />
// output
<div class="w-10 h-10 rounded-full bg-blue-500"></div>
All Model.php
attributes
protected $table = 'users'; // the table associated with the model
protected $primaryKey = 'uuid'; // The primary key for model
protected $keyType = 'int'; // The type of the primary key ID
protected $incrementing = true; // Indicates if the IDs should be auto-incrementing
protected $with = []; // The relationship to eager load on every query.
protected $withCount = []; // The relationship counts that should be eager on every query
protected $guarded = ['id']; // the attributes that are not mass assignable
protected $hidden = []; // attributes that should be hidden from array
protected $fillable = ['email','password']; // attributes that are mass assignable
protected $appends = ['field']; // The attrubutes that should be include in model
protected $fakeColumns = ['extras']; // The attrubutes that should be treated as fake columns
public $timestamps = false; // if the model should be timestamped
const CREATED_AT = 'createdAt'; // The name of the created_at col
const UPDATED_AT = 'updatedAt'; // The name of the updated_at col
protected $perpage = 25; // The number of models to return for pagination
protected $casts = [
'assets' => 'array' // The attributes that should be cast to native types.
];
Custom Blade Conditionals
AppServiceProvider.php
// AppServiceProvider::boot()
Blade::if('writer',function(){
return in_array(Auth::user()->role,['editor','reporter']);
});
view.blade.php
@writer
//
@endwriter
Default to a relationship
class Comment extends Model
{
public function author(){
return $this->belongsTo(User::class)->withDefaults([
'name' => 'Guest'
]);
}
}
// Now in you view, instead of doing this
{{ $comment->author ? $comment->author->name : 'Guest' }}
// You can do this
{{ $comment->author->name }}
Delted/Prune model data on schedule
class Order extends Model
{
use Prunable;
public function prunable(){
return Order::query()
->whereStatus('abandoned')
->where('created_at','<=',now()->subMonth());
}
}
// Console/Kernel.php
$schedule->command('model:prune')->daily();
whereNotNull()
$customers = Customer::query()
->whereNotNull(['payment_date','discount_cost'])
->get()
Do task after a model was created by factory
class UserFactory extends Factory
{
public function definition(){
// ...
}
public function configure():static
{
return $this->afterCreating(function (User $user){
// Task after creating a user
return $user->createTeam();
});
}
}
Saving a model and its relationship with push()
class User extends Model
{
public function contact(){
return $this->hasOne(Contact::class);
}
}
$user = User::first();
$user->name = 'John Doe';
$user->contact->number = '112233445566';
$user->push(); // This will update both user and contact record in DB
Create route resource
Only include specified routes.
Route::resource('roles',RoleController::class)->only('update','index');
Start and End Data validation rule
$rules = [
'start_date' => 'after:tomorrow',
'end_date' => 'after:start_date'
];
Run a logic if a query takes a long time
AppServiceProvider.php
public function boot(){
// Provide a handler to be invoked when a query executes longer than 300ms
DB::whenQueryingForLongerThan(300,function ($connection, QueryExecuited $event){
dump($event->sql);
dump($event->binding);
dump($event->time);
});
DB::allowQueryDurationHandlersToRunAgain();
}
// The handler will be invoked since the query takes longer
User::somethingThatTakeLongTime()->get();
Import classes in the same namespace
// Instead of this
use Illuminate\Support\Facaded\DB;
use Illuminate\Support\Facaded\Log;
// Do this
use Illuminate\Support\Facaded\{DB,Log};
Two ways of updating a model
Product::where('id',1)->update(['stock_left'=>1]);
// Result SQL query
// Update `products` set `stock_left` = 1 where `id` = 1
Product::find(1)->update(['stock_left'=>1]);
// Shorter code but 2 sql queries:
// - Select * from products where products.id = 1 limit 1
// - update products set stock_left = 1 where id = 1
Pint formatting Github action
name: PHP Linting
on: ['push', 'pull_request']
jobs:
phplint:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: 'laravel-pint'
uses: aglipanci/laravel-pint-action@2.2.2
with:
preset: laravel
verboseMode: true
testMode: true
Read duration Str macro
use Illuminate\Support\Str;
Str::macro('readDuration',funciton(...$text){
$totalWords = str_word_count(implode("",$text));
$minutesToRead = round($totalWords / 200);
return (int)max(1,$minutesToRead);
});
echo Str::readDuration($post->text). ' min read';
Same relationship different param
// Get all clients related to this developer
public function clients(){
return $this->belongsToMany(Clients::class);
}
// Get only local clients
public function clients(){
return $this->belongsToMany(Clients::class)
->wherePivot('is_local',true);
}
Get columns of a table
DB::getSchemaBuilder()->getColumnListing('users');
// array (
// 0 => 'id',
// 1 => 'name',
// 2 => 'username',
// 3 => 'email',
// 4 => 'email_verified_at',
// 5 => 'password',
// 6 => 'remember_token',
// 7 => 'current_team_id',
// 8 => 'profile_photo_path',
// 9 => 'github_url',
// 10 => 'linkedin_url',
// 11 => 'deleted_at',
// 12 => 'created_at',
// 13 => 'updated_at',
// 14 => 'two_factor_secret',
// 15 => 'two_factor_recovery_codes',
// 16 => 'two_factor_confirmed_at',
// 17 => 'uuid',
// )
Optional model attributes
$user = User::find(1);
// This code would cause an error if $user is null or undefined
$address = $user->address; // NULL
// But when the optional() function, you can avoid the error;
$address = optional($user)->address;
Assign an re-use var in if condition
// Insted of repeating
if($this->option('type')){
$stub = "/stub/contorller'.{$this->option('type')}.stub";
}
// assign and re-use the variable
if($type = $this->option('type')){
$stub = "stub/controller.{$type}.stub";
}
Middleware in Controller
class MyController extends Controller
{
public function getMiddleware(){
return [
[
'middleware' => 'auth',
'options' => []
]
];
}
}
Clone all laravel docs locally
git clone laravel/docs laravel-docs
After cloning run it like any other project.
Two paginate in one view
$posts = Post::with('user')
->where('ai',false)
->latest()
->simplePaginate(perPage:10, pageName: 'postsPage');
$posts = Post::with('user')
->where('ai',true)
->latest()
->simplePaginate(perPage:10, pageName: 'aiPostsPage');
Show soft deleted data in route
Route::get('/users/{user}',function(User $user){
return $user->email;
})->withTrashed();
Route::resource('users',UserController::class)
->withTrashed();
Route::resource('users',UserController::class)
->withTrashed(['shoe'])
Benchmark database queries
use App\Models\User;
use Illuminate\Support\Benchmark;
Benchmark::dd([
'User count 1' => fn () => User::count(),
'User count 2' => fn () => User::all()->count()
]);
// array:2 [ // vendor\laravel\framework\src\Illuminate\Support\Benchmark.php:48
// "User count 1" => "14.129ms"
// "User count 2" => "1.299ms"
// ]