Laravel Tips and Cheat Sheet Collection

Published: 1 year ago - Updated: 1 year ago

11 minutes - 766 Words

article 'Laravel Tips and Cheat Sheet Collection' banner

Summary

A collection of Laravel tips and useful information for Laravel developers.

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"
// ]

Add Comment

Conversations (0)

Sign up for our newsletter

Stay up to date with the latest news and articles.