I have two items in a table called products with id's 1 and 2. There is a table called invite which has foreign key product_id for products.
In my controller below, i am trying to count the number of product id's that is existing in the invite table.
eg
Product invite
id name id token product_id user_id
1 cal 1 ..d 1 2
2 del 2 dd.. 2 2
3 mac 3 ..gh4 2 2
As above, id's 1 and 2 exist in the invite table. meaning the total count is 2 (although product id 2 appears twice.
When i run my code below, i get a count of 1 instead of 2. What could i be missing out, please?
NB: in this case, i am user just one user
Controller
public function dashboard()
{
$products = Products::orderBy('created_at', 'desc')
->where('user_id', Auth::user()->id)->get();
$get_products_id = [];
foreach($products as $product){
$get_products_id[] = $product->id;
}
$implode_data = implode(',',$get_products_id);
$count_existing_products =
Invite::where('product_id',$implode_data)
->where('user_id', Auth::user()- >id)->get()->count();
return view('dashboard', compact('count_existing_products'));
}
View
<h1>{{count_existing_products}}}</h1>
For WHERE IN clause laravel uses special whereIn() method where you pass array, not string. So, your query becomes:
Invite::whereIn('product_id', $get_products_id)
->where('user_id', Auth::user()->id)
->distinct() // added `distinct` call to get distinct values
->get()
->count();
If distinct does not work, try to groupBy:
Invite::whereIn('product_id', $get_products_id)
->where('user_id', Auth::user()->id)
->groupBy('product_id')
->get()
->count();
There is no need to use implode. you can use whereIn instead of where.
Invite::whereIn('product_id',$get_products_id)
->where('user_id', Auth::user()- >id)->get()->count();
Related
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();
please what is the correct way of getting data from the MN table where one product has assigned multiple combinations of Mark / Model / Year.
E.g. product with ID 1 has following in mmis table:
id | product_id | mark_id | model_id | year_id
178 1 1 1 2
177 1 2 1 3
176 1 3 1 1
other tables are: marks, models, years.
It's completely fine getting all requested data with just:
$items = ProductMmi::where('product_id', $id)
->with(['mark', 'model', 'year'])
->get();
The problem is, that in this way I am not able to sort result based on mark.name ASC, model.name ASC, year.name ASC.
Thank you for any advice.
update:
forgot to add my second try - ordering still in troubles...
$items = ProductMmi::select([
'product_mmis.*',
'marks.*',
'mark_models.*',
'mark_model_years.*'
])
->join('marks', 'product_mmis.mark_id', '=', 'marks.id')
->join('mark_models', 'product_mmis.model_id', '=', 'mark_models.id')
->join('mark_model_years', 'product_mmis.year_id', '=', 'mark_model_years.id')
->where('product_mmis.product_id', $id)
->orderBy('mark', 'asc')
->orderBy('model', 'asc')
->orderBy('year', 'asc')
->get();
you can use this way
$items = ProductMmi::where('product_id', $id)->with(['mark'=>function($query){
$query->orderBy('your_column_name','ASC');
}])->with(['model', 'year'])
->get();
or one thing if you dont want to edit your query then in the model you can do also like this
public function mark()
{
return $this->hasMany(Mark::class)->orderBy('mark','asc');
}
I think the solution was to add orderBy methods between specific joins. E.g. the following case is working completely properly:
$items = ProductMmi::select([
'product_mmis.*',
'marks.*',
'mark_models.*',
'mark_model_years.*'
])
->join('marks', 'product_mmis.mark_id', '=', 'marks.id')
->orderBy('mark', 'asc')
->join('mark_models', 'product_mmis.model_id', '=', 'mark_models.id')
->orderBy('model', 'asc')
->join('mark_model_years', 'product_mmis.year_id', '=', 'mark_model_years.id')
->where('product_mmis.product_id', $id)
->orderBy('year', 'desc')
->get();
Is there a better way to have it done in a more efficient way? Thanks.
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();
This question already has answers here:
Laravel eloquent selecting most recent row from joined table
(3 answers)
Closed 3 years ago.
I have two tables for example users and orders.
I have to join users with orders On users.id = orders.user_id - which is quite fine.
User table has one to many relation in orders table.
I have some conditions also, like orders.pay_type = 'xyz', 'orders.date = yesterday' , 'user.status = active' ..etc.
My problem is that I need to join my user table to the latest row of orders table correspond to that user_id.
Or need to fetch the latest details of that users orders table details along with user table data.
I already tried
->orderBy('orders.date')
->groupBy('orders.user_id')
->get();
but it has no result in o/p.
$data= users::leftJoin('orders', function($join) {
$join->on('orders.user_id', '=', 'users.id')
->on('orders.id', '=', DB::raw("(SELECT max(id) from orders WHERE orders.user_id = users.id)"));
})
->select(*)
This resolve my issue.
If you don't want to change anything in model you can go with this method as well, which is simple query builder method.
DB::table('orders')->leftJoin('users', 'orders.user_id', 'users.id')
->groupBy('orders.user_id')
->orderBy('orders.date', 'DESC')
->get();
Assuming, You might have "orders" function in User.php model
You can also go for below method. (Similar to the answer already given but for array of users instead of one user.)
$users = User::all();
foreach ($users as $user) {
$latest_order = $user->orders()->where('pay_type', 'xyz')->orderBy('date', 'DESC')->first(); // Option 1
$latest_order = $user->orders()->where('pay_type', 'xyz')->latest('date')->first(); // Option 2
}
You can use the following Relation in the User Model
public function latest_order(){
return $this->hasOne('Order')->orderBy('orders.date', 'desc')->limit(1);
}
Update
Since you can not change the Model, you can use the following code to fetch the latest Order for each User.
$user = User::first(); //get first User
$user->orders()->latest('date')->first();
I'm trying to get all the data from the parent that only has a child. Please see my code below.
$customers = Customer::with(['items' => function($query){
return $query->where('status', 2);
}])->get();
dd($customers);
But the code above returns all the customer. By the way, I'm using laravel 4.2.
Items Table:
Customer Table:
with() is for eager loading. That basically means, along the main model, Laravel will preload the relationship(s) you specify. This is especially helpful if you have a collection of models and you want to load a relation for all of them. Because with eager loading you run only one additional DB query instead of one for every model in the collection.
has() is to filter the selecting model based on a relationship. So it acts very similarly to a normal WHERE condition. If you just use has('relation') that means you only want to get the models that have at least one related model in this relation.
e.g :
$users = Customer::has('items')->get();
// only Customer that have at least one item are contained in the collection
whereHas() works basically the same as has() but allows you to specify additional filters for the related model to check.
e.g
$users = Customer::whereHas('items', function($q){
$q->where('status', 2);
})->get();
// only customer that have item status 2
Adding group by to calculating sum
this is another example from my code :
Customer::select(['customer.name', DB::raw('sum(sale.amount_to_pay) AS total_amount'), 'customer.id'])
->where('customer.store_id', User::storeId())
->join('sale', 'sale.customer_id', '=', 'customer.id')
->groupBy('customer.id', 'customer.name')
->orderBy('total_amount', 'desc')
->take($i)
->get()
in your case :
Customer::select(['customer_id', DB::raw('sum(quantity) AS total')])
->whereHas('items', function ($q) {
$q->where('status', 2);
})
->groupBy('customer_id')
->get();
whereHas() allow you to filter data or query for the related model in your case
those customer that have items and it status is 2
afetr getting data we are perform ->groupBy('customer_id')
The GROUP BY statement is often used with aggregate functions (COUNT, MAX, MIN, SUM, AVG) to group the result-set by one or more columns.
select(['customer_id', DB::raw('sum(quantity) AS total')]) this will select customer id and calculate the sum of quantity column
You should use whereHas not with to check child existence.
$customers = Customer::whereHas('items', function($query){
return $query->where('status', 2);
})->get();
dd($customers);
I assume you already defined proper relationship between Customer and Item.
You should try this:
$customers = Customer::whereHas('items', function($query){
$query->where('status', 2);
})->get();
dd($customers);
Customer::select(['items.customer_id',DB::raw('count(items.id) AS total_qty')])
->join('items', 'items.user_id', '=', 'customer.customer_id')
->groupBy('items.customer_id')
->havingRaw('total_qty > 2')
->get();
OR
$data=DB::select("select `items`.`customer_id`, count(items.id) AS total_qty
from `customers`
inner join `items`
on `items`.`customer_id` = `customers`.`customer_id`
group by `items`.`customer_id` having total_qty >= 2");
correct table name and column name.