Laravel eloquent counting a relation - php

I'm new to laravel and eloquent and I'm not sure if this is even possible. but I have 2 tables with a one to many relationship. One is "locations" and one is "users". One location can have many users.
So if I wanted to get all locations with all users I would just do this:
Location::with("users")->get();
But I also want to know how many users each location has, I tried doing this
Location::with("users")->count("users")->get();
But that didn't work.

The n+1 issue that was mentioned doesn't occur if you use eager loading.
$locations = Location::with('users')->get();
$locations->users()->count();
This should result in three queries, no matter how many users or locations you have.
count query against the location model
select * from locations
select * from users where in
The confusion arises from the fact that this also works:
$locations = Location::all();
$locations->users()->count();
But in this case, it does query once for each location.
See the docs for more info:
http://laravel.com/docs/eloquent#eager-loading

You should be using just withCount but I guess it wasn't available back in 2012.
So here's how it should look like:
Location::with("users")->withCount("users")->get();
And you will have an users_count attribute available on you object.
Read the docs for more details: https://laravel.com/docs/5.5/eloquent-relationships#querying-relations (scroll down a bit and you'll see Counting Related Models)

You need to call the count method on each Location record to get users count per location, here is an example:
foreach(Location::get() as $location) // or foreach(Location::with("users")->get() as $location)
{
echo $location->users()->count();
}
This should solve your problem and give you the number of users per each location. You can add a check in the loop to ignore locations with users' count = 0

Related

laravel get eloquent data using its relations Ids

I have three main mySQL tables:
users
tags
news_articles
I also have many to many tables to connect them as needed.
I want to grab all the articles that share the same tags as users so I can personalise their experience.
(example): say a user has tags for "Trump" or "Brexit" I want to grab all the news articles that also have Trump or Brexit.
I could grab the whole data pool and make calculations but that might get unnecessarily server intensive so is there a way of doing it through Eloquent?
Also is Eloquent capable of tallying the user tags to determine which tags the user is most interested in before grabbing the articles or would that need a workaround after?
Thanks in advance.
Christophvh's response did work for me in the end. After some more time with Eloquent I found plenty more ways of doing this.
A way I would go about this now for anyone who happens to be stuck:
By creating a tags method in my User and Article models to return all related tags I can then do the following.
$userTags = $user->tags()->pluck('name')->get()->toArray();
$articles = Article::whereHas('tags', function($tag) use ($userTags) {
$tag->where('name', $userTags)->get();
})->get();
The above code should:
Get only the name field of all tags belonging to a user and store them in an array.
Grab all articles where their related tags name field contain any tag in the $userTags variable
To create a priority tags list I would just need to run array_count_values($userTags) and order it by the returned values, then pass the new ordered $userTags array into the Eloquent query.

Laravel: Order by column value count?

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.

In Laravel Eloquent, select "whereIn" from parent table

In my Laravel project (with MySQL database), I have a few models: Time Entries, Tasks, and Projects.
Time Entries belong to Tasks
Tasks belong to Projects
so each table contains a column for the corresponding ID of its parent.
I have an array of Project IDs, and I am trying to select the time entries which, through their tasks, belong to those projects.
In other words, I'd like to be able to do something like this:
$timeEntries = TimeEntry::whereIn('project_id',$projectIds)->get();
But obviously, I get a column not found error, because all I've got in the time entries table is task_id rather than project_id.
Is there a way to select the desired time entries (based on the project IDs I have) in a single Eloquent query? Help much appreciated.
Add the following method in your Project model
public function timeEntries()
{
return $this->hasManyThrough('App\TimeEntry' , 'App\Task');
}
now you can get all time entries of a project like below
$project = Project::find(id);
$project->timeEntries()->get();
So the type of relation you're explaining is a through relation (http://laravel.com/docs/5.1/eloquent-relationships#has-many-through).
Instead of trying to head up the tree head down the tree from projects->tasks->time_entries.
Projects::whereIn($projectIds)->with('time_entries')->get();
The resulting collection will be projects with (should be at least) a field called time_entries in each project that have all the relevant times.

Sorting collection via nested relationships

Ok so I have an app that searches through course applications which each belong to an applicant. When I search the applications for a course title it returns the required result set but I would like to sort it by the applicant's date of birth.
How can I do this? I've tried sorting the collection doing $applications->sortBy('applicant.dob') but this seems to just order it by each applicant not the overall collection.
Edit
Here's all of my code... http://laravel.io/bin/PD81z
Any one have an idea how I can approach this?
Thanks!
OK
Your table course and table applicant are actually many-to-many relationship
For this to work, table course_applitions serves as a proxy to link the two table.
see the post, especially the part about many-to-many relationships
http://scotch.io/tutorials/php/a-guide-to-using-eloquent-orm-in-laravel
When fetching results from related tables using the above way, you can always add sort options.
Cheers!

Laravel 4 Conditional Relationship

I need to be able to specify conditions on a relationship (one-to-many).
Example being; We have a Post which has many Comments, but I only want the Posts with Comments made by User A and then return the Post object(s).
The only way I can currently think of doing this is with a Fluent query, which wouldn't return the Post object I desire!
EDIT:
The comment has to of been made by User A. The relationship of the User who made the Comment to Post isn't a direct one. It would go through the Comment table.
RE EDIT:
Would it also be possible to say have distinct Posts? Rather than return 3 of the same Post Object?
You can query relationships. You would end up with something like this:
$postsWithComments = $user->posts()->has('comments', '>=', 1)->get();
Here is an extract from documentation: http://laravel.com/docs/eloquent
Querying Relations
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method:
Checking Relations When Selecting
$posts = Post::has('comments')->get();
You may also specify an operator and a count:
$posts = Post::has('comments', '>=', 3)->get();

Categories