Skip to content

Commit

Permalink
Eager Loading a relation that calls another relation returns incorrec…
Browse files Browse the repository at this point in the history
…t results
  • Loading branch information
Allan Dantas authored and allandantasdev committed Jun 18, 2024
1 parent 4345165 commit ce9741a
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 6 deletions.
67 changes: 67 additions & 0 deletions app/Console/Commands/EagerLoadingTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace App\Console\Commands;

use App\Models\Category;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Auth;

class EagerLoadingTest extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:eager-loading-test';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';

/**
* Execute the console command.
*/
public function handle()
{
dump('Lazy loading:');

Auth::login(User::find(1));
dump('Authenticated user: '.Auth::user()->id);
Category::get()->each(
fn(Category $category) => dump(sprintf('- %s: %d examples', $category->name, $category->examples->count()))
);

// --

Auth::login(User::find(2));
dump('Authenticated user: '.Auth::user()->id);
Category::get()->each(
fn(Category $category) => dump(sprintf('- %s: %d examples', $category->name, $category->examples->count()))
);

// --

dump('Eager loading:');

Auth::login(User::find(1));
dump('Authenticated user: '.Auth::user()->id);
Category::with('examples')->get()->each(
fn(Category $category) => dump(sprintf('- %s: %d examples', $category->name, $category->examples->count()))
);

// --

Auth::login(User::find(2));
dump('Authenticated user: '.Auth::user()->id);
Category::with('examples')->get()->each(
fn(Category $category) => dump(sprintf('- %s: %d examples', $category->name, $category->examples->count()))
);


}
}
27 changes: 27 additions & 0 deletions app/Models/Category.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Models;

use Database\Factories\CommentFactory;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Auth;

class Category extends Model
{
protected $fillable = ['user_id', 'name'];

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

public function examples(): HasMany
{
return $this->hasMany(Example::class)
->hasAccess(Auth::user());
}
}
28 changes: 28 additions & 0 deletions app/Models/Example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Example extends Model
{
protected $fillable = ['category_id', 'name', 'restricted'];

public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}

public function scopeHasAccess(Builder $query, ?User $user = null): Builder
{
return $query->where(
fn ($query) => $query->where('restricted', false)
->when(
$user !== null,
fn($query) => $query->orWhereIn('category_id', $user->categories->pluck('id'))
)
);
}
}
10 changes: 9 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

Expand All @@ -20,6 +21,8 @@ class User extends Authenticatable
'name',
'email',
'password',
'foo_id',
'bar_id',
];

/**
Expand All @@ -44,4 +47,9 @@ protected function casts(): array
'password' => 'hashed',
];
}

public function categories(): HasMany
{
return $this->hasMany(Category::class);
}
}
64 changes: 64 additions & 0 deletions app/Providers/TelescopeServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;

class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Telescope::night();

$this->hideSensitiveRequestDetails();

$isLocal = $this->app->environment('local');

Telescope::filter(function (IncomingEntry $entry) use ($isLocal) {
return $isLocal ||
$entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}

/**
* Prevent sensitive request details from being logged by Telescope.
*/
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}

Telescope::hideRequestParameters(['_token']);

Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}

/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewTelescope', function ($user) {
return in_array($user->email, [
//
]);
});
}
}
24 changes: 24 additions & 0 deletions database/migrations/0001_01_01_000003_create_categories_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

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

return new class extends Migration
{
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->text('name');
$table->timestamps();
});
}

public function down()
{
Schema::dropIfExists('categories');
}
};
25 changes: 25 additions & 0 deletions database/migrations/0001_01_01_000004_create_examples_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

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

return new class extends Migration
{
public function up()
{
Schema::create('examples', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('category_id');
$table->foreign('category_id')->references('id')->on('categories');
$table->text('name');
$table->boolean('restricted');
$table->timestamps();
});
}

public function down()
{
Schema::dropIfExists('examples');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

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

return new class extends Migration
{
/**
* Get the migration connection name.
*/
public function getConnection(): ?string
{
return config('telescope.storage.database.connection');
}

/**
* Run the migrations.
*/
public function up(): void
{
$schema = Schema::connection($this->getConnection());

$schema->create('telescope_entries', function (Blueprint $table) {
$table->bigIncrements('sequence');
$table->uuid('uuid');
$table->uuid('batch_id');
$table->string('family_hash')->nullable();
$table->boolean('should_display_on_index')->default(true);
$table->string('type', 20);
$table->longText('content');
$table->dateTime('created_at')->nullable();

$table->unique('uuid');
$table->index('batch_id');
$table->index('family_hash');
$table->index('created_at');
$table->index(['type', 'should_display_on_index']);
});

$schema->create('telescope_entries_tags', function (Blueprint $table) {
$table->uuid('entry_uuid');
$table->string('tag');

$table->primary(['entry_uuid', 'tag']);
$table->index('tag');

$table->foreign('entry_uuid')
->references('uuid')
->on('telescope_entries')
->onDelete('cascade');
});

$schema->create('telescope_monitoring', function (Blueprint $table) {
$table->string('tag')->primary();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
$schema = Schema::connection($this->getConnection());

$schema->dropIfExists('telescope_entries_tags');
$schema->dropIfExists('telescope_entries');
$schema->dropIfExists('telescope_monitoring');
}
};
24 changes: 19 additions & 5 deletions database/seeders/DatabaseSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace Database\Seeders;

use App\Models\Category;
use App\Models\Comment;
use App\Models\Example;
use App\Models\Permission;
use App\Models\Post;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
Expand All @@ -13,11 +17,21 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// User::factory(10)->create();
$user = User::factory()->create();

User::factory()->create([
'name' => 'Test User',
'email' => '[email protected]',
$categories = [Category::query()->create(['user_id' => $user->id, 'name' => 'Category 1']),
Category::query()->create(['user_id' => $user->id, 'name' => 'Category 2']),
Category::query()->create(['user_id' => $user->id, 'name' => 'Category 3']),
];

Example::insert([
['category_id' => $categories[0]->id, 'name' => 'Example 1', 'restricted' => false],
['category_id' => $categories[1]->id, 'name' => 'Example 2', 'restricted' => true],
['category_id' => $categories[2]->id, 'name' => 'Example 3', 'restricted' => false],
['category_id' => $categories[2]->id, 'name' => 'Example 4', 'restricted' => false],
['category_id' => $categories[2]->id, 'name' => 'Example 5', 'restricted' => true],
]);

User::factory()->create(); // another user just for demonstration
}
}

0 comments on commit ce9741a

Please sign in to comment.