Laravel 4: Count relationship within whereHas - php

I have 2 tables as below:
Subscription table have a entrance_limit column, and it has a hasMany('Attendance') relationship. The entrance_limit column will actually limit the number of rows in Attendance. For example, if the entrance_limit value is 10, then we only can create 10 rows in Attendance.
Attendance table has a belongsTo('Subscription') relationship.
How do I get the list of Subscription that have total number of Attendance less than the entrance_limit value?
Subscription::whereHas('attendances', function($q) {
## something like this
$q->count() < subscription->entrance_limit
})

Model your query has below
Subscription::whereHas('attendances', function($q) {
$q->where('entrance_limit','>',$q->count());
})

Finally able to get it working with the following:
Subscription::whereRaw('
(select count(*) from `attendances` where `attendances`.`subscription_id` = `subscriptions`.`id`) < `subscriptions`.`entrance_limit`
');

Related

Laravel OrderBy by related table column

I have a table (A) that has a One to Many relation with another table (B).
I want to query Table A and eager load Table B with the Table A results - but I also want to sort Table A by a value in Table B.
I have tried using OrderBy in the query and also trying SortBy on the resultant collection but cannot get the Table A data to be sorted by the value found in Table B.
Example of what I have tried:
$query = ModelA::with("ModelB"])->get()->sortByDesc('ModelB.sortValue');
Keep in mind, I am only interested in the LATEST record from Table B. So I need to query Table A and sort by a value in the LATEST records of Table B.
How can I achieve this?
EDIT:
The below (as suggested by #ljubadr) works pretty close, but the issue is that there are many record in Table B which means that it doesn't reliably sort as it doesn't seem to sortby the latest records in Table B. Can I have the join return ONLY the latest record for each ID?
$query = ModelA::select('TableA.*')
->join('TableB', 'TableA.id', '=', 'TableB.col_id')
->groupBy('TableA.id')->orderBy('TableB.sortCol', 'desc')
->with(['x'])
->get();
EDIT 2:
#Neku80 answer has gotten me closest but it seems to not sort the column with the greatest accuracy.. I'm sorting a Decimal column and for the most part it is in order but in some places the items are out of order..
$latestTableB = ModelB::select(['TableA_id', 'sortByColumnName'], DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.sortByColumnName')
->get();
For example, the ordering is like:
0.0437
0.0389
0.0247 <-- -1
0.025 <-- +1
0.0127
When I delete all rows except for the 'latest' rows, then it orders correctly, so it still must be ordering with old data...
I have found a solution:
ModelA::select('TableA.*', 'TableB.sortByCol as sortByCol')
->leftJoin('TableB', function ($query) {
$query->on('TableB.TableA_id', '=', 'TableA.id')
->whereRaw('TableB.id IN (select MAX(a2.id) from TableB as a2 join TableA as u2 on u2.id = a2.TableA_id group by u2.id)');
})
->orderBy('TableB.sortByCol')
->get();
Another alternative to order is like this:
$users = User::orderBy(
Company::select('name')
->whereColumn('companies.user_id', 'users.id'),
'asc'
)->get();
Here we are ordering in asc order by company name field.
In this article it is explained in detail.
You can simply execute a left join query:
ModelA::query()->leftJoin('model_b_table', 'model_a_table.primary_key', '=', 'model_b_table.foreign_key')->orderBy('model_a_table.target_column')->get();
This should work if you only need TableB's ID and created_at columns:
$latestTableB = ModelB::select('TableA_id', DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.created_at')
->get();

Laravel return latest histories from all users

I have login history table where i save each user login/logout, therefore each user can have many rows.
I am trying to make filter to get last row of each user at once (getting latest row of all users) but not sure how to.
Code
controller
$histories = LoginHistory::with(['user','user.roles' => function($q) {
return $q->latest()->first();
}])->get();
this return all rows from all users instead of only latest rows.
screenshot
table
In this case my function should return 3 results as my user with id 1 has 2 rows (i only need to get latest one row 2 ) but yet i get all 4 rows with my function.
Any idea?
The raw MySQL query you might use here would look something like this:
SELECT lh1.*
FROM login_history lh1
INNER JOIN
(
SELECT user_id, MAX(login) AS max_login
FROM login_history
GROUP BY user_id
) lh2
ON lh1.user_id = lh2.user_id AND lh1.login = lh2.max_login
Your updated Laravel/Eloquent code:
$subquery = DB::table('login_history')
->select(DB::raw("user_id, MAX(login) AS max_login"))
->groupBy('user_id');
$rs = LoginHistory::joinSub($subquery, 'lh2', function($join) {
$join->on('login_history.user_id', '=', 'lh2.user_id');
$join->on('login_history.login', '=', 'lh2.max_login');
})
->select(['login_history.*'])
->get();

How to order by column in nested 2 level relationship in Laravel?

I have 3 tables: reports, fields and report_fields which is a pivot between the other 2. What i need to do is order report_field.field by the position column in the field table.
I tried ordering in the relation in the Models or when using with but I may be doing it wrong.
ex:
$query = Report::with([ 'reportFields.field' => function ($q) {
$q->orderBy('position', 'asc');
//$q->orderByRaw("fields.position DESC");
},
Can someone give a basic example of ordering a 2 level nested relationship?
Edit: I do not need to order by any column in the base table but the list of entries in the pivot table by an column in the second table.
Edit2:
To give an example how the output should be ordered:
Report
ReportField
Field.position = 1
ReportField
Field.position = 2
ReportField
Field.position = 3
You can add your needed ordering on the relation of the first table reports:
public function reportFields()
{
return $this->hasMany(ReportFields::class)
->select('report_fields.*')
->join('fields', 'report_fields.field_id', 'fields.id')
->orderBy('fields.position', 'asc');
}

how can I sort my data by the rate of another table

I'm using laravel 5.7 developing a project. I have two tables,one is users table, another is orders table.The User model has a hasMany orders relation(the foreign key in the orders table is user_id).In the orders table,there is a column named "win", which value is 1 or 0.When I select the users , I want to sort the users by their orders win rate(the users total count of win orders(win=1) / the user's total count orders).How can I make this possible.
User::withCount(['orders as WinRate' => function($q){
$q->where('win', 1)->get();
$total = ($q / count($q->whereIn('win', [1,0])->get()));
return $total;
}])
->with('orders')
->orderBy('WinRate', 'ASC')->get();
Can you try this.

Laravel 5 orderBy relationship count and pagination

Im working on a link sharing website and need to order the links by their votes.
I have a 'links' table and a link_votes table.
The link_votes table has a 'link_id' reference field to the links table 'id' field.
I have a hasMany relationship in my Link model:
public function votes()
{
return $this->hasMany('\App\LinkVote');
}
This returns all rows (each row is a single vote) int he link_votes table.
My standard query to get all my Links and order them by date created is:
$links = Link::withCount('votes')->orderBy('votes_count', 'desc')->paginate(10);
What I wish to do now is order my links by the amount of votes they have.
What I have currently is:
$links = Link::orderBy('created_at', 'desc')->with('votes')->paginate(20);
This works, but almost too well. I have 2 types of vote in my link_votes table by having a vote_type field. If the vote is an upvote its a type of 1 but if its a downvote its a type of 2.
The issue is here by ordering by vote count its taking all votes as a count and putting votes with a negative vote above those links that don't have any votes.
I need to find a way of ordering by the sum of the upvotes (vote_type = 1) minus the downvotes (vote_type = 2).
In my view I show the vote count by doing that maths.
A change of logic allowed me to do this easier than first thought.
All I needed to do was count the positive (upvotes) as the count to order by as they are the only ones that matter.
To do this I have changed the withCount to:
$links = Link::withCount([
'votes' => function ($query) {
$query->where('vote_type', 1);
}
])
->orderBy('votes_count', 'desc')
->paginate(10);

Categories