Laravel: N to N (mysql) relation using Eloquent ORM - php

I got 4 tables:
// Table countries
+----+------+
| Id | Name |
+----+------+
| 1 | USA |
| 2 | GB |
+----+------+
// Table platforms
+----+---------+
| Id | Name |
+----+---------+
| 1 | Windows |
| 2 | Linux |
+----+---------+
// Table users
+----+-------+------------+-------------+
| Id | Name | country_id | platform_id |
+----+-------+------------+-------------+
| 1 | Admin | 1 | 1 |
| 2 | Test | 2 | 1 |
+----+-------+------------+-------------+
// Table posts
+----+-----------+------------+-------------+---------+
| Id | Title | country_id | platform_id | user_id |
+----+-----------+------------+-------------+---------+
| 1 | TestPost1 | 2 | 1 | 1 |
| 2 | TestPost2 | 2 | 2 | null |
+----+-----------+------------+-------------+---------+
The database should be able to implement the following relations:
User (N) <-> (N) Platform
User (N) <-> (N) Country
User (0..1) <-> (N) Post
Post (N) <-> (N) Country
Post (N) <-> (1) Platform
So now I tried to implement these relations following Laravel Eloquent ORM documentation:
// Country.php
public function posts()
{
return $this->belongsToMany('App\Post');
}
public function users()
{
return $this->belongsToMany('App\User');
}
// Platform.php
public function users()
{
return $this->belongsToMany('App\User');
}
public function posts()
{
return $this->belongsToMany('App\Post');
}
// User.php
public function posts()
{
return $this->hasMany('App\Post');
}
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Platform');
}
// Post.php
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Comment');
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
But now I am confused, as I thought the way to implement N to N relations in mysql is to add a third table to db, for example like that:
// Table CountryUserRelations to implement User (N) <-> (N) Country
+----+------------+---------+
| Id | country_id | user_id |
+----+------------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 2 |
+----+------------+---------+
But how does Eloquent ORM handle the rules inside my model? Will it keep the N to N relations without having to add a relations table? Or am I missing something or misunderstanding the Eloquent ORM Relations concept?

I just joined stackoverflow so I do not have enough credit to comment so I will leave an asnwer here.
First of all please correct your relationship definition.
in User Model:( you have mistake here)
public function countries(){
return $this->belongsToMany(Country::class);
}
and in your Country Model:
public function users(){
return $this->belongsToMany(User::class);
}
second you need to create country_user table using:
php artisan make:migration create_country_user_table
after it you need to complete your table:
Schema::create('country_user', function (Blueprint $table){
$table->increments('id');
$table->unsignedInteger('country_id');
$table->unsignedInteger('user_id');
$table->foreign('country_id')->references('id')->on('countries');
$table->foreign('user_id')->references('id')->on('users');
}

Related

Three-way pivot Table Laravel

I'm struggling to connect three models in my Laravel app. The models are Bottle, Label and Spirit. I want to get all the labels based on bottle_id and spirit_id so I created a pivot table to store the relationships between Bottle-Label-Spirit. Please see below my current setup.
DB
+---------+--------+---------+-------------------------+
| bottles | labels | spirits | bottle_label_spirit |
+---------+--------+---------+-------------------------+
| id | id | id | id |
| name | name | name | bottle_id |
| | | | label_id |
| | | | spirit_id |
| | | | created_at |
| | | | updated_at |
+---------+--------+---------+-------------------------+
Where bottle_label_spirit is my pivot table
BOTTLE CLASS
class Bottle extends Model
{
public function labels() {
return $this->belongsToMany(Label::class)->withTimestamps();
}
public function spirits() {
return $this->belongsToMany(Spirit::class)->withTimestamps();
}
}
LABEL CLASS
class Label extends Model
{
public function bottles() {
return $this->belongsToMany(Bottle::class)->withTimestamps();
}
public function spirits() {
return $this->belongsToMany(Spirit::class)->withTimestamps();
}
}
SPIRIT CLASS
class Spirit extends Model
{
public function labels() {
return $this->belongsToMany(Label::class)->withTimestamps();
}
public function bottles() {
return $this->belongsToMany(Bottle::class)->withTimestamps();
}
}
QUESTIONS
So my questions are:
Is this the right approach to handle this manyToMany relationships?
If yes, how do i get all the labels where bottle_id = 1 and spirit_id = 1
You do not need a 4th table for this, you can accomplish all of this using hasManyThrough. This will allow you to query distant relationships.

Laravel Relationship - Additional fields in pivot table linking to additional models

I have simplified the problem to get to the point. I have three tables, users, roles, account.
Normally I would set up the User model to have a many to many relationships with roles but I want those roles to be specific to each account. So I have added an additional field to the pivot table. Here are the tables and fields that I have;
‘users’ table
|—————————
| id | name |
|—————————
| 1 | Bob |
| 2 | Jim |
| 3 | Fred |
|—————————
‘roles’ table
|—————————
| id | title |
|—————————
| 1 | Administrator |
| 2 | Manager |
| 3 | Approver |
|—————————
‘accounts’ table
|—————————
| id | name |
|—————————
| 1 | ABC Company |
| 2 | XYZ Shipping |
| 3 | KLM Transport |
|—————————
I then have the pivot table role_user with an additional pivot field for the account;
|—————————
| role_id | user_id | account_id
|—————————
| 1 | 3 | 1
| 2 | 2 | 1
| 3 | 2 | 3
| 3 | 1 | 2
|—————————
I have used the withPivot function on the belongsToMany function when setting up the many to many relationships. This allows me to get the information using $user->roles->pivot->account_id but what I need is to be able to get the name of that company. All it’s passing to the blade template is the id from the pivot table and not linking that to an actual Account model.
Is there a way with Eloquent to get this entire model in the same way as the original relationship?
Create a Custom Pivot Model
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUserAccountPivot extends Pivot
{
public function user()
{
return $this->belongsTo(User::class);
}
public function role()
{
return $this->belongsTo(Role::class);
}
public function account()
{
return $this->belongsTo(Account::class);
}
}
Update your belongsToMany relationships
Bellow is an example with the User::roles relationship
class User //extends...
{
public function roles()
{
return $this->belongsToMany(Role::class, /*other parameters*/)->using(RoleUserAccountPivot::class)->withPivot('account_id');
}
}
Usage
$user->roles->first()->pivot->account // returns Account model
Hope it helps.
Reference links:
Laravel doc on custom pivots

How to create a Pivot table in Laravel

I have a sponsorship app I'm creating and trying to get a pivot table working between my users and kids, but when I have a user sponsor a kid, the pivot table kid_user is not populated with the kid_id or the user_id. Not sure what I'm missing.
A Kid can have as many users (sponsors) as their slots allow them.
Users can sponsor a kid, multiple kids or the same kid multiple times
if needed.
Here is my kid_user pivot table:
public function up()
{
Schema::create('kid_user', function (Blueprint $table) {
$table->integer('kid_id')->unsigned()->index();
$table->foreign('kid_id')->references('id')->on('kids')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->primary(['kid_id', 'user_id']);
});
}
Then on the User model: The user can have many kids...
public function kid()
{
return $this->hasMany(Kid::class);
}
Then on the Kid model: The kids can have many users...
public function user() {
//return $this->belongsTo(User::class); (Tried this..)
return $this->hasMany(User::class);
}
Here are the tables I'm using. (Only relevant info is included in the tables)
+---------------------+
| Tables_in_Database |
+---------------------+
| kid_user |
| kids |
| users |
+---------------------+
+-----------------------------+
| users table |
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | John | Smith |
| 2 | Jane | Doe |
+----+------------+-----------+
+-----------------------------+
| kids table |
+----+------------+-----------+-------+
| id | first_name | last_name | slots |
+----+------------+-----------+-------+
| 1 | Bobby | Little | 3 | -> Can be sponsored 3 times
+----+------------+-----------+-------+
+--------------------+
| kid_user table | THE BELOW RESULTS ARE WHAT I'M LOOKING FOR.
+--------------------+
| *kid_id | *user_id | * = Primary Key
+---------+----------+
| 1 | 1 | -> Sponsored 1st Slot
+---------+----------+
| 1 | 2 | -> Sponsored 2nd Slot
+---------+----------+
| 1 | 1 | -> Sponsored 3rd Slot
+---------+----------+
So when a User sponsors a Kid. I would like the kid_id & user_id to be entered into the kid_user pivot table. Any help would be appreciated.
First of all you might wanna rename the functions in your models in plural as it does not have one but many from the relationship.
So in your user model add this:
public function kids()
{
return $this->belongsToMany(Kid::class);
}
And in your Kid model:
public function users()
{
return $this->belongsToMany(User::class);
}
Then in order to save to the pivot table since your table naming is correct, just doing:
$user->kids()->attach($kid);
Will save it properly in the pivot table. Making sure first that you have existing User and Kid for the variables. More details here

How to Create Relation in Eloquent Relationships Laravel

I have Two Table
1- projects
User with id 5 has created 3 project - which means he will have 3 unique conversation with our different agent
----------------------------------------
|id | user_id | title | time |
|1 | 5 | Example | 2018-06-30 |
|2 | 5 | Example | 2018-06-30 |
|3 | 5 | Example | 2018-06-30 |
----------------------------------------
2- conversation
-------------------------------------------
|id | project_id | user_one | user_two |
|1 | 1 | 5 | 3 |
|2 | 2 | 5 | 7 |
|3 | 3 | 5 | 10 |
-------------------------------------------
Whenever a project is created, a conversation is created with that project id, Now in Laravel i want to get that project detail using Eloquent Relationships.
User_one is the project creator and user_two is our agent assigned to that product.
This is What I've tried
class Chat extends Model {
public function project()
{
return $this->belongsTo('App\ProjectModel');
}
}
class ProjectModel extends Model
{
public $table = 'projects';
}
Here is the controller function
public function Progress($id){ // Id passed here is 2, so it should show detail of project number 2
return \App\Chat::find($id)->project()->get();
}
After all this I'm getting an error - Call to a member function project() on null
You can try like this with creating 2 Models Project and Conversation
table
conversations(id, project_id, creator, agent)
Project Model
public function conversations(){
return $this->hasMany('App\Conversation');
}
Conversation Model
public function project(){
return $this->belongsTo('App\Project', 'project_id');
}
public function creatorUser(){
return $this->belongsTo('App\User', 'creator');
}
public function agentUser(){
return $this->belongsTo('App\User', 'agent');
}
Fetch Data
public function Progress($id){ // Id passed here is 2, so it should show detail of project number 2
$chats = Conversation::with('project', 'creatorUser', 'agentUser')->where('project_id', 2)->get();
foreach($chats as $chat){
dd($chat->project); //it will always print same project because we have filter the conversation by project_id
dd($chat->creatorUser);
dd($chat->agentUser);
}
}
you can create a relation Along with specifying foreign Key and owner key like:
class Chat extends Model {
public function project()
{
return $this->belongsTo(App\ProjectModel::class, 'project_id', 'id');
}
}
and then you will be able to get:
\App\Chat::where('id', $id)->first()->project;

Laravel BelongsToMany Yielding Nothing

I'm trying to get the output of a belongstoMany relationship between users and sections database, which are tied to User and Section model, respectively.
User model has this method:
public function section()
{
return $this->belongsToMany('App\Models\Section','user_section')->withTimestamps();
}
Section model has this method:
public function user()
{
return $this->belongsToMany('App\Models\User','user_section')->withTimestamps();
}
My user_section table looks like this:
+----+---------+------------+------------+------------+
| id | user_id | section_id | created_at | updated_at |
+----+---------+------------+------------+------------+
| 1 | 1 | 1 | NULL | NULL |
| 2 | 2 | 1 | NULL | NULL |
+----+---------+------------+------------+------------+
2 rows in set (0.00 sec)
So why when I do this in the business logic:
$user = User::where("id",Auth::user()->id)->first(); //user with id of 1 in my case
return json_encode($user->section());
Do I get {} as output?
$user->section() will return the BelongsToMany relationship instead of the model object itself. You can do $user->section instead to load the relationship and have the model returned.

Categories