Get reply which having most vote in laravel - php

I have the reply relationship with like & unlike model. User can like for their best reply and unlike for bad reply.
The like & unlike relationship is almost same,just store in different table.
Now i want to get the replies that having most like & reply that having most unlike, compare them and show only the replies which have most number of vote. How can i achieve it?
In my Discussion Model
public function replies(){
return $this->hasMany('App\Forum\Reply');
}
In Reply Model
public function discussion(){
return $this->belongsTo('App\Forum\Discussion');
}
public function user(){
return $this->belongsTo('App\User');
}
public function likes(){
return $this->hasMany('App\Forum\Like');
}
In the like model
public function user(){
return $this->belongsTo('App\User');
}
public function reply(){
return $this->belongsTo('App\Forum\Reply');
}

I think you can use Eloquents withCount here.
So you'd have something like:
$mostLikedReply = Reply::withCount('likes')->orderBy('likes_count', 'desc')->first();
$mostUnlikedReply = Reply::withCount('unlikes')->orderBy('unlikes_count', 'desc')->first();
Note that withCount will place a {relation}_count column on your resulting models. That's why the orderBy is ordering on what the withCount applies, then by grabbing the first result, it should be the highest liked/unliked reply, from here you can compare what you need.
Read more about counting relations here

How about
$mostLikedReply = $discussion->replies->sortByDesc(function($reply) {
return $reply->likes->count();
}->first();
This will give you the model for the most liked reply.
And the same for most unliked reply and then compare the two? I guess at that point you can maybe merge the whole stuff if you need to.

Related

Laravel count-function in Model, then sortBy count()

I am trying to Order the Posts table by the number of votes a Post has got.
The votes are stored in an other table
(Votes: post_id, user_id, vote_type)
Post-Model:
class Post extends Model
{
public function user()
{
return $this->hasOne(User::class);
}
public function votes()
{
return DB::table('votes')->where('post_id','=',$this->id)->sum('vote_type');
}
}
The votes functions returns the number of votes a post has recieved(The Votes a stored in a seperate table)
Now I am trying to order all the Posts by the number of votes they have got.
Post::get()->sortBy('votes');
This returns follwing Error:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
I would be thankful about any help to fix this!
give it a try
Post::get()->sortBy(function($query){
return $query->votes();
});
Alternative
You can use withCount() as it will place a {relation}_count column on your resulting models.
Post::withCount(['votes'])
->orderBy('votes_count')
->get()
For Pagination
Refer docs for more details on paginations
Post::withCount(['votes'])
->orderBy('votes_count')
->paginate();
Add the following function in your Post Model
protected static function boot()
{
parent::boot();
static::addGlobalScope('voteCount', function ($builder) {
$builder->withCount('votes');
});
}
Now, each of your post model will always have a voteCount value and You can sort on that.
In your controller, use:
Post::get()->sortBy('voteCount');
Keep in mind that this will always return a votesCount with the PostModel, but I'm assuming it will be required as it usually does in this kind of applications.
If you want to sort votes as a attribute you have to to make it to be attribute by adding getVotesAttribute() method in your Post Model.
class Post extends Model
{
public function user()
{
return $this->hasOne(User::class);
}
public function getVotesAttribute()
{
return DB::table('votes')->where('post_id','=',$this->id)->sum('vote_type');
}
}

Laravel -- Flatten data appended via `with`

I've got two models, User and Seminar. In English, the basic idea is that a bunch of users attend any number of seminars. Additionally, exactly one user may volunteer to speak at each of the seminars.
My implementation consists of a users table, a seminars table, and a seminar_user pivot table.
The seminar_user table has a structure like this:
seminar_id | user_id | speaking
-------------|-----------|---------
int | int | bool
The relationships are defined as follows:
/** On the Seminar model */
public function members()
{
return $this->belongsToMany(User::class);
}
/** On the User model */
public function seminars()
{
return $this->belongsToMany(Seminar::class);
}
I am struggling to figure out how to set up a "relationship" which will help me get a Seminar's speaker. I have currently defined a method like this:
public function speaker()
{
return $this->members()->where('speaking', true);
}
The reason I'd like this is because ultimately, I'd like my API call to look something like this:
public function index()
{
return Seminar::active()
->with(['speaker' => function ($query) {
$query->select('name');
}])
->get()
->toJson();
}
The problem is that since the members relationship is actually a belongsToMany, even though I know there is only to ever be a single User where speaking is true, an array of User's will always be returned.
One workaround would be to post-format the response before sending it off, by first setting a temp $seminars variable, then going through a foreach and setting each $seminar['speaker'] = $seminar['speaker'][0] but that really stinks and I feel like there should be a way to achieve this through Eloquent itself.
How can I flatten the data that is added via the with call? (Or rewrite my relationship methods)
Try changing your speaker function to this
public function speaker()
{
return $this->members()->where('speaking', true)->first();
}
This will always give you an Item as opposed to a Collection that you currently receive.
You can define a new relation on Seminar model as:
public function speaker()
{
return $this->belongsToMany(User::class)->wherePivot('speaking', true);
}
And your query will be as:
Seminar::active()
->with(['speaker' => function ($query) {
$query->select('name');
}])
->get()
->toJson();
Docs scroll down to Filtering Relationships Via Intermediate Table Columns

Laravel simplify nested relation output

I get all items owned by authenticated user.
$items=Auth::user()->with('items')->get();
In my view i can access items collection, but instead of title_id want to retrieve item_title value.
Currently i'm able to get item_title value using code below:
$item->title->title
Is it possible to simplify it to retrieve title like this:
$item->title
?
Here is my models and relations:
Users
id
username
Item
id
user_id
title_id
Item_Titles
id
title
User model:
public function items()
{
return $this->hasMany('Item', 'user_id', 'id');
}
Item model:
public function user(){
return $this->belongsTo('User', 'user_id', 'id');
}
public function title()
{
return $this->belongsTo('ItemTitle','title_id','id');
}
ItemTitle model:
public function item()
{
return $this->hasMany('Item', 'title_id', 'id');
}
UPDATE
Excuse me probably I wasn't clear. To be precise - I just want to find Eloquent alternative to:
$items=Item::where('user_id','=',Auth::id())->leftJoin('item_titles', 'item.title_id', '=', 'item_titles.id')->get();
#foreach ($items as $item)
{{ $item->title }}
#endforeach
Just change your relationship function to
Item model:
public function title()
{
return $this->belongsTo('ItemTitle','title_id','id')->first()->title;
}
You will need to call it as $item->title() unless you also do
public function getTitleAttribute(){
return $this->title();
}
You might get some funny stuff with everything being called 'title' but with this $item->title should also work I think
Yes it is. It looks like you setup a many-to-many relationship with the Item model being the pivot table.
User Model
public function titles()
{
return $this->belongsToMany('ItemTitle', 'items');
}
Note: Change ItemTitle to the correct namespace. Also, change items to the Item model's table name.
You can also define the inverse relationship like this:
ItemTitle Model
public function users()
{
return $this->belongsToMany('User', 'items');
}
From there, you can get all the authenticated user's ItemTitles like this:
$titles = Auth::user()->titles;
Link to the documentation: https://laravel.com/docs/5.1/eloquent-relationships#many-to-many
Editing based on the comments below (thanks to #ash for helping clarify and for his suggestion):
The other answer is more along the lines of what you are trying to achieve so I would recommend taking a look at that. However, there is an error in your question. This does not return items:
$items=Auth::user()->with('items')->get();
That returns all users with their items eager loaded. To see proof of this, if you dd($items), you will most likely see every single user in the database.
That is most likely not what you want to do. To get all items for the authenticated users, you should do this:
$items = Auth::user()->items;
It's that simple to get a collection of items. This will run 2 queries - 1 to get the user and another to get all of his items.

Laravel Eloquent - equivalent to first() for last?

I have a few models such as User, Post, Comment, etc.
In the User, I have:
public function posts()
{
return $this->hasMany('Post');
}
I can get the first post via $customer->posts()->first(), but what if I want to get the latest Post? There is no last() as I can see.
This is further compounded by a hasManyThrough relationship (unfortunately we inherited a wacky schema):
public function comments()
{
return $this->hasManyThrough('Comment', 'Post');
}
If I try to do an $this->comments()->orderBy('comments.id', 'desc')->first(); it returns a User object??
No, this
// User model
$this->comments()->orderBy('comments.id', 'desc')->first();
won't return User model.
And to get what you asked for, simply do this:
$customer->posts()->latest()->first();

Laravel OrderBy relationship count

I'm trying to get the most popular hackathons which requires ordering by the respective hackathon's partipants->count(). Sorry if that's a little difficult to understand.
I have a database with the following format:
hackathons
id
name
...
hackathon_user
hackathon_id
user_id
users
id
name
The Hackathon model is:
class Hackathon extends \Eloquent {
protected $fillable = ['name', 'begins', 'ends', 'description'];
protected $table = 'hackathons';
public function owner()
{
return $this->belongsToMany('User', 'hackathon_owner');
}
public function participants()
{
return $this->belongsToMany('User');
}
public function type()
{
return $this->belongsToMany('Type');
}
}
And HackathonParticipant is defined as:
class HackathonParticipant extends \Eloquent {
protected $fillable = ['hackathon_id', 'user_id'];
protected $table = 'hackathon_user';
public function user()
{
return $this->belongsTo('User', 'user_id');
}
public function hackathon()
{
return $this->belongsTo('Hackathon', 'hackathon_id');
}
}
I've tried Hackathon::orderBy(HackathonParticipant::find($this->id)->count(), 'DESC')->take(5)->get()); but I feel like I made a big mistake (possibly the $this->id), because it doesn't work at all.
How would I go about trying to get the most popular hackathons which is based on the highest number of related hackathonParticipants?
This works for me in Laravel 5.3, using your example:
Hackathon::withCount('participants')->orderBy('participants_count', 'desc')->paginate(10);
This way it is ordered on the query and the pagination works nicely.
Edit: If using Laravel 5.2 or greater, use kJamesy's answer. It will likely perform a bit better because it's not going to need to load up all the participants and hackathons into memory, just the paginated hackathons and the count of participants for those hackathons.
You should be able to use the Collection's sortBy() and count() methods to do this fairly easily.
$hackathons = Hackathon::with('participants')->get()->sortBy(function($hackathon)
{
return $hackathon->participants->count();
});
Another approach can be by using withCount() method.
Hackathon::withCount('participants')
->orderBy('participants_count', 'desc')
->paginate(50);
Ref: https://laravel.com/docs/5.5/eloquent-relationships#querying-relations
I had similar issue and using sortBy() is not suitable because of pagination, exactly as Sabrina Gelbart commented in previous solution.
So I used db raw, here's simplified query:
Tag::select(
array(
'*',
DB::raw('(SELECT count(*) FROM link_tag WHERE tag_id = id) as count_links'))
)->with('links')->orderBy('count_links','desc')->paginate(5);
You can also use join operator. As Sabrina said, you can not use sortby at the db level.
$hackathons = Hackathon::leftJoin('hackathon_user','hackathon.id','=','hackathon_user.hackathon_id')
->selectRaw('hackathon.*, count(hackathon_user.hackathon_id) AS `count`')
->groupBy('hackathon.id')
->orderBy('count','DESC')
->paginate(5);
But this code takes all records from database. So you should paginate manually.
$hackathons = Hackathon::leftJoin('hackathon_user','hackathon.id','=','hackathon_user.hackathon_id')
->selectRaw('hackathon.*, count(hackathon_user.hackathon_id) AS `count`')
->groupBy('hackathon.id')
->orderBy('count','DESC')
->skip(0)->take(5)->get();
Referred from : https://stackoverflow.com/a/26384024/2186887
I needed to sum multiple counts and then use it to set order. Following query worked for me in Laravel 8.
$posts = Post::withCount('comments','likes')->orderBy(\DB::raw('comments_count + likes_count'),'DESC')->get();
You can use below code
Hackathon::withCount('participants')->orderByDesc("participants_count")->paginate(15)
Or if you even want ASC/DESC with single method
Hackathon::withCount('participants')->orderBy("participants_count", 'asc')->paginate(15)

Categories