(Laravel) Polymorphic relation through pivot table - laravel

Let's say, I have an Event model, which has more participants of various models (Player, Coach, Admin) through polymorphic relation and a pivot table (EventParticipant), which also contains a boolean column participate. I want to get participants through $event->participants which retrieves a collection of players, coaches, and admins through a polymorphic relation.
I have something similar created with standard non-polymorphic relation in trainings, like this:
class Training extends Model
{
/**
* Training has more players.
*/
public function players() {
return $this->belongsToMany('App\Player', 'training_player')
->using('App\TrainingPlayer')
->withPivot('participate');
}
}
class TrainingPlayer extends Pivot
{
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'participate' => 'boolean'
];
}
How could this be modified in the case of events, where participants() can be either Player, Coach, or Admin model? (Maybe something with MorphPivot class but I can't imagine how.)
(Instead of player_id (in TrainingPlayer class) which refers to id of the Player model, there are two columns role and rollable_id (in EventParticipant class) which refers to id of the Player, Coach, or Admin model, respectively)
class Event extends Model
{
/**
* Event has more participants (players, coaches, or admins).
*/
public function participants() {
//
}
}
class EventParticipant extends MorphPivot
{
//
}
Any help would be appreciate. :) Thx

I have been looking for something similar and came up with a solution. As per Jonas comment, you can't have different Models in 1 related set but you can have 1 for each model using 1 pivot table.
You can now query this with \App\Team::find(1)->with(['managers', 'users'])->get();
Schema::create('associations', function (Blueprint $table) {
$table->id();
$table->string('association_type');
$table->integer('association_id');
$table->integer('team_id');
$table->integer('meta')->nullable();
$table->timestamps();
});
Schema::create('managers', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
class Manager extends Model
{
public function teams()
{
return $this->belongsToMany('\App\Team', 'associations')->using('App\Association');
}
}
class Team extends Model
{
public function managers()
{
return $this->morphedByMany('App\Manager', 'association')->using('App\Association');
}
public function users()
{
return $this->morphedByMany('App\User', 'association')->using('App\Association');
}
}
class User extends Authenticatable
{
public function teams()
{
return $this->belongsToMany('\App\Team', 'associations')->using('App\Association');
}
}
// App/Association
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Association extends MorphPivot
{
protected $table = 'associations'; // not sure if this is needed
}

Related

Get Many Through and reverse in Laravel 9

I am building an app that has multiple vendors selling products. I need to isolate the customers who have placed an order with that particular vendor.
I have 3 models; Vendor, Customer, Order.
Vendor 'hasMany' Orders.
Order 'belongsTo' Vendor
Order 'belongsTo' Customer
Customer 'hasMany' Orders
class Vendor extends Model
{
/**
* #return HasMany
*/
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
}
class Order extends Model
{
/**
* #return BelongsTo
*/
public function customer() : BelongsTo
{
return $this->belongsTo(Customer::class);
}
/**
* #return BelongsTo
*/
public function vendor() : BelongsTo
{
return $this->belongsTo(Vendor::class);
}
}
class Customer extends Model
{
/**
* #return HasMany
*/
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
}
I would like to get all of the orders for a vendor, something like:
class Vendor extends Model
{
/**
* #return HasManyThrough
*/
public function customers(): HasManyThrough
{
return $this->hasManyThrough(Customer::class, Order::class);
}
}
What is the best way to do this?
I have tried hasManyThrough, however as the relationships are not one directional, it doesn't appear to work.
Edit: Here's the relevant migrations:
Vendor
Schema::create('vendors', function (Blueprint $table) {
$table->id();
$table->string('name')->nullable();
$table->string('logo')->nullable();
$table->string('business_type', 32);
$table->string('address', 128)->nullable();
$table->string('postcode', 16);
$table->bigInteger('town_id')->unsigned()->index();
$table->string('website', 128)->nullable();
$table->string('telephone', 64)->nullable();
$table->tinyInteger('approved')->default(0);
$table->timestamps();
});
Customer
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->string('first_name');
$table->string('last_name');
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_active');
$table->string('country');
$table->timestamps();
});
Order
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('status')->index();
$table->bigInteger('customer_id')->unsigned()->nullable()->index();
$table->bigInteger('vendor_id')->unsigned()->index()->nullable();
$table->string('reference')->nullable()->unique()->index();
$table->decimal('sub_total')->default(0);
$table->decimal('discount_total')->default(0);
$table->decimal('shipping_total')->default(0);
$table->decimal('tax_total')->default(0);
$table->decimal('grand_total')->default(0);
$table->timestamps();
});
I would like to get all of the orders for a vendor
Then you need to fix your Vendor model as follow (same for Customer model)
class Vendor extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
}
If your order belongs to more relationships than now, you should consider Polymorphic Relationships

What eloquent relations to use?

I'm building my first Laravel application and I'm confused about the relations because there are so many relationships.
A user can have multiple players(characters).
Players can play on multiple servers.
Each server has 2 stores(block store, perk store).
Each block store uses a different currency on a different server.
The perk store uses the same currency on all servers but some
products can be bought with server-specific currency.
Players also have a bank and a wallet that contains each currency
that they own.
Each server can have teams(guilds, factions, kingdoms) depending on the game type.
class CreateWalletTable extends Migration
{
public function up()
{
Schema::create('wallets', function (Blueprint $table) {
$table->id();
$table->integer('player_id');
$table->integer('currency_id');
$table->decimal('amount');
$table->timestamps();
$table->foreign('player_id')->references('id')->on('players');
$table->foreign('currency_id')->references('id')->on('currencies');
});
}
}
class Wallet extends Model
{
public function player(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Player::class);
}
public function currency(): \Illuminate\Database\Eloquent\Relations\hasOne
{
return $this->hasOne(Currency::class);
}
}
class CreateCurrenciesTable extends Migration
{
public function up()
{
Schema::create('currencies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('plural');
$table->string('symbol');
$table->integer('decimals');
$table->boolean('tradable');
$table->timestamps();
});
}
}
class Currency extends Model
{
public function wallet(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Wallet::class);
}
}

laravel 8 update child table data from parent table

I'm working on a Laravel 8 project.
I have a payments table, it's the migration:
Schema::create('payments', function (Blueprint $table) {
$table->id();
$table->enum('gateway',['idpay','zarinpal']);
$table->unsignedInteger('res_id')->nullable();
$table->char('ref_code',128)->nullable();
$table->enum('status',['paid','unpaid']);
$table->unsignedBigInteger('order_id');
$table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade');
$table->timestamps();
});
as you can see this table has a foreign key that references on orders table, and it is orders migration:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('amount');
$table->char('ref_code',128)->nullable();
$table->enum('status',['unpaid','paid',]);
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
I created a one to one relationship in Order model:
class Order extends Model
{
use HasFactory;
protected $guarded = [];
public function payment()
{
return $this->hasOne(Payment::class);
}
}
The problem is that when I want to use order() method on Payment class it does not work.
for example:
Payment::find(10)->order()->update(['status' =>'paid']);
I get this error:
BadMethodCallException Call to undefined method
App\Models\Payment::order()
UPDATE:
Here is Payment model:
class Payment extends Model
{
use HasFactory;
protected $guarded = [];
}
Thank you for helping me.
You should use like this a method in the payment model.
public function order()
{
return $this->belongsTo(Order::class);
}
Because you still don't have any relationship in the payment model to order.
You can check here for detailed information.
https://laravel.com/docs/8.x/eloquent-relationships#one-to-one-defining-the-inverse-of-the-relationship
You have to describe the order relation ship in the Payment model
class Payment extends Model
{
use HasFactory;
protected $guarded = [];
public function order()
{
return $this->belongsTo(Order::class);
}
}
and after that you can access the payment's order like this:
Payment::find(10)->order->update(['status' =>'paid']);

Why does Laravel Combine query is not working?

My Channels Model is :
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Model is:
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Migration File :
class CreateChannelsTable extends Migration
{
public function up()
{
Schema::create('channels', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('channels');
}
}
Discusion Migraion file
class CreateDiscussionsTable extends Migration
{
public function up()
{
Schema::create('discussions', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->string('title');
$table->text('content');
$table->text('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('discussions');
}
}
**Controller is: **
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function channel($slug)
{
$channel= Channels::where('slug',$slug)->first();
return view('channel')->with('discussion',$channel->Discussions);
}
}
And Finnaly the route is
Route::get('channel/{slug}',[
'uses' => 'HomeController#channel',
'as' => 'channel']);
**Now i am trying to fetch the data: **
$channel->Discussions
but it's giving me the error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'discussions.channels_id' in 'where clause' (SQL: select * from discussions where discussions.channels_id = 1 and discussions.channels_id is not null)
I don't know what exactly to do now. Please Help me
First of all, it's very important to name your classes and its relationships appropriately.
Laravel makes some assumptions about your foreign keys based on your class names and relationship definitions. You could also specify the foreign keys if you want.
However, here's how i'd do it.
class Channel extends Model
{
protected $fillable = ['title','slug'];
public function discussions()
{
return $this->hasMany('App\Discussion');
}
}
Notice here the model name is called Channel. When we have this class name and relationship, Laravel will assume that the discussion model has a foreign key called channel_id which it does.
You also need to define an inverse relationship in your Discussion model
class Discussion extends Model
{
public function channel()
{
return $this->belongsTo('App\Channel');
}
}
Now, doing the following would work:
public function channel($slug)
{
$channel = Channel::whereSlug($slug)->first();
return view('channel')->with('discussion', $channel->discussions);
}
If you are tied and cannot change the model name for whatever reason, then you need to specify a foreign key on your relationship definitions.
For example:
public function discussions()
{
return $this->hasMany('App\Discussion', 'channel_id');
}
public function channel()
{
// This one can stay the same as Laravel will try to match `channel_id`
// on the discussion table to the id on the channels table
return $this->belongsTo('App\Channel');
}
Fore more information, read about Eloquent Relationships.
Sorry, I was already writing this up when #Julien Metral commented, but this is an extension of what he already said :)

One to many relationship Laravel migration

I currently have a 1:1 relationship and I need it to be a one to many.
Such that Job Details can have multiple results for 1 Job Search.
Job Searches
public function up()
{
Schema::create('job_searches', function (Blueprint $table) {
$table->increments('id');
});
}
Job Details
public function up()
{
Schema::create('job_details', function (Blueprint $table) {
$table->integer('job_details_id',11);
$table->foreign('job_details_id')->references('id')->on('job_searches');
});
}
The current output I am getting is:
Job_Search_Id
1
Job_Detail_Id
1
When adding another result to the job details I get:
Illuminate\Database\QueryException with message 'SQLSTATE[23503]:
Foreign key violation: 7 ERROR: insert or update on table
"job_details" violates foreign key constraint
"job_details_job_details_id_foreign"
I also have stated the relationships in my models
Job Search Model
class JobSearches extends Model
{
protected $primaryKey = 'id';
public function job_details(){
return $this->belongsToMany('App\job_details');
}
}
Job Details Model
class JobDetails extends Model
{
protected $primaryKey = 'job_details_id';
public function job_search(){
return $this->hasOne('App\job_search');
}
Change the migration to:
public function up()
{
Schema::create('job_details', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('job_search_id');
});
Schema::table('job_details', function (Blueprint $table) {
$table->foreign('job_search_id')->references('id')->on('job_searches');
});
}
And the JobSearch class:
class JobSearch extends Model
{
public function jobDetails(){
return $this->hasMany('App\JobDetail');
}
}
And the JobDetail class:
class JobDetails extends Model
{
public function jobSearch()
{
return $this->belongsTo('App\JobSearch');
}
}
If you'll use the code without modification, it will work for you.

Resources