What eloquent relations to use? - laravel

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);
}
}

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

Laravel creates a one-to-many relationship instead of one-to-one

When I create a one-to-one relationship migration, laravel creates a one-to-many relationship. I tried to solve this in different ways and nothing worked.
How can I solve this?
Company:
class Company extends Model
{
public function user()
{
return $this->hasOne(User::class);
}
...
}
User:
class User extends Authenticatable
{
public function company(){
return $this->belongsTo(Company::class);
}
...
}
Migrations:
Schema::create('Company', function (Blueprint $table) {
$table->mediumIncrements('idCompany');
...
});
Schema::create('User', function (Blueprint $table) {
$table->id();
$table->increments('idUser');
$table->unsignedMediumInteger('Company_idCompany')
->unique()
->nullable();
$table->foreign('Company_idCompany')
->references('idCompany')
->on('company')
->onDelete('set null');
...
});
Laravel is creating nothing (on the migration), you always have to manually create the Model relationship (you are using hasOne and belongsTo, so that is 1-to-1) and migrations (you are creating User and Company not following the standards).
So, update your migrations to:
Schema::create('company', function (Blueprint $table) {
$table->id();
...
});
Schema::create('user', function (Blueprint $table) {
$table->id();
$table->increments('user_id');
$table->foreignId('company_id')
->unique()
->nullable();
$table->foreign('company_id')
->references('id')
->on('company')
->onDelete('set null');
...
});
See that I have moved everything to lowercase and snake case, remember to follow Laravel conventions or you are going to have a harder time working with Models...
Then, your relationships are correct:
class Company extends Model
{
public function user()
{
return $this->hasOne(User::class);
}
...
}
class User extends Authenticatable
{
public function company(){
return $this->belongsTo(Company::class);
}
...
}
So, when you do access a relationship, it will work out of the box now.
If you do Company::first()->user, that will return User or null, and if you do User::first()->company, that will return Company or null, there is no 1-to-N.

Laravel 8 pivot working only in 1 direction

I got a problem I have done model and migration for 3 tables: movies, actors and actor_movie (pivot)
when Im using from model Actor method movie() it working but don't work from Movie using actor()
class Movie extends Model
{
use HasFactory;
public function actors()
{
return $this->belongsToMany(Actor::class, 'actor_movie');
}
}
class Actor extends Model
{
use HasFactory;
public function movies()
{
return $this->belongsToMany(Movie::class, 'actor_movie');
}
}
movies migration:
public function up()
{
Schema::create('movies', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
actor migration:
public function up()
{
Schema::create('actors', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
Pivot migration:
public function up()
{
Schema::create('actor_movie', function (Blueprint $table) {
$table->bigInteger('movie_id')->unsigned()->index();
$table->foreign('movie_id')->references('id')->on('movies')->onDelete('cascade');
$table->bigInteger('actor_id')->unsigned()->index();
$table->foreign('actor_id')->references('id')->on('actors')->onDelete('cascade');
$table->primary(['actor_id', 'movie_id']);
$table->timestamps();
});
}
As said in the comments, you are running your tests in a Tinker console.
Tinker console loads all your PHP files and dependencies on startup and keep them in memory.
For your code to be refreshed, you need to kill tinker and restart it

(Laravel) Polymorphic relation through pivot table

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
}

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