Count from other table - php

I am having problem to count number of devices where guid is not null.
It need to get all the shops by user user_id and then count all the devices where guid is not null.
$shops = Shop::with('devices')->where('user_id', $userId)->get();
$deviceActive = $shops->reduce(function ($carry, $item) {
return $carry + $item->devices->whereNotNull('guid')->count();
});
dd($deviceActive );
It work when I do:
return $carry + $item->devices->count();
but it need to count where guid is not null.
I would also be interested to hear if there is alternative reduce approach.

Since $item->devices is a collection there is no whereNotNull() for collections. So try to use where():
$item->devices->where('guid', '<>', null)->count();

Try:
$shops = Shop::with('devices')
->where('user_id', $userId)
->where('guid', '!=', null)->get();
$get_count = count($shops); // it return how many values have $shops
OR
$shops= DB::table('devices')->where('user_id', $userId)
->where('guid', '!=', null)->get();
$get_count = count($shops);
if you did not have the class DB add at your controller:
use DB;

Related

Order and Sub Order collection - laravel 7 (Simple)

lets say I have a collection of users Users::all()
I would like to take sort/order it like such Users::all()->sort('created_at', 'DESC')
then I would like to sub order it by an array like [1,5,3,9,4,8] so perhpas a call like this Users::all()->sort('created_at', 'DESC')->sortBy("id", [1,5,3,9,4,8])
Any Advice?
Edit 1
I have found this, is this correct to use?
$ids = collect([1,5,3,9,4,8]);
$users = Users::all()->sort('created_at', 'DESC');
$users = $ids->map(function($id) use($users) {
return $users->where('cat_id', $id)->first();
});
I think you could just invoke orderBy() twice.
$ids = [1,5,3,9,4,8];
$users = Users::all()
->orderBy('created_at', 'desc')
->orderBy($ids)
->get();
Does this answer your question?
You can use whereIn like this probably:
$ids = [1,5,3,9,4,8];
$users = Users::all()
->orderBy('created_at', 'desc')
->whereIn('cat_id', $ids)
->get();
https://laravel.com/docs/9.x/queries#additional-where-clauses
The whereIn method verifies that a given column's value is contained within the given array
So I found a solution.
$ids = json_decode($interview->question_ids ?? '[]');
if(count($ids) == 0){ // if empty create new id array and save to DB
$ids = collect(questions::all()->where('interview_id', $interview->id)->pluck('id')->toArray());
$interview->question_ids = json_encode($ids);
$interview->save();
}
$questions = questions::all()->where('interview_id', $interview->id)->sortBy([
['order_timestamp', 'asc'],
['created_at', 'asc'],
]);
$questions = $ids->map(function($id) use($questions) {
return $questions->where('id', $id)->first();
});
$questions = $questions->flatten();

Query array using whereHas

I want to filter products by category (manyToMany relationship). Basically the user select which categories to show. The categories I want to filter are in $request->keywords. This is what I tried:
$products = Product::all();
foreach($request->keywords as $keyword) {
$products = Product::whereHas('categories', function($q) use ($keyword){
$q->where('title', '=', $keyword);
})->get();
}
return response()->json($products);
The problem is that this does not get all the categories, only the products with the last category in the array. I guess at this point: $q->where('title', '=', $keyword);, $q does not keep the results of the last loop iteration but always deletes the last loop results.
I also tried the same thing with $q->orWhere('title', '=', $keyword);, but that actually doesn't give me any results.
Thanks for your help!
Changed your approach to fetch the data,
$products = Product::all();
if (!empty($request->keywords)) { // if keywords not empty
$keyword = $request->keywords;
$products = Product::whereHas('categories', function ($q) use ($keyword) {
$q->whereIn('title', $keyword); // where in for array
})->get(); // it won't override old data as there is no loop
}
return response()->json($products);
You can simply use whereIn on the title field by passing in your keywords.
$products = Product::all();
$titles = [];
foreach($request->keywords as $keyword) {
$titles[] = $keyword;
}
$products = Product::whereHas('categories', function($q) use ($keyword){
$q->whereIn('title', $titles);
})->get();
I'm improving other persons answer. Below is the filtered code here
$builder = new Product;
if($request->filled('keywords')) { // if keywords key has any value then query will execute.
$builder = $builder->whereHas('categories', function($q) use ($request){
$q->whereIn('title', array_values($request->keywords));
});
}
$items = $builder->get();
dd($items);
You can try this.

How to fetch users not assigned to a particular role in Laravel [duplicate]

In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();

Get relation for multiple objects with laravel

A User has many Phones. I want to get all the phones from active users. I can do:
$phones = [];
$users = User::with('phones')->where('active', 1)->get();
foreach($users as $user) {
$phones = array_merge($phones, $user->phones->toArray());
}
dd($phones); // <- Here are all the phones.
But I'm not sure it's the more elegant or laravel-ish way. Is there a built in magic function for such a case? Can I get all the phones from active users without writing a loop and insert in an Array?
You should use whereHas() method:
$phones = Phone::whereHas('user', function($q) {
$q->where('active', 1);
})->get();
You can also use whereExists, which can be more efficient if you're not using data from the users table:
Phone::whereExists(function($query) {
$query->select(\DB::raw('NULL'))
->from('users')
->whereRaw('users.id = phones.user_id AND users.active = 1')
})->get();

Laravel whereIn with a where clause on each array item

Say I have a user object (which belongsToMany groups) and I'm doing a whereIn with an array of their respected ids like so:
whereIn('user_id', $group->users->modelKeys())
I need to, however, set a condition where I only pull data from each array item based on a condition of the group_user pivot table, "created_at" (which is basically a timestamp of when that user was added to the group).
So I need something like this:
whereIn('user_id', $group->users->modelKeys())->whereRaw('visits.created_at > group_user.created_at')
That doesn't work though because it's not doing the whereRaw for each array item but it's doing it once for the query as a whole. I might need to do something like a nested whereIn but not sure if that'll solve it either. Thoughts?
My full query as it is now:
$ids = $group->users->modelKeys();
return DB::table('visits')->whereIn('user_id', function($query) use ($ids) {
$query->select('user_id')->from('group_user')->whereIn('group_user.user_id', $ids)->whereRaw('visits.created_at > group_user.created_at');
})->sum("views");
Ok got it to work using nested loops instead:
$visits = DB::table('visits')->whereIn('user_id', $group->users->modelKeys())->get();
$sum = 0;
foreach($group->users as $user) {
foreach($visits as $visit) {
if($visit->user_id == $user->id) {
if($visit->created_at >= $user->pivot->created_at) {
$sum += $visit->views;
}
}
}
}
return $sum;
Would still like to see if it's possible to do it in a single query, no array looping.
Solved it! The foreach loop approach was making calls take waaaay too long. Some queries had over 100k records returning (that's a lot to loop through) causing the server to hang up. The answer is in part a big help from Dharmesh Patel with his 3rd edit approach. The only thing I had to do differently was add a where clause for the group_id.
Here's the final query (returns that 100k results query in milliseconds)
//Eager loading. Has overhead for large queries
//$ids = $group->users->modelKeys();
//No eager loading. More efficient
$ids = DB::table('group_user')->where('group_id', $group->id)->lists('user_id');
return DB::table('visits')->join('group_user', function ($query) use ($ids) {
$query->on('visits.user_id', '=', 'group_user.user_id')->on('visits.created_at', '>=', 'group_user.created_at');
})->whereIn('group_user.user_id', $ids)->where('group_id', $group->id)->sum('views');
Have you considered using a foreach?
$users = whereIn('user_id', $group->users->modelKeys());
foreach ($users as $user) {
// do your comparison here
}
I guess you need to use JOINS for this query, following code may take you in right direction:
$ids = $group->users->modelKeys();
return DB::table('visits')->join('group_user', function ($query) use ($ids) {
$query->on('visits.user_id', '=', 'group_user.user_id')
->whereIn('group_user.user_id', $ids)
->whereRaw('visits.created_at > group_user.created_at');
})->sum("views");
EDIT
$ids = $group->users->modelKeys();
return DB::table('visits')->join('group_user', function ($query) use ($ids) {
$query->on('visits.user_id', '=', 'group_user.user_id');
})->whereIn('group_user.user_id', $ids)
->whereRaw('visits.created_at > group_user.created_at')->sum("views");
EDIT
$ids = $group->users->modelKeys();
return DB::table('visits')->join('group_user', function ($query) use ($ids) {
$query->on('visits.user_id', '=', 'group_user.id') // group_user.id from group_user.user_id as per the loop
->on('visits.created_at', '>=', 'group_user.created_at');
})->whereIn('group_user.user_id', $ids)
->sum("views");

Categories