Home > database >  Laravel package base relationship models
Laravel package base relationship models

Time:12-29

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

  1. each users can be have one package which we created user_package for that
  2. each user_package belongs to many users
  3. each user_package has on packages
  4. each packages can be have many features which we created features table
  5. each features belongs to many packages

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.

  • Related