Delete from collection if empty - php

How to delete the item when products[] is empty?
This is how I get it from the database:
$orders = Order::with(['order_details.products' => function($q) use ($place) {
$categories = Categorie::where('place', $place)->get();
$validCategories = [];
foreach ($categories as $categorie){
array_push($validCategories, $categorie->id);
}
$q->where('products.categorie_id', '=', $validCategories);
}])->get();
and this is the result: Result
But I only want the items if products[] is not empty. How can I do that?
for example here: example
Here are the products empty but now I want to remove all this data.

You could attempt to filter the eager load using with with a callback and using a whereHas with the same callback to remove the unneeded order_details.
Try something like this:
$catIds = Categorie::where('place', $place)->pluck('id');
$orders = Order::with(['order_details' => function ($q) use ($catIds) {
$q->whereHas(['products' => $func = function ($q) use ($catIds) {
$q->whereIn('categorie_id', $catIds)
})->with(['products' => $func]);
}])->get();
If not you can iterate through your collection and remove the unneeded records.

Related

send comma separated some array generated in a loop in php and laravel

suppose we have an Object that accept some value as array that is separated by comma:
$keyboard = new Keyboard(
['value1'],
['value2'],
['value3'],
['value4']
);
Now I want to fetch some value from database via a loop and send them finally to that object. but I do not know how can I collect them and send a comma separated list of them.
I write this code But can not get appropriate result from it:
$brands = Option::with('translations')->whereHas('attribute', function ($query) {
$query->where('type', '=', 'brand');
})->get();
$titles = [];
foreach ($brands as $key => $brand) {
$titles [] = array($brand->title_fa);
}
$keyboard = new Keyboard(
$titles
);
You can use ... operator to pass parameters. But check before whether size of an array is proper.
$keyboard = new Keyboard(...$titles);
First I suggest plucking the value that you want and then map them
Look at the Laravel Collection methods to avoid doing unnecessary loops and doing it more fluent
https://laravel.com/docs/7.x/collections
$brands = Option::with('translations')->whereHas('attribute', function ($query) {
$query->where('type', '=', 'brand');
})->get()
->pluck('title_fa')
->map(function($brand){ return [$brand]; })->all()
Now you have a $brands array as required
So you can pass it as an array or as a Varidic params using the Splat operator as suggested in the other answer
$keyboard = new Keyboard($brands);
$keyboard = new Keyboard(...$brands);

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.

Laravel - Carry array through map

Let's say I have a model collection that I'm mapping through like this:
$alreadyImported = [];
$players = Players::whereNotIn('id', $alreadyImported)
->get()
->random(25)
->pluck('id');
$groups = $players->map(function ($item, $key) use ($alreadyImported) {
array_merge($alreadyImported, $item->id);
$group = [
'username' => $item['username'],
];
return $group;
});
// $groups is a pivot table with group and players
Why does my $globalList always start at []? How can I carry the already-merged $globalList to the next map iteration?
The player IDs does not matter. It's for show. I am looking to pass the array through the map iterations.
Just use pluck() to get IDs from the collection:
$ids = $players->pluck('id');
Or, if you just need IDs:
$ids = Players::where('banned', false)->pluck('id');
If you're going to add any other data, you don't need to merge it to some array or a collection because map() will create a new collection.
Finally, you don't need to use collect() because get() will return collection.

Paginating posts according to tags

When a user clicks on a tag say "mobile", I'm trying to get all the posts that are associated with that tag through this method:
public function getRelevantPostsFromTag($tag)
{
$posts = [];
$tag = Tag::where('name', '=', $tag)->first();
foreach ($tag->posts as $post) {
array_push($posts, $post);
}
return collect($posts);
}
I'm then trying to paginate the returned collection by trying to do so:
$posts = $this->postRepository->getRelevantPostsFromTag($tag);
$posts = $posts->paginate(8);
But I'm getting a method paginate does not exist error. Is the application of paginate on a custom collection disallowed?
You could do something like this to get all posts by tag name and paginate.
$posts = Post::whereHas('tags', function ($q) use ($name) {
$q->where('name', $name);
})->paginate(...);
Illuminate\Support\Collection does not have a paginate method.
If you already have a collection you can manually create a paginator and use something like Collection#forPage to help slice it for you to pass into the paginator.

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