muuucho's avatar
Level 11

Trait for CRUDS in multi tennant app

I have a trait that

1 Inserts team_id when any new post is stored into the DB

2 Filters on team_id when a post or multiple posts are selected from the DB

Now I like to add a security measure when a user soft deletes a model (actually that is an update): I like to add ```where('$model->team_id, '=', Auth::user()->team_id) to any delete attempt. How? My trait:

<?php
namespace App\Traits;
use Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

trait GoodTrait
{
    /**
     * @property integer $team_id
     */
    protected static function booted(): void
    {
        static::creating(function (Model $model) {
            if (Auth::user()){ // Skip filter when seeding, apply it elsewhere
                $model->team_id = auth()->user()->team_id;
                $model->cb = auth()->id();
                $model->ub = auth()->id();
            }
        });

        static::updating(function ($model) {
            $model->team_id = auth()->user()->team_id; // Just to be sure...
            $model->ub = auth()->id();
        });

        static::addGlobalScope('team_id', function (Builder $query) {
            $query->where($query->qualifyColumn('team_id'), '=', auth()->user()->team_id); // Define qualifyColumn to avoid ambiguous problems on relation queries
        });

    }
}
0 likes
2 replies
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

To add a security measure when a user soft deletes a model, you can extend your trait to include a check on the deleting event. This will ensure that the team_id matches the authenticated user's team_id before allowing the soft delete operation to proceed.

Here's how you can modify your trait to include this functionality:

<?php

namespace App\Traits;

use Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

trait GoodTrait
{
    /**
     * @property integer $team_id
     */
    protected static function booted(): void
    {
        static::creating(function (Model $model) {
            if (Auth::user()) { // Skip filter when seeding, apply it elsewhere
                $model->team_id = auth()->user()->team_id;
                $model->cb = auth()->id();
                $model->ub = auth()->id();
            }
        });

        static::updating(function ($model) {
            $model->team_id = auth()->user()->team_id; // Just to be sure...
            $model->ub = auth()->id();
        });

        static::addGlobalScope('team_id', function (Builder $query) {
            $query->where($query->qualifyColumn('team_id'), '=', auth()->user()->team_id); // Define qualifyColumn to avoid ambiguous problems on relation queries
        });

        static::deleting(function (Model $model) {
            if ($model->isSoftDeleting()) {
                $model->team_id = auth()->user()->team_id; // Ensure team_id matches before soft delete
                $model->ub = auth()->id();
            }
        });
    }

    /**
     * Determine if the model is being soft deleted.
     *
     * @return bool
     */
    public function isSoftDeleting()
    {
        return $this->usesSoftDeletes() && !is_null($this->getDeletedAtColumn());
    }

    /**
     * Determine if the model uses soft deletes.
     *
     * @return bool
     */
    public function usesSoftDeletes()
    {
        return in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses_recursive($this));
    }
}

Explanation:

  1. Creating and Updating Events: These events ensure that the team_id and user-related fields are set correctly when creating or updating a model.
  2. Global Scope: This scope ensures that all queries are filtered by the team_id of the authenticated user.
  3. Deleting Event: This event checks if the model is being soft deleted. If so, it ensures that the team_id matches the authenticated user's team_id before proceeding with the soft delete.
  4. Helper Methods:
    • isSoftDeleting(): Checks if the model is currently being soft deleted.
    • usesSoftDeletes(): Checks if the model uses the SoftDeletes trait.

By adding these checks, you ensure that only models belonging to the authenticated user's team can be soft deleted, adding an extra layer of security to your application.

muuucho's avatar
Level 11

Looks like the way to do it!

Please or to participate in this conversation.