in our web application we want to have a simple package base cms
, for having that we have users
, packages
, features
and user_package
table on database
- each
users
can be have onepackage
which we createduser_package
for that - each
user_package
belongs to manyusers
- each
user_package
has onpackages
- each
packages
can be have manyfeatures
which we createdfeatures
table - each
features
belongs to manypackages
when i try to get user package it, i think it should be:
user->user_package->package->[feature]
my models:
class User extends Authenticatable { //... public function user_package() { return $this->hasOne(UserPackage::class); } } class UserPackage extends Model { public function package() { return $this->belongsTo(Package::class); } } class Package extends Model { public function feature(): HasMany { return $this->hasMany(Features::class); } } class Features extends Model { public function package(): BelongsToMany { return $this->belongsToMany(Package::class); } }
migrations:
Schema::create('packages', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('description')->nullable()->default(''); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent(); }); Schema::create('features', function (Blueprint $table) { $table->id(); $table->foreignId('packages_id')->nullable() ->constrained()->cascadeOnUpdate() ->cascadeOnDelete(); $table->string('title'); $table->text('description')->nullable()->default(''); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent(); }); Schema::create('user_packages', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained() ->cascadeOnUpdate() ->cascadeOnDelete(); $table->foreignId('packages_id')->nullable()->constrained() ->cascadeOnUpdate() ->cascadeOnDelete(); $table->longText('features')->nullable()->default(''); $table->integer('price'); $table->dateTime('start_date'); $table->dateTime('end_date'); $table->timestamp('created_at')->useCurrent(); $table->timestamp('update_at')->useCurrent(); });
now when i try to get data i get null
in package
relation ship
Route::get('/test',function(){ dd(auth()->user()->with(['user_package'=>function($query){ $query->with(['package'=>function($package){ $package->with('feature')->get(); }])->first(); }])->first()); });
output:
AppModelsUser {#1319 ▼ #hidden: array:2 [▶] #casts: array:1 [▶] #connection: "mysql" #table: "users" ... #relations: array:1 [▼ "user_package" => AppModelsUserPackage {#1570 ▼ #connection: "mysql" #table: "user_packages" ... #dispatchesEvents: [] #observables: [] #relations: array:1 [▼ "package" => null ] ... }
Advertisement
Answer
I think the crux of your question comes down to fetching nested relationships, which is as simple as auth()->user()->with('user_package.package.feature')
but there is a problem with your relationships.
Your relationship between Package
and Feature
is broken; since the features
table has a package_id
column, a feature by definition cannot “belong to many” packages.
class User extends Authenticatable { public function user_package(): HasOne { return $this->hasOne(UserPackage::class); } } class UserPackage extends Model { public function user(): BelongsTo { return $this->belongsTo(User::class); } public function package(): BelongsTo { return $this->belongsTo(Package::class); } } class Package extends Model { public function features(): HasMany { return $this->hasMany(Feature::class); } /** * It never hurts to define both ends of the relationship * even if they aren't being used */ public function user_packages(): HasMany { return $this->hasMany(UserPackage::class); } } class Feature extends Model { public function package(): BelongsTo { return $this->belongsTo(Package::class); } }
Now, using the relationship, you can get the package features:
dump(Auth::user()->load('user_package.package.features'))
You’ve got some naming problems that I corrected in my examples above – relationship methods that return a single model should be singular, others should be plural. Class names should never be plural words (i.e. Feature
not Features
.)
Generally speaking, there’s no need to create a pivot class if all it’s doing is connecting two models. I’m not going to get into that since it would make for a much longer answer, but it’s something to keep in mind.
And it’s a matter of personal taste, but your class names should be one word only (i.e. Subscription
instead of UserPackage
) as it makes figuring out things like relationship names more intuitive.