diceline-chartmagnifiermouse-upquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Eager Loading with Subqueries in Laravel Eloquent

Eager loading with subqueries allows you to load related models using a subquery instead of joining the tables, which can improve performance in certain situations. For example, if you have a User model and a Post model, you can count the number of posts created by each user in the last month using a subquery like this:

$users = User::withCount(['posts as post_count' => function ($query) {
    $query->select(DB::raw('count(*)'))
          ->where('created_at', '>', now()->subMonth());
}])->get();

Laravel Request Authorization: Understanding the Differences between Request Classes and Policies

In Laravel, both request classes and policies can handle authorization.

Request classes have an authorize method that is called automatically by the framework before the request is handled. It is a convenient way to perform authorization checks on a specific request.

Policies provide a more flexible and reusable way to handle authorization across multiple request classes. They are classes that organize authorization logic around a particular model or resource. They allow you to define granular abilities for your application's users and groups.

When both the request's authorize method and a policy's __call method (or method with the same name as the action) return false for the same action, the framework will consider the user as unauthorized and will return a 403 Forbidden response.

In such scenarios, the priority is given to the authorize method of the request class. If it returns true, the framework will continue to process the request and will not check against any policy. However, if it returns false or throws an exception, Laravel will check the policy's method with the same name as the action, and if it returns false, the user will be considered unauthorized and a 403 Forbidden response will be returned.

In summary, both request classes and policies can handle authorization in Laravel, with priority given to the authorize method of request classes.

How to use Laravel's Tappable trait to achieve fluency

Given the following schemas:

Schema::create('skills', function (Blueprint $table) {
    $table->id();
    $table->string("name");
});
//pivot table
Schema::create('category_skill', function (Blueprint $table) {
    $table->id();
    $table->foreignId("skill_id")->constrained();
    $table->foreignId("category_id")->constrained();
});
//the categories table will be omitted for brevity

Suppose you have to write a method which created a skill, attached multiple categories to it and returned the created skill afterwards:

class CreateSkillAction
{
    public function execute(SkillDTO $skillData): Skill
    {
       ...
    }
}

You could easily achieve this with the following code:

public function execute(SkillDTO $skillData): Skill
{
    $skill = Skill::create($skillData->attributes());
    $skill->categories()->attach($skillData>categories);
    return $skill;
}

But if you wanted to make it a bit more fluent - akin to using fluent interfaces - you could do the following:

1. Add the Tappable trait to you model:

class Skill extends Model
{
    use \Illuminate\Support\Traits\Tappable;
    ...
}

2. Rewrite the execute method to use the tap method - now present on the model:

public function execute(SkillDTO $skillData): Skill
{
    return Skill::create($skillData->attributes())
        ->tap(fn($skill)=>$skill->categories()->attach($skillData->categories));
}

How to trigger model events on pivot tables in Laravel

Suppose we have an app where we have a couple of tasks and users and we want to be able to assign users to tasks.

But what if at the same time we want to know who assigned each user to each task? We can easily achieve this by taking advantage of Laravel's model events - for pivot tables.

We would need 3 tables: one for tasks, one for users and one for the assigning users to tasks:

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string("title");
    ...
    $table->timestamps();
});

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string("username");
    ...
    $table->timestamps();
});

Schema::create('task_user', function (Blueprint $table) {
    $table->id();
    $table->foreignId("assignee_id")->constrained("users");
    $table->foreignId("skill_id")->constrained();
    $table->foreignId("user_id")->constrained();
    $table->timestamps();
});

1. Create the corresponding TaskUser pivot model.

 php artisan make:model TaskUser --pivot

2. Add your event handlers.

class TaskUser extends Pivot
{
    protected static function booted()
    {
        static::creating(function ($pivot_model) {
            // your implementation here
        });
        ...
    }
}

3. Let Laravel know which model it should use for the pivot table.

This is the key - without this step the callback described above will not be executed!

//in the User model
public function tasks()
{
    return $this->belongsToMany(Task::class)
      ->using(TaskUser::class);
}

Now, you can do:

$user->tasks()->attach($tasks);

That's it! Your event callbacks or corresponding observer methods should now be executed.