Get multiple average values in raw expression - Laravel MongoDB jenssegers - laravel

I am using Laravel with MongoDB and jenssegers package. I have a collection called ProductDetails in which I have three fields for which I need to retrieve the average value: price, margin and weight. I can make three different queries but I would like to get that in one query. I think it would be cleaner.
I tried using raw expressions but I cannot make it work. I can't even retrieve the average of a single value. I have seen many people using mongoDB aggregation but I don't see why and how it would be useful here.
Here's what I'd love to achieve:
$productDetails = ProductDetails::select('AVG(price) as avg_price','AVG(margin) as avg_margin','AVG(weight) as avg_weight')
->where('id', '=', $id)
->where('active', '=', true)
->get();
It works when I try to retrieve the values (not the average) but the select does not work with AVG() which is why I wanted to use raw expressions
This is an unfruitful attempt:
$productDetails = ProductDetails::raw(function($collection)
{
return $collection ->find([
'name' => 'First product',
'avg_price' => ['$avg' => "price"]
]);
})

you just have to use selectRaw :
$productDetails = ProductDetails::selectRaw('AVG(price) as avg_price, AVG(margin) as avg_margin, AVG(weight) as avg_weight')
->where('id', '=', $id)
->where('active', '=', true)
->get();
be careful about the ' location, it should be around the hole statement in select raw expressions.
but :
->where('id', '=', $id)
this statment will make the query return only one row, I think you mean
->where('product_id', '=', $id)
is not?

Related

How do I search by a table included using with() statement

I am trying to search with a column on a connected table using with. According to laravel the following should work.
$lineItems = Invoice::with(['invoiceHeader' => function ($query) {
$query->where('community_id', '=', 1);
}, 'invoiceLineItems'])
->limit(10)
->get()
->toArray();
However, I don't get anything from the invoiceHeader table and I get all the invoices available. If I take out the function I get the same but with invoiceHeader's table values showing up.
$lineItems = Invoice::with(['invoiceHeader', 'invoiceLineItems'])
->limit(10)
->get()
->toArray();
It seems I might be doing something of a right join where I get all the Invoices but then only the invoiceHeader values when applicable to the foreign key.
Edit:
I put ->toSql(); after the limit() and it shows I only get the following.
"select * from `invoice` limit 10"
You should use whereHas mixing with with:
$lineItems = Invoice::with(['invoiceHeader', 'invoiceLineItems'])
->whereHas('invoiceHeader', function ($query) {
return $query->where('community_id', 1);
})
->limit(10)
->get()
->toArray();

How create a subquery with eloquent and laravel with softdeletes

I am using Laravel 7 and Vue.js 2.
I want to retrieve the tasks that are not assigned to a specific user.
I made the following code that works correctly.
$tasks_user = TaskUser::select('task_id')
->where('user_id', $id)
->get();
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', $tasks_user)
->distinct()
->get();
By the way to be more elegant I decided to transform the above code into a single query as follows:
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', function($q) use ($id)
{
$q->select('task_id')
->from('task_user')
->where('user_id', $id)
->get();
})
->distinct()
->get();
Unfortunately I discovered that the above query didn't work because it doesn't considers softdeletes.
For example, if the user with id 3 was related with the task 7 but now that row has been deleted with softdeletes in the table task_user, the first code returns also the task with id 7 (correctly) and the second one not (uncorrectly).
So finally, I must do a single query that works as the first code.
Can help?
You can actually combine both approaches. whereNotIn accepts also an Eloquent Query, it doesnt need to be a callback. Try this:
$userRelatedTasksQuery = TaskUser::select('task_id')
->where('user_id', $id);
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', $userRelatedTasksQuery)
->distinct()
->get();
Be sure to not use get() at the end of the $userReleatedTasksQuery, as you want the eloquent query instance, not the result.

Finding a user with highest post created in last 24 hours, laravel Eloquent

How to find the user with the highest post created in the last 24 hours in laravel?
sorted by the number of posts in descending order.
If I'm not wrong, you are asking for the users with the highest number of posts created in the last 24 hrs.
To accomplish this, do the following:
$users = User::withCount(['posts' => function ($query) {
$query->where('created_at', '>=', carbon()->now()->subDay());
}])->orderBy('posts_count', 'DESC')
->get();
As the documentation states, you can add constraints to the queries.
Counting Related Models
If you want to count the number of results from a relationship without actually loading them you may use the
withCount method, which will place a {relation}_count column on
your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You may add the "counts" for multiple relations as well as add
constraints to the queries:
$posts = Post::withCount(['votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
use Carbon\Carbon;
get user id:
$minusday = Carbon::now()->subDay();
$user_id = DB::table('posts')
->select('user_id', DB::raw('count(id) as total'))
->where('created_at', '>=', $minusday)
->groupBy('user_id')
->orderBy('total','desc')
->limit(1)
->get();
In regular SQL syntax you'd need something like below:
SELECT COUNT(id), user_id
FROM posts
WHERE created_at = today
GROUP BY user_id
ORDER BY COUNT(user_id) DESC
LIMIT 1;
It gets all the posts, groups them by user_id, sorts them with the highest user_id count up top and gets the first record.
I am by no means an expert on SQL, let alone the query builder in Laravel, so someone else would probably be better at writing that.
I know that you can get the posts that were created today by using Carbon, like so:
Post::whereDate('created_at', Carbon::today())->get();
EDIT: This might work for you:
$last24h = Carbon::now()->subDay();
DB::table('posts')
->select(array(DB::raw('COUNT(id)', 'user_id')))
->where('created_at', '>=', $last24h)
->groupBy('user_id')
->orderBy('COUNT(id)', 'DESC')
->limit(1)
->get();
Be sure to include use Carbon\Carbon to be able to use Carbon.
This should give you both the amount of posts and the corresponding user id.

Laravel 5.2 Eloquent Order Query by Eagerly Loaded Data

Is it possible to order the results of an Eloquent query by the eagerly loaded data. To be clear I get the right data, it is paginated properly, and works in every way except it isn't ordered the way I need it to be. In this case I'd like to sort users by profile.firstname.
Something like this:
$results = User::where('id', '!=', $user->id)
->whereNotIn('id', $ids)
->with([
'profile' => function ($query) {
$query->addSelect(['id', 'first_name', 'last_name']);
},
])
->orderBy('profile.first_name', 'desc')
->paginate();
The data is used in an Ionic application and is used for an infinite scroll so I need to have the data in the format below ordered by first_name prior to being received on the client, but the above example doesn't work.
DATA needed for Ionic View
user: {
id: '1',
username: 'aUsername'
email: 'anEmail'
profile: {
id: '2',
user_id: '1',
first_name: 'aFirstName',
last_name: 'aLastName',
phone_number: '999-999-9999'
}
}
...
Solution (with flattened result set)
$results = User::join('user_profiles', 'users.id', '=', 'user_profiles.user_id')
->select('users.id', 'users.username', 'user_profiles.first_name', 'user_profiles.last_name')
->where('users.id', '!=', $user->id)
->whereNotIn('id', $existingContactIds)
->where('users.username', 'like', $request->input('username') . '%')
->orderBy('user_profiles.first_name')
->paginate();
Solution (with proper result format)
$results = User::where('users.id', '!=', $user->id)
->whereNotIn('users.id', $existingContactIds)
->where('users.username', 'like', $request->input('username') . '%')
->with([
'profile' => function ($query) use ($columns) {
$query->addSelect('user_profiles.first_name', 'user_profiles.last_name');
},
])
->select('users.id', 'users.username')
->join('user_profiles', 'users.id', '=', 'user_profiles.user_id')
->orderBy('user_profiles.first_name')
->paginate();
For ordering by another table, you need to perform a join.
Chain in a join for your profile table
-> join('profiles', 'profiles.id', '=', 'users.profile_id')
You'll then be able to orderBy first_name (though not profile.first_name).
It isn't possible to orderBy eager loaded tables. If you look at the SQL Laravel produces, you'll see that it firsts gets all the results from the users tables then passes the IDs of just the results (so a subset of all results if you're using pagination) to the profiles table to get relevant data.
You could sometimes avoid the join by using the Collection method sortBy on the results, but this won't work when coupled with pagination as sortBy only works on the results, so will be ordering a subset of data only (but sortBy does support the dot notation you've tried using for your orderBy).
Im seeing your code and I think it should be ->orderBy('profile->first_name', 'desc') or try this other way ->orderBy('first_name', 'desc')
Try like this
$results = User::where('id', '!=', $user->id)
->whereNotIn('id', $ids)
->with([
'profile' => function ($query) {
$query->addSelect(['id', 'first_name', 'last_name']);
$query->orderBy('first_name','DESC');
},
])
->paginate();

How do I select an object from a collection where 1 of 2 columns equals a value?

I have the following which returns a collection of objects;
$cwGames = Schedule::where('date', '<', Carbon::now()->addDays(7))
->where('date', '>', Carbon::now()->addDays(1))->get();
Now I want to select from this collection only the object where $id is in col A or $id is in col B.
What's the best way to do this? Thanks.
Thanks to ExoticChimp for direction on the answer below. I added the use ($id) to get it to work. See edit here...
$cwGames = Schedule::where('date', '<', Carbon::now()->addDays(7))
->where('date', '>', Carbon::now()->addDays(1))
->where(function ($query) use ($id) {
// Replace col_A and col_B with your column names
$query->where('home_team_id', $id)
->orWhere('away_team_id', $id);
})->get();
The question is slightly ambiguous what you mean by col A and col B. However, if what you want is to add in an additional where clause which is effectively if colA or colB = $id, then the following should work (from the Laravel docs)
$cwGames = Schedule::where('date', '<', Carbon::now()->addDays(7))
->where('date', '>', Carbon::now()->addDays(1))
->where('name', '=', 'John')
->where(function ($query) {
// Replace col_A and col_B with your column names
$query->where('col_A', $id)
->orWhere('col_B', $id);
})
->get();
https://laravel.com/docs/master/queries#where-clauses
Laravel Collections has a lot of cool features that I think you should check on the official docs. In your case, the "filter" method is the best way to process the elements of your collection.
According to the official docs:
The filter method filters the collection by a given callback, keeping only those items that pass a given truth test
In your case:
$cwGames->filter(function($value, $key) {
// If your check returns true, the element will be kept in the collection
}
Check the docs here (filter method explained)

Resources