Querying MorphToMany Relation Laravel - php

The thing I want to get from the database is that get all the posts with the data which will identify whether the post is liked by the auth()->user() or not. Most probably via count.
App\Post
public function likes()
{
return $this->morphToMany('App\User', 'likeable');
}
App\User
public function likePosts()
{
return $this->morphedByMany('App\Post', 'likeable')->withTimestamps();
}
Likeables Table
Likeables table has ('user_id', 'likeable_id', 'likeable_type')
I tried using orWhereHas
$posts = Post::with( ['user', 'tags', 'category'])->orwhereHas('likes', function($q) {
$q->where('user_id', auth()->id());
})->latest()->withoutTrashed()->paginate(10);
But with about query I am only getting those posts which the user has liked. I want to get all posts and a check whether the post is liked by the user or not
I came across whereHasMorph but it was only for morphTo and not for morphToMany.

#m____ilk I was able to solve this but creating a mutator:
public function isLiked()
{
return $this->likes()->where('user_id', auth()->id())->count() > 0;
}
I ran a loop on the posts and attached a custom attribute to a single post based on the mutator.
$posts = Post::with( ['user', 'tags', 'category', 'post.user', 'post.tags', 'post.category'])->latest()->withoutTrashed()->paginate(10);
foreach ($posts as $post) {
// Mutator Condition
if ($post->is_liked) {
// Custom Attribute
$post->isLiked = 1;
} else {
$post->isLiked = 0;
}
}
return $posts;

In laravel 9 I did something like:
$posts = Post::with( ['user', 'tags', 'category', 'post.user', 'post.tags', 'post.category'])
->withCount([
'likes',
'likes as is_liked' => function($q) {
$q->where('user_id', auth()->id());
}
])->latest()->withoutTrashed()->paginate(10)

Related

How Can I get Latest 5 Commentors user information By Laravel Relational Eloquent eager loading

I'm in a situation where I need to display the last 5 unique commenters information at the top of the comment list as follows screenshot.
comment image
To do this. I did as follows:
Post Model
public function comments()
{
return $this->hasMany(Comment::class);
}
public function commenter_avatars(){
return $this->comments()->distinct('user_id')
->select('id','post_id','user_id','parent_id')
->whereNull('parent_id')
->with('user')->limit(5);
}
My Controller method as follows
public function index() {
$feeds = auth()->user()
->posts()
->with(['user:id,first_name,last_name,username,avatar', 'media', 'commenter_avatars'])
->orderBy('id', 'desc')
->paginate(10);
return PostResource::collection($feeds);
}
I tried to use groupBy and Distinct.. But did't work as expected.
Did I miss something? or Have there any more best way to solve this?
Thank you in advance!
Noted: I am using latest Laravel (8.48ˆ)
I don't know about your joining of post, user and comments table. But i guess, you can do something similar to following.
At first get latest 5 unique user id of one post:
$userIds = Comments::where("post_id", $post_id)->distinct("user_id")->orderBy("id")
->limit(5)->pluck('user_id');
Then, fetch those user information
$users = Users::whereIn("id", $userIds )->get();
Then, you can return those users
UPDATE
You may use map() to fetch and reorder output. Following is an idea for you:
In Controller:
public function index(Request $request) {
$skipNumber = $request->input("skip"); // this is need for offsetting purpose
$userIds = [];
$feeds = Posts::with("comments")->where("comments.user_id", Auth::id())
->skip($skipNumber)->take(10)->orderBy('comments.id', 'desc')
->map(function ($item) use($userIds){
$users = [];
$count = 0;
foreach($item["comments"] as $comment) {
if(!in_array($comment["user_id"], $userIds) && $count < 5){
$count++;
$userIds.push($comment["user_id"])
$user = User::where("id", $comment["user_id"])->first();
$users.push($user);
}
if($count == 5) break;
}
$data = [
"post" => $item,
"latest_users" => $users
];
return $data;
})->get();
return PostResource::collection($feeds);
}
My code syntax may be slightly wrong. Hopefully you will get the idea.
I have solved this issue by using eloquent-eager-limit
https://github.com/staudenmeir/eloquent-eager-limit

reduce database query to one and avoid Call to a member function load() on null error

I have this function:
public function show($id)
{
if (count($post = Post::find($id))) {
$post = $post->load(['comments' => function ($q) {
$q->latest();
$q->with(['author' => function ($q) {
$q->select('id', 'username');
}]);
}, 'user' => function ($q) {
$q->select('id', 'username');
}]);
$this->authorize('seePost', $post);
return view('post.show', ['post' => $post]);
} else {
dd('no post');
}
}
I added the if statement as if I try to open a route to a non existent post id I get the error Call to a member function load() on null.
However now I have two queries, one looks for the Post in the DB and if it finds one then I have to load the relations with the second one. What can I do to go back to just one query with all the relations loaded and avoid the error? Any clue?
You can use Constraining Eager Loads do it like this:
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
$post = Post::with(["comments" => function ($query) {
// Order by created_at, query comment author & select id, username
$query->latest()->with(["author" => function ($q) {
$q->select("id", "username");
}]);
}, "user" => function ($query) {
// Query post author & select id,username
$query->select("id", "username");
}])
// Fetch post or throw a 404 if post is missing
->findOrFail($id);
// You can also return an empty post instance like this if post is missing
// ->findOrNew([]);
// Or return the post or null if post is missing
// ->find($id);
// Authorize
$this->authorize('seePost', $post);
return view("post.show", ["post" => $post]);
Laravel has an Eager Loading feature that would be helpfull in your case. Eager Loading allows you to autoload relations along with the same query that you use to retrieve your main model info. https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
You could a below codes.
Easiest way is :
$post = Post::with('comments.author', 'user')
->find($id);
Or fine tune query with callback :
$post = Post::with(['comments' => function ($q) {
// if you use comments select, then you need to specify foreign key too
$q->select('id', 'author_id', 'details') // comment fields
->latest(); // Use chaining method
// OR use $q = $q->latest();
},
'comments.author' => function ($q) {
$q->select('id', 'username'); // author fields
},
'user' => function ($) {
$q->select('id', 'username'); // user fields
}])
->find($id);
In some cases you might need some modifications, bu in overall that should avoid you N+1 queries problem.

dd function not able to fetch content from database

I am building a post system in which user can post on his wall.Posts are created successfully and are saving into database but it can't be fetched from the database.
I've also relationship between user and posts.
my postcontroller:
public function getmyplace()
{
$posts=Post::where(function($query){
return $query->where('user_id',Auth::user()->id)
->orWhere('user_id',Auth::user()->friends()->lists('id')
);
})
->orderBy('created_at','desc')->get();
dd($posts);
$accounts=Account::orderBy('updated_at','desc')->limit(1)->get();
return view('myplace',['posts'=>$posts,'accounts'=>$accounts]);
}
public function postCreatePost(Request $request)
{
Auth::user()->posts()->create([
'body' => $request->input('body'),
]);
return redirect()->route('myplace');
}
my database:
output:
"Return" statement is not applyed in nested query builder and Collection::lists() method return array. Try this:
$posts = Post::where(function($query){
$query->where('user_id',Auth::user()->id)
->orWhereIn('user_id',Auth::user()->friends()->lists('id'));
})
->orderBy('created_at','desc')->get();
dd($posts);

Selecting required fields using relations in Laravel

I have a model Meetings like this:
public function meeting_comments(){
return $this->hasMany('App\MeetingsComments', 'meeting_id', 'id');
}
public function meeting_users() {
return $this->hasMany('App\UserMeetingDetails', 'meeting_id', 'id');
}
The Controller is like this:
$res = Meetings::with('meeting_comments', 'meeting_users')
->select('')->get()->toArray();
I only need comments from meeting_comments and user_id from meeting_users.
What do I put in select to only get the required fields from meeting_comments and meeting_users ??
You do it through a closure in the with call:
$res = Meetings::with(['meeting_comments' => function($query) {
$query->select('comments', 'meeting_id');
}, 'meeting_users' => function($query) {
$query->select('user_id', 'meeting_id');
}])
->get()->toArray();
I'm taking this from memory, so the syntax may be slightly incorrect, but it should work. :)

Laravel 4 Redirect::action() "Route not defined"

I'm currently having troubles with Laravel 4. I would like to use slugs for forum categories and forum topics (slugs are unique). In order to determinate if the user is in a category or in a topic, I have this route:
Route::get('forum/{slug}', function($slug) {
$category = ForumCategory::where('slug', '=', $slug)->first();
if (!is_null($category))
return Redirect::action('ForumCategoryController#findBySlug', array('slug' => $slug));
else {
$topic = ForumTopic::where('slug', '=', $slug)->first();
if (!is_null($topic))
return Redirect::action('ForumTopicController#findBySlug', array('slug' => $slug));
else
return 'fail';
}
});
And I have the following error when I try to reach a category:
Route [ForumCategoryController#findBySlug] not defined.
Here is my ForumCategoryController:
class ForumCategoryController extends BaseController {
public function findBySlug($slug) {
$category = ForumCategory::where('slug', '=', $slug)->first();
return View::make('forum.category', array(
'title' => 'Catégorie',
'category' => $category
));
}
}
Where is the problem ? Is there a way to do it better ? Help please :)
Laravel is telling that you have to define a route to use Route::action(), something like:
Route::get('forum/bySlug/{slug}', 'ForumTopicController#findBySlug');
Because it will actually build an url and consumme it:
http://your-box/forum/bySlug/{slug}
For that it must find a route pointing to your action.

Categories