Laravel: Order by column value count? - php

I used to have a query like this
$topReferrals = User::orderBy('user_referrals', 'DESC')->get();
Recently I changed my database structure to not count user_referrals for each user in an int datatype, but to have a column for each user called referred_by and have its value who they have been referred by, I need to adapt my query to work with the new system.
I'm not quite sure how I would go about this, I was hoping someone could help?

Eloquent offers several means of counting relations. One approach would be to set the $withCount property on the User model:
// assuming your model has a referrals method.
protected $withCount = ['referrals'];
This will append an attribute on each queried model that can be used in subsequent queries or collection modifications.
User::all()->sortByDesc('referrals');

Just add referred_by column in User table as it denotes foreign key linked to the user.
Use query below to get count of users that have been referred for each referrer:
User::where('referred_by', $referred_user_id)->count();
OR
User::where('referred_by', $referred_user_id)->orderByDesc('referred_by')->get();
Note: Just make sure you have created a table for referrers to store all the details for referrers in case you want to grabs other data regarding them. You just need to add relations to the User model to directly access to the database.
public function hasReferrers () {
$this->hasOne('App\Referrer');
}
Where App\Referrer is model for your referrers table.

As I’ve understood the question, I think this is how I’d approach getting the number of referrals per user..
$all = User::all();
foreach ($all as $current) {
$current->referrals = User::where('referred_by', $current)->count();
}
$all->sortBy('referrals');
$all should now be a list of users sorted by the number of referrals.

Related

Append data from another model in a model (laravel 5.4)

I want to append the count of data from a table in my database but I am having a problem with the relationship.
I have 3 Models
Voucher model:
vouchers table
VoucherSerial Model:
voucher_serials table
Each voucher will have many serials
UserVoucher Model:
user_vouchers table
When the user redeemed the voucher it will be stored in user_vouchers table. I also had defined the relationship for all the Models
In Voucher.php I want to append the count of the voucher redeemed by the user.
public function getVoucherRedeemedAttribute()
{
//query to append the voucher redeemed count for each voucher
}
I've tried so many solution but mostly I got errors. My biggest problem is because to count the voucher redeemed for each voucher based on user_vouches table but each voucher has many serial_id which i want to count as the same voucher
I know my explanation regarding the question is bad but I need some help regarding this. Hope someone can help me.
Thank you so much in advance
You can add the number of related objects to the result with the withCount method:
If you want to count the number of results from a relationship without
actually loading them you may use the withCount method, which will
place a {relation}_count column on your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
Source: https://laravel.com/docs/5.8/eloquent-relationships#counting-related-models
If I understand your problem correctly, you want to get the count one level deeper (number of vouchers instead of number of voucher serials). You might be able to use a hasManyThrough relationship:
The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. For example, a Country model might have many Post models through an intermediate User model. In this example, you could easily gather all blog posts for a given country.
Source: https://laravel.com/docs/5.8/eloquent-relationships#has-many-through
Combined it will look something like this:
class User {
//...
public function vouchers()
{
return $this->hasManyThrough(App\Voucher::class, App\VoucherSerial::class);
}
}
// Controller
$user->withCount('vouchers');
I've never actually used withCount and hasManyThrough together but it should work ;)

Laravel paginate not working with select certain columns on querybuilder

I have user role relation (many to many) defined as follows in Role model:
public function users()
{
return $this->belongsToMany('App\User');
}
Now I want to show all users in certain role. I execute the following:
$list = Role::find(1)->users()->select('name')->orderBy('name')->paginate(10);
Here $list contain all users that belong to role with id 1 and that is correct. But the problem is: when adding breakpoint on $list it shows that: all user model's attributes are retrieved just like select('name') has no effect.
This is a big problem especially when i want to return the $list as json. User password is retrieved even though it's in the $hidden array of User Model.
My question is:
why paginate() on querybuilder does not respect select() nor attributes in model's $hidden array?
Note: I know that there is other solutions to achieve what I want. I just want an answer to my question to figure out what is going on?
select('name') has no effect because paginate() method takes columns as second parameter
$list = Role::find(1)->users()->orderBy('name')->paginate(10, ['name']);
You can read more about parameters and methods here
https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Builder.html#method_paginate
Since users have the role_id then why don't you fetch the users as:
User::where('role_id', 1)->orderBy('name')->paginate(10);
You are basically getting a role and then fetching a list against that role.

How to retrieve data from additional columns in a pivot table and also be able to update it in laravel 5.2

I was working on making a group functionality for my website which uses a many to many relationship between groups and users.
My User model looks like this:
public function groups(){
return $this->belongsToMany('App\Group')->withPivot('role')->withTimestamps();
}
My Groups model looks like this:
public function users(){
return $this->belongsToMany('App\User')->withPivot('role')->withTimestamps();
}
So my third column has the name of role which is a string variable and is set to a default of "member" for members of my group and I set it to "admin" for the actual user who creates a new group. But I want the admin to have the option of making multiple members admins as well which would require me to check weather the current current user who sent the request is an admin or not. If he is, then I wanna be able to take his request of making a member an admin which would require me to update the role for that particular "member" to an "admin".
In the laravel documentation it only shows you how to attach and detach data in a pivot table and else where I have only seen methods of retrieving data from the first two columns but how can I do the same for additional columns and also be able to update it using the updateExistingPivot method?
You could access the column simply using pivot e.g :
$user->pivot->role
Take a look at Retrieving Intermediate Table Columns in documentation Eloquent Relationships.
Hope this helps.

Laravel 5.1 / Eloquent: How do I use a 3rd table to select from the second table

I have three tables: users, purchase_orders and approvals.
One purchase_order has to be approved by multiple users.
When a new purchase_order gets created, I also create 3 pending approvals belonging to that PO.
The approvals table has a field allowed_user_type that determines who can approve it.
I can't figure out, what is the Eloquent way of selecting the pending purchase orders that can be approved by a specific user, as these are determined from the approvals table.
So far I can pull the pending approvals from the approvals table for a user with the following in the User model.
public function approvals_pending()
{
return $this->hasMany('App\Approval', 'allowed_user_type', 'user_type')
->where('approved', '=', 0);
}
The question is, how do I combine this with a theoretical filter?
I mean ideally, I would love to write:
return $this->hasMany('App\PO')->whereIn('id', '=', $this->approvals_pending()->get()->po_id);
Or something like that...
Any ideas would be greatly appreciated.
OK, for anyone interested I found a solution:
It's very close to what I thought I would have to write.
The lists method basically creates a single array out of the selected field, so it can be plugged-in directly to a whereIn method like so:
return \App\PO::whereIn('id', $this->approvals_pending()->lists('po_id'));
I don't know if this is the most Eloquent way of doing this but it does work.

Laravel, do I need multiple database tables for votes on different models

I have a Question model which has a one to many relationship with an Answer model.
Now I want to add upvote/downvote funcionality to both of these models, do I need to create two tables like VotesQuestions and VotesAnswers or can I somehow manage with one? If so, how?
You can use a polymorphic relationship. This is built into Laravel. Documentation is here. The code shown here is for Laravel 4, but the functionality is the same for Laravel 5.
Create a votes table, and make sure it has at least two specific fields: votable_id and votable_type. In a database migration, you would use the statement $table->morphs('votable');, and it will create the two fields. You can have as many other fields as you like, but to make sure the relationship works, those two fields are required.
Next, setup the Vote model with the votable relationship. The name of this relationship should match the base name of the fields you created:
class Vote extends Eloquent {
public function votable() {
return $this->morphTo();
}
}
With this setup, you can now associate votes to any model you want. Go ahead and add the votes relationship to the Question and Answer models:
class Question extends Eloquent {
public function votes() {
return $this->morphMany('Vote', 'votable');
}
}
class Answer extends Eloquent {
public function votes() {
return $this->morphMany('Vote', 'votable');
}
}
You can now access the votes for any question/answer through the relationship:
$q = Question::first();
$qVotes = $q->votes; // Collection of votes for the question.
$a = Answer::first();
$aVotes = $a->votes; // Collection of votes for the answer.
You can also get the related question/answer model through the vote, if you ever need to:
$v = Vote::first();
$vRelated = $v->votable; // Will automatically be a Question or Answer object, depending on what the vote was for.
I would do an table for the question and when you want to up/downvote the question there should be a count column for both, otherwise you want to log it that an user can only vote for it once, so you need another table for user_id, question_id and type (up/down).
ofc you can handle it with one table, but that is really worth because you save many things that are not necessary.
you can create a table with an internal id, 1,2,3,4 and 1 is always the question or 0 and 2-xx (1-xxx) are always the answers. so you can handle it with one table
You could create a generic Votes model/table which has a field called "model" and "model_id" and then use reflection to get the correct object.

Categories