Eloquent eager loading relationship - laravel

Hello wonderful people of StackOverflow, I hope you all have a good day ( ˘⌣˘)♡(˘⌣˘ )
I'm new to Laravel and currently learning about eloquent relationships (hasMany)
I'm sorry this post was too long to read, but I wanted to clarify every step I did until I ran into problems, and also sorry if my english is bad (⁄ ⁄•⁄ω⁄•⁄ ⁄)
So here we go
In my User model
public function posts() {
// A user can have many posts
return $this->hasMany('App\Post');
}
In my Post model
public function owner()
{
// A post belongs to a user
return $this->belongsTo('App\User', 'user_id');
}
In my Post Table, I have 3 simple records
|----------------------------------------------------------|
| id | user_id | body | ... | ... |
| 1 | 1 | My first post | ... | ... |
| 2 | 1 | Second post | ... | ... |
| 3 | 1 | Another post | ... | ... |
And then let's say, we want to see the user with id = 1 with all posts they created, so I use the code below:
// AccountController
public function profile($id)
{
$user = App\User::with('posts')->findOrFail($id);
return $user;
}
// Will return data:
{
"id": 1,
"name": "Prof. Angela Runte Jr.",
"email": "mborer#example.org",
...
"posts": [
{
"id": 1,
"user_id": 1,
"body": "My first post",
...
},
{
"id": 1,
"user_id": 1,
"body": "Second post",
...
},
{
"id": 1,
"user_id": 1,
"body": "Another post",
...
}
}
And in Blade view, I can simply get data like below:
// in profile.blade.php
$user->name
$user->...
#foreach($user->posts as $post)
// Show each post data
#endforeach
It works perfectly the way I want, Thanks to Taylor Otwell for creating an amazing framework (´• ω •`) ♡
And then I had an idea in my mind, let's make a feature where a user can post to other users, why not? Let's do it! (✧ω✧)
So in my Post migration, I change the table schema:
From:
// Before
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignBigInteger('user_id');
$table->text('body');
$table->timestamps();
To:
// After
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBigInteger('user_id');
// New column: nullable()
$table->foreign('for_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBigInteger('for_id')->nullable();
$table->text('body');
$table->timestamps();
As you can see, I put nullable() in column 'for_id'
In words, if for_id is empty then this post belongs only to the user who made the post
Otherwise the post belongs to the user who made the post, as well as the intended user (for_id).
Then after that, I run
// I only use UserSeeder (wanna keep it simple tho)
php artisan migrate:fresh --seed
And now we have 2 users with id = 1 and 2 (Yay!)
|----------------------------------------------------------------------------------------|
| id | name | email | ... | ... |
| 1 | Camden Kulas | lemke.fabian#example.net | ... | ... |
| 2 | Mrs. Roberta Stroman | ybartoletti#example.com | ... | ... |
Let's use tinker for creating dummy data
php artisan tinker
>> $user = App\User::first();
=> App\User {#3910
id: 1,
name: "Camden Kulas",
email: "lemke.fabian#example.net",
...
}
>> $user->posts()->create(['body' => 'New first post']);
=> App\Post {#4120
body: "New first post",
user_id: 1,
updated_at: "2020-08-30 03:42:43",
created_at: "2020-08-30 03:42:43",
id: 1,
}
// we create one for Mrs. Roberta Stroman (user with id #2)
>> $user->posts()->create(['for_id' => 2, 'body' => 'Hello there']);
=> App\Post {#3912
for_id: 2,
body: "Hello there",
user_id: 1,
updated_at: "2020-08-30 03:44:18",
created_at: "2020-08-30 03:44:18",
id: 2,
}
So now we have 2 record in our posts table
|--------------------------------------------------------------------|
| id | user_id | for_id | body | ... |
| 1 | 1 | NULL | New first post | ... |
| 2 | 1 | 2 | Hello there | ... |
We're still in user id #1
>> $user->posts
=> Illuminate\Database\Eloquent\Collection {#4120
all: [
App\Post {#4125
id: 1,
user_id: 1,
for_id: null,
body: "New first post",
...
},
App\Post {#4128
id: 2,
user_id: 1,
for_id: 2,
body: "Hello there",
...
},
],
}
( ̄ω ̄) Ok 'till here, all works fine, let's try with user id #2
In my mind, I want to get user id #2, and with all posts made by id # 2, and all posts from others for id # 2
>> $user = App\User::find(2);
=> App\User {#4121
id: 2
name: "Mrs. Roberta Stroman",
email: "ybartoletti#example.com",
...
}
>> $user->posts
=> Illuminate\Database\Eloquent\Collection {#4067
all: [],
}
Σ(°ロ°) The posts is empty, so why the hell it's empty?
ఠ ͟ಠ Then I try to use with()
>> $user = App\User::with('posts')->find(2);
=> App\User {#4120
id: 2,
name: "Mrs. Roberta Stroman",
email: "ybartoletti#example.com",
...
posts: Illuminate\Database\Eloquent\Collection {#4123
all: [],
},
}
(ꐦ°᷄д°᷅) What?? Why The posts still empty? щ(ºДºщ)
So, after searching and reading for days, still I couldn't figured it out how to solve this (。╯︵╰。)
And then I tried to change in my User model: the posts() function
From:
public function posts()
{
// A post belongs to a user
return $this->hasMany('App\Post');
}
To:
public function posts()
{
// In my logic like:
// Hey I want to fetch all posts that made by this user, **or where all posts that created by others for this user**
// Make sense in human language right?
return $this->hasMany('App\Post', 'user_id')->orWhere('posts.for_id', $this->id);
}
I exited the tinker, then re-enter the tinker again
>> $user = App\User::first();
// return user id #1
>> $user->posts
// works fine, return all posts
(ಠ益ಠ) Now I am facing other problem
>> $user = App\User::with('posts')->first(); // notice that I'm using with()
// tinker return the desired user, and also return ONLY one post
>> App\User {#4123
id: 1,
name: "Camden Kulas",
email: "lemke.fabian#example.net",
....
posts: Illuminate\Database\Eloquent\Collection {#4130
all: [
App\Post {#4128
id: 1,
user_id: 1,
for_id: null,
body: "New first post",
created_at: "2020-08-30 03:42:43",
updated_at: "2020-08-30 03:42:43",
},
// where is the second post?
],
},
}
Then I try to change tinker session with user id #2
>> $user = App\User::find(2);
// return user id #2
>> $user = App\User::with('posts')->find(2);
// return user id #2, but no post (only array all)
So without writing further, the questions I want to ask
[#1] Why user #1 only fetch one post, meanwhile he created 2 posts?
One post without for_id (NULL), and second post with for_id
[#2] How to make user id #2 to fetch all their posts, and also posts that created for them?
Because IMHO posts() function in User model, perfectly make sense for me, but it doesn't work
If there's any unclear explanation, it will edit in time
Thanks in advance
Update
I figured it out that I can simply get all posts by user X and all posts for user X with this way:
$user = App\User::find(X);
$posts = $user->posts()->orWhere('for_id', X)->get();
return [
'user' => $user,
'posts => $posts
];
So now the question comes to my mind:
How to use with() for $posts?
// Like this
$user = App\User::with('allposts')->find(X);
// Return user X and all posts made by X and posted to user X
// So I can simply get data in view like this:
#foreach ($user->allposts as $post)
// Loop all posts by user X and for user X
#endforeach

Related

Attach new key and value by matching collection value

Hello great people of SO!
First of all, I'm sorry if my english is not very good, but I'm gonna try my best here to describe my problem
I have 3 Models
User
Post
Like
User.php
___________________________________________________________
| id | name | email | password | created_at | updated_at |
| ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... |
User model relationship:
public function posts() {
return $this->hasMany(Post::class, 'user_id', 'id');
}
Post.php
__________________________________________________
| id | user_id | body | created_at | updated_at |
| ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... |
Post model relationship:
public function user() {
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function likes() {
return $this->morphMany('likeable');
}
Like.php
_________________________________________________________________________
| id | likeable_type | likeable_id | user_id | created_at | updated_at |
| ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... |
| ... | ... | ... | ... | ... | ... |
Like model relationship:
public function likeable() {
return $this->morphTo('likeable');
}
Everything works fine, for simple C.R.U.D
The problem comes when I use Laravel Debugbar,
I saw so many repetitive queries just to fetch few records:
Ex:
// Let say that I have 5 users
$users = User::all();
foreach ($users as $user) {
$user->load('posts');
}
return $users;
// Result
select * from `posts`.`user_id` 1 ...
select * from `posts`.`user_id` 2 ...
select * from `posts`.`user_id` 3 ...
So I decide to change the method
Ex:
$users = User::all();
$posts = Post::whereIn('user_id', $users->pluck('id')->toArray())->get();
// Result:
select * `posts`.`user_id` in (1, 2, 3, 4, 5)
I no longer see repetitive query, which is good,
After solving this repetitive query, I fetch 'likes' from specific posts
Ex:
$users = User::all();
$posts = Post::whereIn('user_id', $users->pluck('id')->toArray())->get();
$posts_likes = Like::where('likeable_type', 'App\Post') // morphMany
->whereIn('likeable_id', $posts->pluck('id')->toArray())->get();
Now here's the problems, I do not know how to pair posts_likes to to it's post
Ex:
$posts = [
{
'id': 1,
'user_id': 2,
'body': 'Lorem ipsum...',
...
},
{
'id': 2,
'user_id': 2,
'body': 'Sit amet...',
...
},
{
'id': 3,
'user_id': 3,
'body': 'abcde...',
...
},
... etc
];
$posts_likes = [
{
'id': 1,
'likeable_type': 'App\Post',
'likeable_id': 2,
'user_id': 3,
...
},
'id': 2,
'likeable_type': 'App\Post',
'likeable_id': 2,
'user_id': 2,
...
{
'id': 3,
'likeable_type': 'App\Post',
'likeable_id': 1,
'user_id': 5,
...
},
{
'id': 4,
'likeable_type': 'App\Post',
'likeable_id': 3,
'user_id': 1,
...
},
... etc
];
My question:
How to insert likes inside $posts collection by matching exact id? (post id == like likeable_id)
So I can access them in loop, like: $post->likes = [...]
Ex:
$posts = [
{
'id': 1,
'user_id': 2,
'body': 'Lorem ipsum...',
'likes': [
// All likes for post with this id (1)
],
...
},
{
'id': 2,
'user_id': 2,
'body': 'Sit amet...',
'likes': [
// All likes for post with this id (2)
],
...
},
...
];
If there's any unclear explanation, I will edit it a.s.a.p
Thanks in advance
you can eagar load all of them
ref link https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading
$users = User::with('posts.likes')->get();
return $users;
this code will work if you set correct relationship
//user model
public function posts()
{
return $this->hasMany(Post::class, 'user_id', 'id');
}
//post model
public function likes()
{
return $this->morphMany('likeable');
}
i suppose to generate json link
[{
"name": "user",
"posts": [{
"name": "postName",
"likes": []
},
{
"name": "postName",
"likes": []
}
]
}]

Eloquent Polymorphic MorphMany with children

I have 3 models (User, Post, and Comment).
User model:
public function posts() // A user can have many posts
{
return $this->hasMany('App\Post', 'from_id');
}
Post model:
protected $with = [ // I'm using with() here
'from',
'for',
'comments',
];
public function from() // Post owner
{
return $this->belongsTo(User::class, 'from_id');
}
public function for() // To whom this post is addressed
{
return $this->belongsTo(User::class, 'for_id');
}
public function comments() // All comments for this post
{
return $this->morphMany(Comment::class, 'commentable');
}
Post migration:
$table->id();
$table->string('uuid');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('for_id')->references('id')->on('users');
$table->unsignedBigInteger('for_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Comment model:
protected $with = [ // I'm also using with() here
'from',
'children',
];
public function from() // Comment owner
{
return $this->belongsTo(User::class, 'from_id', 'id');
}
public function commentable()
{
return $this->morphTo();
}
public function children() // child comments for this comment (?) Not sure how it's working or not
{
return $this->hasMany(Comment::class, 'parent_id');
}
Comment migration:
$table->id();
$table->string('uuid');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('parent_id')->references('id')->on('comments');
$table->unsignedBigInteger('parent_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Note that I have two dummy users in my Users table.
Then let's say we have two records inside our Posts table:
|------------------------------------------------------|
| id | uuid | from_id | for_id | body | ... |
|------------------------------------------------------|
| 1 | .... | 1 | null | ... | ... |
| 2 | .... | 1 | null | ... | ... |
So let's create a comment for id #1:
php artisan tinker
>> $post = App\Post::first();
>> $post->comments()->create(['from_id' => 2, 'body' => 'Test comment']);
>> App\Comment {#3744
from_id: 2,
body: "Test comment",
commentable_id: 1,
commentable_type: "App\Post",
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
updated_at: "2020-09-01 15:00:38",
created_at: "2020-09-01 15:00:38",
id: 1,
}
Now let's see the first posts with comments (I'm using protected $with in my Post model):
php artisan tinker
>> $post = App\Post::first();
>> App\Post {#4030
id: 1,
uuid: "e9503551-99ac-495f-902e-b505408ab9ef",
from_id: 1,
for_id: null,
body: "Vero vel officia qui et. Veritatis laudantium itaque nisi sint repellendus laborum. Nihil at aliquam alias in.",
created_at: "2020-09-01 14:59:11",
updated_at: "2020-09-01 14:59:11",
deleted_at: null,
comments: Illuminate\Database\Eloquent\Collection {#4039
all: [
App\Comment {#4049
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4061
id: 2,
name: "Prof. Runte Jr.
email: "abigail#example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4040
all: [],
},
},
],
},
}
We can see the first post now has a comment, alright.
Now I want to create a child comment for the first comment inside the post before:
php artisan tinker
// get the first post, and then get the first comment of it
>> $post = App\Post::first()->comments()->first();
>> App\Comment {#4060
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4073
id: 2,
name: "Prof. Runte Jr.
email: "abigail#example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4030
all: [],
},
}
// Now we want to create child comment
// Get the first post
>> $post = App\Post::first()
// Get the first comment from it
->comments()->first()
// Get children relationship (IDK if this the right words to put it) and create a child comment
->children()->create(['from_id' => 1, 'body' => 'Testing child comment']);
This is what tinker returns:
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field 'commentable_id' doesn't have a default value (SQL: insert into comments (from_id, body, parent_id, uuid, updated_at, created_at) values (1, Testing child comment, 1, aa3d624f-3984-438c-adb0-26086459de33, 2020-09-01 15:38:36, 2020-09-01 15:38:36))'
So my question is:
How to make a child comment inside polymorphic relationship (morphMany())? I already set the column for parent_id which belongs to comment id.
As comments() return collection of comments. So, You should use this one like:
$post = App\Post::first();
foreach($post->comments() as $comment){
$comment->children()->create([
'from_id' => 1,
'body' => 'Testing child comment'
]);
}
You can also use save() method as:
$post = App\Post::first();
$comment = new App\Comment([
'from_id' => 1,
'body' => 'Testing child comment'
]);
$child = $post->comments()->first()->save($comment);
And You have to change in your App\Comment method children() with this :
public function children()
{
return $this->morphMany(Comment::class, 'commentable');
}

Updating a Whole Table (Laravel and Vuejs)

I have a table on admin panel which contains 60 rows and 10 columns. Those columns belongs 2 different tables in database. Simplified version is like so:
| Asset | Trade | Status | Code | Link |
Code and Link columns contain empty text inputs. When I enter anything, their values go to Vue.js object and I save them in the Posts table which works perfectly fine.
Trade and Status columns have radio buttons and they belong to Trades table. When I check the radio buttons, their values go to Vue.js object. When I try to update the Trades table it returns 405 error.
The button calls that function:
publish() {
this.form.post("/api/posts")
.then(() => {
this.form
.put("/api/trades")
})
.catch(() => {
//error codes...
});
}
I don't know if POST and PUT requests can be used together, or if this is the correct syntax, but I like to make both action with one click.
My update function is down below. As you can see I'm trying to use "asset_id" which is the foreign key in the Trades table, instead of "id" which is primary key of Trades table. I think this causes the problem, but I'm not sure. I also add that line of code to Post modal file, but it didn't help:
protected $primaryKey = 'asset_id';
My update function:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $asset_id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $asset_id)
{
$input = $request->all();
if (count($input['asset_id']) > 0) {
foreach ($input['asset_id'] as $asset_id) {
if (array_key_exists($asset_id, $input['status'])) {
$updates = [
'trade' => $input['trade'][$asset_id],
'status' => $input['status'][$asset_id],
'private' => (array_key_exists($asset_id, $input['public_code']) ? 0 : 1)
];
Post::where('asset_id', $asset_id)->find($asset_id)->update($updates);
}
}
}
}
When I check the radio buttons and fill the inputs Vue.js object becomes something like this:
form: new Form([
asset_id: [2, 5, 16, 52],
trade: {
2: "Long",
5: "None",
16: "Long",
52: "Short"
},
status: {
2: "Active",
5: "Canceled",
16: "Pending"
52: "Active"
},
public_code: {
2: "VBFABVR",
16: "hmbtr46"
},
link: {
2: "http://...",
16: : "http://..."
}
])
I'm not sure if I should provide more details. If it's so, please let me know. I hope someone tells me what is wrong here.
EDIT:
Route codes:
Route::apiResources([
'users' => 'API\UsersController',
'roles' => 'API\RolesController',
'plans' => 'API\PlansController',
'categories' => 'API\CategoriesController',
'assets' => 'API\AssetsController',
'posts' => 'API\PostsController',
'trades' => 'API\TradesController',
]);
Route List:
| POST | api/trades | trades.store | App\Http\Controllers\API\TradesController#store | api
| GET|HEAD | api/trades | trades.index | App\Http\Controllers\API\TradesController#index | api
| PUT|PATCH | api/trades/{trade} | trades.update | App\Http\Controllers\API\TradesController#update | api
| GET|HEAD | api/trades/{trade} | trades.show | App\Http\Controllers\API\TradesController#show | api
| DELETE | api/trades/{trade} | trades.destroy | App\Http\Controllers\API\TradesController#destroy | api
It was a rookie mistake. I was trying to pass a collection of the trades to database, but put request requires /api/trades/$id. That's why I've created a for loop in front-end, and in back-end, all I need was a simple update function.
This is the function that is called when I click the submit button:
publish() {
var trades = this.form.asset_id.length;
this.form.post("/api/posts")
for (var i = 0; i < trades; i++) {
if (this.form.asset_id[i] in this.form.status) {
this.form.put("/api/trades/" + this.form.asset_id[i])
}
}
}
And this is the upload function:
public function update(Request $request, $id)
{
$item = Trade::findOrFail($id);
$input = $request->all();
$updates = [
'trade' => $input['trade'][$id],
'status' => $input['status'][$id],
'private' => (array_key_exists($id, $input['public_code']) ? 0 : 1)
];
$item->update($updates);
}

Laravel relationship : one foreignKey for several localKey relationship

I have to develop an api with Laravel and i'm stuck with this problem:
I need to get all matchs and for each match i need to get the users who belongs to this match (there is 2 users per match)
I've search answer for this problem and i found this https://github.com/topclaudy/compoships but it didn't worked for me.
So far, i have this :
class Match extends Model
{
protected $table = 'matchs';
public $primaryKey = 'id';
public function playerOne() {
return $this->hasMany('App\User', 'id', 'playerOne_id');
}
public function playerTwo() {
return $this->hasMany('App\User', 'id', 'playerTwo_id');
}
}
Controller Method:
public function index()
{
$matchs = Match::with('playerOne', 'playerTwo')->orderBy('created_at', 'asc')->get();
return $matchs;
//return new MatchsCollection($matchs);
}
and my database look like this :
(Matchs table)
id | playerOne_id | playertwo_id | winner_id | status
------------------------------------------------------------
1 | 1 | 3 | 0 | PENDING
2 | 2 | 1 | 0 | PENDING
3 | 3 | 2 | 0 | PENDING
and users table :
id | name | email
-----------------------------------
1 | John | John#email.com
2 | Mark | Mark#email.com
3 | Harry | Harry#email.com
and when i call my api i get this result:
[
{
id: 1,
playerOne_id: 1,
playerTwo_id: 3,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-10 00:00:00",
updated_at: null,
users: [
{
id: 1,
name: "John",
email: "John#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
}
]
},
{
id: 2,
playerOne_id: 2,
playerTwo_id: 1,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-11 08:26:28",
updated_at: "2019-01-11 08:26:28",
users: [
{
id: 2,
name: "Mark",
email: "Mark#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:40:13",
updated_at: "2019-01-11 10:40:13"
}
]
},
{
id: 3,
playerOne_id: 3,
playerTwo_id: 2,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-11 08:45:22",
updated_at: "2019-01-11 08:45:22",
users: [
{
id: 3,
name: "harry",
email: "harry#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:40:13",
updated_at: "2019-01-11 10:40:13"
}
]
}
]
What i wan't to get is this result (i just show you the first match)
[
{
id: 1,
playerOne_id: 1,
playerTwo_id: 3,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-10 00:00:00",
updated_at: null,
users: [
{
id: 1,
name: "John",
email: "John#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
},
{
id: 3,
name: "Harry",
email: "Harry#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
}
]
}
]
is this possible in Laravel ? thanks :)
To be able to return all the players as part of the user array you could either change the relation between Match and User to be a belongsToMany (many-to-many) or simply manipulate the match data before returning it from the controller.
Changing it to a BelongsToMany would be my recommendation since it semantically makes more sense, however, I understand that (depending on how much work you've done so far) it may not be practical.
You would need to:
rename your matchs table to be matches (this will also mean you can remove
protected $table = 'matchs'; from your Match model class ).
Create a table called match_user. If your project is using migrations then you can run:
php artisan make:migration create_match_user_table --create=match_user
In that migration you would need to change the contents of the up() method to be:
public function up()
{
Schema::create('match_user', function (Blueprint $table) {
$table->integer('match_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->primary(['match_id', 'user_id']);
});
}
Run php artisan migrate
In your Match model change the users() relationship to be:
public function users()
{
return $this->belongsToMany(User::class)->withTimestamps();
}
If the data you have in your tables is dummy data you can skip this step. Otherwise, you'll need to move the playerOne_id and playertwo_id information to the new pivot table. You could do that will the following:
Match::all()->each(function ($match) {
$match->users()->attach([$match->playerOne_id, $match->playertwo_id]);
});
It doesn't really matter where you run this as it only has to be run once so after you've run it you can delete it. I would usually run this in a route or in tinker.
Then comes the clean up. Again, if the data you have is just dummy data, then I would just remove the playerOne_id and playertwo_id the original migration, otherwise create a new migration that removes those field Dropping columns docs
Once the above has been done you would just need to do the following in your controller to get the results:
$matches = Match::with('users')->latest()->get();

Laravel Eloquent with 'with' and 'wherehas'

Let's say I have three databases, player, credit, and photo, linked with foreign keys:
player
id | name | address
credit
id | player_id | credit_status
photo
id | player_id
Say I want to get all players who has credit_status $status, I'd do it like this:
$status = 'bar';
Player::with('photo','credit')->whereHas('credit', function ($q) use ($status) {
$q->where('credit_status', $status)->with('credit_status');
})->paginate(15);
This will lists all players who has credit_status $credit, but it still lists all credits by that player regardless of the status.
The output is something like:
{
id: 1
name: Andrew
address: home
photo: {
id: 2
photo: image1
}
credit: {
[
{
id: 6
credit_status: foo,
id: 2
credit_status: bar
}
]
}
},
{
id: 2
name: Mark
address: home
photo: {
id: 5
photo: image4
}
credit: {
[
{
id: 10
credit_status: foo,
id: 6
credit_status: bar,
id: 8
credit_status: bar
}
]
}
}
I want to filter the credit in with('credit') also.
My desired output:
{
id: 1
name: Andrew
address: home
photo: {
id: 2
photo: image1
}
credit: {
[
{
id: 2
credit_status: bar
}
]
}
},
{
id: 2
name: Mark
address: home
photo: {
id: 5
photo: image4
}
credit: {
[
{
id: 6
credit_status: bar,
id: 8
credit_status: bar
}
]
}
}
You can just do the same filtering on the with (constraining eager loading):
$creditFilter = function ($q) use ($status) {
$q->where('credit_status', $status);
};
Player::with(['photo', 'credit' => $creditFilter])
->whereHas('credit', $creditFilter)
->paginate(15);
You can save that closure and pass it to the with and whereHas so you don't have to type the same identical closure twice.
Laravel 5.6 Docs - Eloquent - Relationships - Eager Loading - Constraining Eager Loads
If you want to filter credits too then you have to also use the condition in the credit. Actually whereHas() and with() work independently they are not dependent on each other.
$status = 'bar';
Player::with(['photo','credit' => function($query) use ($status){
$query->where('credit_status', $status)->with('credit_status');
}])->whereHas('credit', function ($q) use ($status) {
$q->where('credit_status', $status)->with('credit_status');
})->paginate(15);
You only need to make conditional with the 'with' function; not with the 'whereHas' function.
$creditFilter = function ($q) use ($status) {
$q->where('credit_status', $status);
};
Player::with(['photo', 'credit'])
->with(['credit' => $creditFilter])
->paginate(15);

Resources