Laravel eager loading & whereHas - laravel

Several questions have been asked about this but none provided a satisfactory explanation for this problem.
I run the following query:
$return = Vacation::whereHas('dates', function ($query) use ($from, $to) {
$query->whereBetween('start_date', [$from, $to]);
})->get();
According to the query log, this produces the following SQL. The returned result is correct but there is a problem with the JOINed data.
select * from `vacations` where exists (select * from `vacation_dates` where `vacations`.`id` = `vacation_dates`.`vacation_id` and `start_date` between '2017-08-01 00:00:00' and '2017-08-30 00:00:00')
Since there's no JOIN, the related records are added afterwards through eager loading and the constraint is lost.
The scenario involves Vacations that have multiple start / stop dates and I want to check which Vacations have a start_date within the $start and $end date range.
If there's no occurrences, no records are returned.
When there is an occurrence, the Vacation is returned with ALL the dates and not just the ones in the constraint.
I understand how / what it's doing but don't see how I can get what I need: the record with the joined data that follows the constraint.
Anyone?

Solution:
$callback = function($query) use($from, $to) {
$query->whereBetween('start_date', [$from, $to]);
};
$return = Vacation::whereHas('dates', $callback)->with(['dates' => $callback])->get();
Solution is from this SO post

Same answer as #stef gave, but better looking syntax (I believe)
$return = Vacation::query()
->whereHas('dates', $datesFilter = function($query) use ($from, $to) {
$query->whereBetween('start_date', [$from, $to]);
})
->with(['dates' => $datesFilter])
->get();

Related

Filter record through dates not working laravel

I have a expiry_date (type=date) column in my table
$currentDate = date('Y-m-d');
$Data = Post::whereDate('expiry_date','<=',$currentDate)->where(['status' => 'active'])->orWhere(['p_id' => 3])->select('id','title','status','p_id','expiry_date')->orderBy('id', 'DESC')->get();
i want to filter data if current date is greater than expiry date then those record should not be shown but in my scenario i'm still getting record.
Any solution Thanks.
You must group orWhere clause in closure. Grouping in closure is like () in real query.
$Data = Post::whereDate('expiry_date','<=',$currentDate)
->where(function($query){
return $query
->where(['status' => 'active'])
->orWhere(['p_id' => 3]);
})
->select('id','title','status','p_id','expiry_date')
->orderBy('id', 'DESC')
->get();
But, because I don't know your project - i may wrong with grouping.

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();

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.

Making a query to get elements active on a time between 2 specific dates

Cant seem to get the logic right. I have a function that takes 2 dates ($date1 and $date2). I have database records of events that also have 2 dates (starting_at and ending_at).
I want to get all events that completely or partially overlap with time between $date1 and $date2. I have browsed other similar topics and came up with this... but i have a feeling it is not correct.
$events = Event::with(['user']);
$events->where(function($query) use ($date1,$date2) {
$query->where(function($query) use ($date2) {
$query->where('starting_at', '<=', $date2);
});
$query->where(function($query) use ($date1) {
$query->where('ending_at', '>=', $date1);
});
});
If dates are in the same table, use:
$events = Event::with('user')
->where('starting_at', '<=', $date2)
->where('ending_at', '>=', $date1)
->get();
Make sure you've added starting_at and ending_at to the dates array:
protected $dates = ['starting_at', 'ending_at'];
With Laravel 5.3 (maybe 5.2, but 5.3 for sure) you can do:
$events = $events->whereBetween($date1, $date2)->get();
Lower version could be:
$events = $events->where('starting_at', '<=', $date2)
->where('ending_at', '>=', $date1)
->get();
Laravel has a simple way of doing this.
$events = Event::with(['user'])->whereBetween('field_name', array($date1, $date2))->get();
Check the docs here https://laravel.com/docs/5.3/queries#where-clauses
UPDATE
If date1 and date2 are from entirely different columns on the database, then refer #Alexey Mezenin answer which is more appropriate.

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