Laravel: orderBy a column with collections - php

I need to OrderBy a column with collection.
I need to orderBy(updated_at, 'desc') all posts which owned by current logged user.
Here is my code :
$posts = auth()->user()->posts->sortByDesc('updated_at');
Here is User model :
class User extends Authenticatable
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
It doesn't return any errors also doesn't sort !
Any helps would be great appreciated.
P.S:
I know I can achieve this with :
$posts = Post::where('user_id', auth()->user()->id)->orderBy('updated_at', 'desc')->get();
But I would like to do the same thing with collections.

So this is how you sort with SQL:
$posts = auth()->user()->posts()->orderBy('updated_at', 'DESC');
And with collections:
$posts = auth()->user()->posts->sortByDesc('updated_at');
I've tested the 2nd one and it works as intended for me.
Documentation: https://laravel.com/docs/6.x/collections#method-sortbydesc
*Its available since Laravel 5.1

#devk is right. What I wrote in the first post is correct.
The problem was in DataTables in the the view.
It needed to add this line to the Datatables options:
"order": [[ 5, 'desc' ]], // 5 is the `updated_at` column (the sixth column in my case)
So this is working fine :
$posts = auth()->user()->posts->sortByDesc('updated_at');

Try adding the following to your User model
public function posts_sortedByDesc(){
return $this->hasMany(Post::class)->sortByDesc('updated_at');
}
Then get the posts by calling posts_sortedByDesc instead of posts

Related

Eloquent limit relationship fields

I have the following relationships:
TheEpisodeJob hasOne TheEpisode
TheEpisodeJob hasMany TheJobs
I am successfuly retrieving all TheEpisodesJobs and TheSeriesEpisodes with all the fields in database (including sensitive information) using this command:
$jobs = TheEpisodeJob::with('TheEpisode')->get();
I would like to limit TheEpisode fields shown only for this case (public $hidden will not work)
EDIT
Let's say I need only title and description field from TheEpisode.
How can I achieve that?
As #Buglinjo pointed out you can scope the relationship when eager loading, however, if you're going to be doing this to only select specific columns you must included the related column in the select so that Eloquent knows which Model to attach the related data to.
This should give you what you want:
$jobs = TheEpisodeJob::with(['TheEpisode' => function ($query) {
$query->select('jobID', 'title', 'description');
}])->get();
Furthermore, if you then wanted to to get rid of the jobID as well you could do something like:
$jobs->transform(function ($job) {
$job->TheEpisode->transform(function ($item) {
unset($item->jobID);
return $item;
});
return $job;
});
Hope this helps!
As far as I understood you, you want to limit the results according to some more parameters. If I am right, you should add more queries, like:
->where, ->orwhere, ->select, ->whereNull
Here is the link for more queries. Hope it will help )
I saw an update, so then you need
->pluck('title', 'description');
for more information, go to the link above
You should do like this:
$jobs = TheEpisodeJob::with(['TheEpisode' => function($q){
$q->get(['title', 'description']);
//or
$q->pluck('title', 'description');
}])->get();
Note: pluck is getting as array not as Eloquent Object.

laravel pick last comment posted related to each post

I have got three tables in laravel like so:
Users, posts, and comments
I'm trying to come up with a query that fetches me all the user's posts, plus the date of last comment with each post.
Approach i've taken that's not working perfectly is:
$posts = User::find($userId)->posts()->with('latestComment')->get();
In my Post model I have:
public function latestComment()
{
return $this->hasOne(Comment::class)->latest();
}
In my findings, i haven't been to see a way to get the date from the lastComment load.
Any pointers welcome,
Thanks
Just discovered one needs to add the foreign key to the select method like so:
return $this->hasOne(Comment::class)->latest()->select('field','foreign_key');
You should use eager loading constraint. Code from the other answers will first load all comments, which you don't want.
$posts = Post::where('user_id', $userId)
->with(['comments' => function($q) {
$q->taletst()->take(1);
}])
->get();
You can use the existing relationship and get the latest comment.
public function comments() {
return $this->hasMany(Comment::class);
}
public function latestComment() {
return $this->comments()->last();
}

laravel eloquent sort by relationship

I have 3 models
User
Channel
Reply
model relations
user have belongsToMany('App\Channel');
channel have hasMany('App\Reply', 'channel_id', 'id')->oldest();
let's say i have 2 channels
- channel-1
- channel-2
channel-2 has latest replies than channel-1
now, i want to order the user's channel by its channel's current reply.
just like some chat application.
how can i order the user's channel just like this?
channel-2
channel-1
i already tried some codes. but nothing happen
// User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies'])
->orderBy('replies.created_at'); // error
}
// also
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies' => function($qry) {
$qry->latest();
}]);
}
// but i did not get the expected result
EDIT
also, i tried this. yes i did get the expected result but it would not load all channel if there's no reply.
public function channels()
{
return $this->belongsToMany('App\Channel')
->withPivot('is_approved')
->join('replies', 'replies.channel_id', '=', 'channels.id')
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'ASC');
}
EDIT:
According to my knowledge, eager load with method run 2nd query. That's why you can't achieve what you want with eager loading with method.
I think use join method in combination with relationship method is the solution. The following solution is fully tested and work well.
// In User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved');
}
public function sortedChannels($orderBy)
{
return $this->channels()
->join('replies', 'replies.channel_id', '=', 'channel.id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Then you can call $user->sortedChannels('desc') to get the list of channels order by replies created_at attribute.
For condition like channels (which may or may not have replies), just use leftJoin method.
public function sortedChannels($orderBy)
{
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Edit:
If you want to add groupBy method to the query, you have to pay special attention to your orderBy clause. Because in Sql nature, Group By clause run first before Order By clause. See detail this problem at this stackoverflow question.
So if you add groupBy method, you have to use orderByRaw method and should be implemented like the following.
return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
Inside your channel class you need to create this hasOne relation (you channel hasMany replies, but it hasOne latest reply):
public function latestReply()
{
return $this->hasOne(\App\Reply)->latest();
}
You can now get all channels ordered by latest reply like this:
Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
To get all channels from the user ordered by latest reply you would need that method:
public function getChannelsOrderdByLatestReply()
{
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}
where channels() is given by:
public function channels()
{
return $this->belongsToMany('App\Channel');
}
Firstly, you don't have to specify the name of the pivot table if you follow Laravel's naming convention so your code looks a bit cleaner:
public function channels()
{
return $this->belongsToMany('App\Channel') ...
Secondly, you'd have to call join explicitly to achieve the result in one query:
public function channels()
{
return $this->belongsToMany(Channel::class) // a bit more clean
->withPivot('is_approved')
->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'desc');
}
If you have a hasOne() relationship, you can sort all the records by doing:
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
This sorts all the channels records by the newest replies (assuming you have only one reply per channel.) This is not your case, but someone may be looking for something like this (as I was.)

How do I take related posts (by category) with Eloquent?

I have a question on how to fetch the related posts of a particular post by category using Eloquent. I know how to do it in pure MySQL but am sure Eloquent will have nicer alternative to it.
My tables are: posts categories post_category (pivot)
I have made the neccessary Eloquent connections, so I want to do something like: $post->categories()->posts()->exclude($post)->get().
Of course this won't work. I get an error on posts() because "Builder doesn't have a method posts()", but hopefully you get the idea. How would you do it with Eloquent?
It's hard to say what you want to achieve, bot probably you want to get:
Posts::whereIn('id', $post->categories()->lists('id'))->whereNot('id',$post->id)->get();
One of the confusing parts about Eloquent's relations is that the method on a model that defines the relation will bring back a relation object when called like you're calling it:
$posts->categories();
To return a collection of category models attached to your posts you should use something like this:
Post::find(primary key of post)->categories;
Or get all posts and iterate through the models individually:
$posts = Post::all();
foreach($posts as $post) {
$post->categories;
}
Here's a resource I found very helpful in learning to use Eloquent's relation methods: http://codeplanet.io/laravel-model-relationships-pt-1/
I was trying to get my related posts by category and searched on google and got here.
I made this, and it worked fine.
public function getSingle($slug){
$post = Post::where('slug', '=', $slug)->first();
$tags=Tag::all();
$categories=Category::all();
$related= Post::where('category_id', '=', $post->category->id)
->where('id', '!=', $post->id)
->get();
return view('blog.show')
->withPost($post)
->withTags($tags)
->withCategories($categories)
->withRelated($related);
}
In my view('blog.show')
$post->title
$post->content
//related posts
#foreach($related as $posts)
$posts->title
$posts->category->name
#endforeach
I don't know if this is the right way, but it works for me. I hope this helps someone
Why don’t you define a ‘relatedposts’ relation where search for posts with the same category id?
Then you can simply do $post->relatedposts...
You’re making it overcomplicated imo...
if you have multiple category (Pivot ex:RelPortfolioCategory)
Portfolio Model:
public function getCats(){
return $this->hasMany(RelPortfolioCategory::class,'portfolioID','id');
}
controller:
public function portfolioDetail($slug){
$db = Portfolio::where('slug' , $slug)->with('getCats')->firstOrFail();
$dbRelated = RelPortfolioCategory::whereIn('categoryID' , $db->getCats->pluck('categoryID'))->whereNot('portfolioID' , $db->id)
->with('getPortfolioDetail')->get();
return view('portfolioDetail' , compact('db' , 'dbRelated'));
}

Laravel leftJoin not return correctly

I have a problem with laravel select using leftJoin. I'm trying to select 2 posts and count how many comments there is(first post have 7 comments, second - 0), but I got only first post with 7 comments.
Code is:
$posts = DB::table('posts')
->leftJoin('comments', 'comments.post', '=', 'posts.id')
->select(DB::raw('posts.title, posts.body, posts.created_at, posts.slug, CASE WHEN comments.post IS NULL THEN 0 WHEN comments.post IS NOT NULL THEN count(comments.post) END as count'))
->get();
And when I trying to check what i see in web browser i got error:
Call to a member function count() on a non-object
This error in my view file at line where i using #if($posts->count())
I have debugged that i got only one post from using print_r().
Any suggestions?
I think your best bet here is to use some of the built in functionality of laravel's Eloquent ORM.
set up a relationship in the models:
Post.php:
<?php
class Post extends Eloquent {
protected $table = 'posts';
public function comments()
{
return $this->hasMany('Comment', 'posts');//first param refrences the other model, second is the foreign key
}
Comment.php:
<?php
class Comment extends Eloquent {
protected $table = 'comments';
public function comments()
{
return $this->belongsTo('Post');//first param refrences the other model, second is unnecessary if you are using auto incrementing id
}
now you have a relationship set up and there is no need for the join.
Usage:
there may be a better way to do this, but this should work.
$posts = Post::with('comments')->get();//retrieves all posts with comments
foreach($posts as $post){
$count = count($post['comments']);
$post['comment_count'] = $count;
}
return $posts;
this will return a result that contains all of the posts, with a field called 'comments' that contains an array of all of the comments related. the 'comment_count' field will contain the count.
example:
[
{
"id": 1,
"created_at": "2014-07-02 11:34:00",
"updated_at": "2014-07-02 11:34:00",
"post_title": "hello there",
"comment_count": 1,
"comments": [
{
"id":'blah'
"comment_title":"blah"
}
]
}
you can now pass this to your view and loop through each post and get the $post['comment_count']

Categories