Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

muuucho's avatar
Level 11

Seeding throws error. Problem with the trait that filters by team in Pub Model.

1 I have a Trait in my multi-tenant project that filters on team_id. I have a crud named Pub that works. However, when I seed the pub I get an error. 2 Why does the scope have a name: static::addGlobalScope('team',) I named it 'team' but am unsure where it is used. 3 I also had problems with ambiguous column team_id in relational queries so I reached out for $query->qualifyColumn('team_id'). Does it mean that Laravel then assumes that the correct team_id is the model table's column named team_id that should be filtered and not any other relational table's column named team_id? I hope so

Trait:

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

trait FilterByTeam
{
    /**
     * @property integer $team_id
     */
    protected static function booted(): void
    {
        static::creating(function (Model $model) {
            $model->team_id = auth()->user()->team_id;
        });

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

    }
}

My Pub Model look like this

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Traits\FilterByTeam;
class Pub extends Model
{
    use FilterByTeam;
    use HasFactory;
    protected $fillable = ['cb', 'team_id', 'name', 'type_id', 'vat', 'gender'];

    const GENDER_LIST = [
        'male' => 'Male',
        'female' => 'Female',
    ];

    public function tags(){
        return $this->belongsToMany(Tag::class); 
    }

    public function type(){
        return $this->belongsTo(Type::class); 
    }

    public function user(){
        return $this->belongsTo(User::class, 'cb');
    }
}

Then my pubs table

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('pubs', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('cb');
            $table->unsignedBigInteger('team_id');
            $table->string ('name');
            $table->unsignedBigInteger ('type_id')->nullable();
            $table->boolean ('vat')->default(0);
            $table->enum('gender', ['male', 'female']);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('pubs');
    }
};

PubFactory:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Pub>
 */
class PubFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'cb' => fake()->numberBetween(1,3),
            'team_id' => 1,
            'name' => fake()->unique()->company(),
            'type_id' => fake()->numberBetween(1,3),
            'vat' => fake()->boolean(),
            'gender' => fake()->randomElement(['male', 'female']),
        ];
    }
}

The error when I seed the database after having implemented the Trait:

 ErrorException

  Attempt to read property "team_id" on null

  at C:\xampp\htdocs\livewire\app\Traits\FilterByTeam.php:14
     10▕      */
     11▕     protected static function booted(): void
     12▕     {
     13▕         static::creating(function (Model $model) {
  ➜  14▕             $model->team_id = auth()->user()->team_id;
     15▕         });
     16▕
     17▕         // Define qualifyColumn to avoid ambiguous problems on relation queries
     18▕         static::addGlobalScope('team', function (Builder $query) {

  1   C:\xampp\htdocs\livewire\app\Traits\FilterByTeam.php:14
      Illuminate\Foundation\Bootstrap\HandleExceptions::Illuminate\Foundation\Bootstrap\{closure}("Attempt to read property "team_id" on null", "C:\xampp\htdocs\livewire\app\Traits\FilterByTeam.php")

  2   C:\xampp\htdocs\livewire\vendor\laravel\framework\src\Illuminate\Events\Dispatcher.php:458
      App\Models\Pub::App\Traits\{closure}(Object(App\Models\Pub))

If I remove the Trait from the Pub model the seeder seeds table pubs as expected.

0 likes
1 reply
LaryAI's avatar
Level 58

The error you're encountering is due to the fact that the auth()->user() call returns null when running the seeder. This is because there is no authenticated user during the seeding process. To resolve this, you can modify the trait to check if there is an authenticated user before attempting to access the team_id.

Here's a revised version of your FilterByTeam trait:

<?php
namespace App\Traits;

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

trait FilterByTeam
{
    /**
     * @property integer $team_id
     */
    protected static function booted(): void
    {
        static::creating(function (Model $model) {
            if ($user = Auth::user()) {
                $model->team_id = $user->team_id;
            }
        });

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

Explanation:

  1. Check for Authenticated User: Before accessing auth()->user()->team_id, the code now checks if there is an authenticated user using if ($user = Auth::user()). This prevents the error when there is no authenticated user, such as during seeding.

  2. Global Scope: The global scope is also wrapped in a check to ensure it only applies the filter if there is an authenticated user.

Seeding with a Specific Team ID:

Since the seeder does not have an authenticated user, you should explicitly set the team_id in your factory or seeder. Your PubFactory already sets a team_id, so it should work as expected.

Example Seeder:

If you need to seed with different team_id values, you can do something like this in your seeder:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\Pub;

class PubSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // Seed pubs for team_id 1
        Pub::factory()->count(10)->create(['team_id' => 1]);

        // Seed pubs for team_id 2
        Pub::factory()->count(10)->create(['team_id' => 2]);
    }
}

This way, you ensure that the team_id is set correctly during the seeding process, and the trait will not throw an error due to the absence of an authenticated user.

Please or to participate in this conversation.