Laravel miltiple level relationship with limit() - laravel

I have some problem with Laravel Eloquent and need your help.
I want to display 1 replies of each comment of single post.
Here is my tables
posts (id,title)
id | title
---------------------
1 | My post
---------------------
comments(id,post_id,comment,parent_id)
id | post_id | comment | parent_id
-----------------------------------------
1 | 1 | First comment | null
-----------------------------------------
2 | 1 | Second comment | null
-----------------------------------------
3 | null | 3rd comment | 1
-----------------------------------------
4 | null | 4th comment | 1
-----------------------------------------
5 | null | 5th comment | 2
-----------------------------------------
6 | null | 6th comment | 2
-----------------------------------------
My model (Eloquent)
class Post extends Model
{
public function comments()
{
return $this->hasMany('Comment', 'post_id');
}
}
---------------------
class Comment extends Model
{
public function reply()
{
return $this->hasMany('Comment', 'parent_id');//self relationship
}
}
My query function
public function getPost($postId){
$posts = Post::with(['comment.reply'=>function($q){
$q->limit(1);
}])
->find($postId);
return $posts;
}
And I get result
{[
id=>1,
title=>'My post',
'comments'=>[
0=>[
id=>1,
comment=>'First comment',
parent_id=>null,
post_id=>1,
reply=>[
0=>[........(comment id:3).......]
]
],
1=>[
id=>2,
comment=>'Second comment',
parent_id=>null,
post_id=>1,
reply=>null
]
]
]}
But I want like this
{[
id=>1,
title=>'My post',
'comments'=>[
0=>[
id=>1,
comment=>'First comment',
parent_id=>null,
post_id=>1,
reply=>[
0=>[........(comment id:3,4)........]
]
],
1=>[
id=>2,
comment=>'Second comment',
parent_id=>null,
post_id=>1,
reply=>[
0=>[........(comment id: 5,6).........]
]
]
]
]}
Please kindly help!

Try this:
$posts=Post::where(['id'=>1])->with(['comments'=>function($query)
{
$query->with(['replies'=>function($query)
{
$query->limit(1);
}]);
}])->first();
print_r($posts);

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": []
}
]
}]

Laravel model observer method doesn't get fired

In my Laravel (7.x) I am trying to use Observers for updating device_inventories table if the value of the record if the record of the subscriber_devices table is updated as inactive.
device_inventories | subscriber_devices
----------------------- | --------------------------------
# | title | status | # | device_inventory_id | status
----------------------- | --------------------------------
1 | device-1 | active | 1 | 1 | active
2 | device-2 | active | 2 | 2 | active
For example:
Suppose I update the record id = 2 of subscriber_devices to status = inactive then the value of device_inventories / status should be updated to replacement. Assuming, that the device was damaged and needs to be sent for replacement.
AppServiceProvider.php
use App\SubscriberDevice;
use App\Observers\ObserverChangeDeviceInventoryStatusToReplacement;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
SubscriberDevice::observe(ObserverChangeDeviceInventoryStatusToReplacement::class);
}
}
App\Observers\ObserverChangeDeviceInventoryStatusToReplacement.php
use App\DeviceInventory;
use App\SubscriberDevice;
class ObserverChangeDeviceInventoryStatusToReplacement
{
public function updated(SubscriberDevice $SubscriberDevice)
{
DeviceInventory::where('id', $SubscriberDevice->device_inventory_id)->update([
'status' => 'replacement'
]);
}
}
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
self::where('token', $token)->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
The observer method doesn't seems to be firing. What am I doing wrong.?
Thanks to #EmptyBrain.
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
$self = self::where('token', $token)->first();
$self->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
Reference laravel Eloquent model update event is not fired

Laravel eloquent get count on belongsTo id

I have 2 tables subscribers & subscriber_packages. I need to fetch the count of currently active packages for the selected subscriber via given subscriber_package / id.
Tables:
# | subscriber_id | package_id | active
---------------------------------------
1 | 1 | 1 | true
2 | 1 | 2 | true
3 | 1 | 3 | true
4 | 1 | 4 | false
---------------------------------------
App\SubscriberPackage.php
class SubscriberPackage extends Model
{
public function subscriber()
{
return $this->belongsTo(Subscriber::class);
}
public function package()
{
return $this->belongsTo(Package::class);
}
public function _countActivePackages($subscriberPackage)
{
return self::where([
'id' => $subscriberPackage,
'status' => \Common::STATUS_ACTIVE,
])->whereHas('subscriber')->count();
}
}
At your subscriber you need to define correctly a HasManyThrough relationship and just add properly a where cause to check the status even in separate method
public function packages()
{
return $this->hasManyThrough(Package::class, SubscriberPackage::class);
}
public function activePackages()
{
return $this->packages()
->where("subscriber_package.status", "=", "active");
}
After that you can always make a count with it
$subscriber->activePackages()->count();

Eloquent eager loading relationship

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

How use conditional relationship in eloquent laravel

I have a 'conversation_message' table and separate sender role by column 'sender_type' (admin/user). Both of admin & user in a different table. But when I call the model, that showed error Call to a member function addEagerConstraints() on null
Table column and data
| id | id_group | id_reply | id_sender | sender_type | message
| 1 | 1 | null | 3 | admin | Hi, I'm admin
| 2 | 1 | 1 | 3 | admin | I wanna give u promo
| 3 | 1 | 2 | 18 | user | What's promo ?
I've tried if conditional with column value, but it doesn't work.
Conversation_message.php
public function sender(){
switch($this->sender_type){
case "user":
return $this->belongsTo(User::class, 'id_sender', 'id');
case "admin":
return $this->belongsTo(Admin::class, 'id_sender', 'id');
default:
return;
}
}
InboxController.php
public function message_detail_chat($groupId){
$data = [];
$data['messages'] = Conversation_message::with('replies')
->with('sender')
->with('recipients')
->where(['id_group' => $groupId])->get();
}
I expect to use conditional model by column value 'sender_type' but the actual output is wrong.
laravel provide query scope your can read it from here https://laravel.com/docs/5.8/eloquent#local-scopes
function userSender(){
$this->belongsTo(User::class, 'id_sender', 'id');
}
function adminSender(){
return $this->belongsTo(Admin::class, 'id_sender', 'id');
}
public function scopeSender($query)
{
return $query
->when($this->sender_type === 'user',function($q){
return $q->with('userSender');
})
->when($this->sender_type === 'admin',function($q){
return $q->with('adminSender');
});
}
Now you can access your Sender like this
Conversation_message::Sender()->first();
This should give you the right Sender. Hope it helps.

Resources