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:
App\Models\User {#1319 ▼
#hidden: array:2 [▶]
#casts: array:1 [▶]
#connection: "mysql"
#table: "users"
...
#relations: array:1 [▼
"user_package" => App\Models\UserPackage {#1570 ▼
#connection: "mysql"
#table: "user_packages"
...
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"package" => null
]
...
}
CodePudding user response:
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.