I have a one to many relation between Person and Visit tables like this:
public function visits()
{
return $this->hasMany('App\Models\Visit');
}
And want to get the persons who has a sickness_id of 1 in the relation like this:
$persons = Person::whereHas('visits', function ($query) {
$query->where('sickness_id', 1);
})->get();
And it works fine but I want to search just last visit of each person.
I mean if a person has two visits, one with sickness_id of 1 and other with sickness_id of 2, do not return this person because last visit is sickness_id of 2.
you can use hasOne relation for that:
public function lastVisit()
{
return $this->hasOne('App\Models\Visit')->latest();
}
then you can load it:
$persons = Person::whereHas('lastVisit', function ($query) {
$query->where('sickness_id', 1);
})->get();
I think the above answer is gonna work. Using addSelect, this might also work:
Person::addSelect('last_visit_id', Visit::select('id')
->whereColumn('person_id', 'persons.id')
->latest()
->limit(1)
)
->where('last_visit_id', 1)
->get();
$person = modelname::query()
->where(['sickness_id' => '1' ])
->select('visits')
->orderBy('id', 'DESC')
->first();
Related
Help me please.
I'm trying to write a function where I get all the categories of my forum with the 3 most recently updated Topics in the given categories.
But according to the result, take(3) filters by id (where the id is not higher than 3), and I need to get the last 3 records.
public function index()
{
$forums = Category::with(['posts' => function ($q){
return $q->take(3)->get();
}])->get();
dd($forums);
}
you should order your complete query by update_at descending, only after you can take the first 3.
$q->orderBy('update_at', 'desc')->take(3)->get();
Your Categories table seems to be a different table from posts, so when a post is created or updated you should also set update_at of its category to now.
As far as I know you can not use take() or limit() inside with();
EDIT: solution that was selected by mr.Sardov is to use package staudenmeir/eloquent-eager-limit.
Link is provided below this answer.
So for you need to do is by limit it from model relationship.
For example:
class Category extends Model {
public function posts()
{
return $this->hasMany('App\Models\Post');
}
public function limitPosts()
{
return $this->hasMany('App\Models\Post')
->limit(3);
}
public function limitLatestPosts()
{
return $this->hasMany('App\Models\Post')
->orderBy('created_at', 'desc'). // or use ->latest()
->limit(3);
}
}
And that use it like this:
Category::query()
->with(['limitPosts' => function($query) {
$query->orderBy('created_at', 'desc'); // the last records
}])
->where('id', '<=', 3) // id not higher than 3
->get();
Or
Category::query()
->with('limitLatestPosts')
->where('id', '<=', 3) // id not higher than 3
->get();
Hope this can help you out.
I have created many-to-many relation using belongsToMany function:
class Doctor extends Model
{
...
public function categories()
{
return $this->belongsToMany('App\Category', 'doctors_to_categories', 'doctor_id', 'category_id');
}
...
}
Now I want to create query with many-to-many condition. In SQL in would be:
SELECT *
FROM `doctors`
JOIN `doctors_to_categories`
ON `doctors_to_categories`.`doctor_id` = `doctors`.`id`
WHERE `doctors_to_categories`.`category_id` = 1
I have tried to achieve this like:
$doctors = Doctor::with(['categories' => function($query) {
$query->where('category_id', '=', 1);
}])->get();
Or
$doctors = Doctor::with(['categories' => function($query) {
$query->where('categories.id', '=', 1);
}])->get();
But it is not working. Any ideas how it should be? Thanks for any help.
The with() function does not actually introduce a join in your query, it just loads the relation of all models as a second query. So the with() function couldn't possibly change the original result set.
What you are looking for is whereHas(). This will add a WHERE EXISTS clause to the existing query.
$doctors = Doctor::with('categories')->whereHas('categories', function ($query) {
$query->where('categories.id', 1);
})->get();
Using ->with() doesn't actually limit the results of the Doctor::...->get() query; it simply tells Laravel what to return in the relationships attribute. If you actually want to enforce returning only Doctors that have a category 1 relationship, you need to use whereHas():
$doctors = Doctor::whereHas('categories', function($query) {
$query->where('categories.id', '=', 1);
// `id` or `categories.id` should work, but `categories.id` is less ambigious
})->get();
You can add whereHas condition for this. Try code below:
$doctors = Doctor::with('categories')->whereHas('categories', function($query) {
$query->where('id', 1);
})->get();
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();
I have 2 models: TheSeries and TheEpisodes.
TheSeries has many TheEpisodes and TheEpisodes has one TheSeries.
I am trying to list all TheSeries and display latestEpisode in each, by using TheEpisodes.addDate.
The code I have right now is this:
$TheSeries = TheSeries::with('TheEpisodes');
What should I do to display only latest 1 episode for each TV serial?
EDIT
->take(1) and ->limit(1) do not work for TheEpisodes
EDIT (Latest Semi-Working Code)
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc');
}])->get()
This works, it returns the episodes in correct order but I am unable to limit the results to 1. This following codes don't work:
// Example 1
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->take(1);
}])->get()
// Example 2
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->limit(1);
}])->get()
// Example 3
$results = TheSeries::take(5)->with(['TheEpisodes' => function($q) {
$q->orderBy('addDate', 'desc')->first();
}])->get()
Those are the column names of the tables:
TheSeries - id, originalTitle, aliasTitle, description, imageURL, startingDate, endingDate, activeBool
TheEpisodes: id, seriesID, authorID, addDate, episodeVersion
Define a TheLatestEpisode hasOne relation on your TheSeries model:
class TheSeries extends Model
{
public function TheLatestEpisode()
{
return $this->hasOne(TheEpisode::class, 'seriesID')->orderBy('id', 'desc');
}
}
Then you can easily do:
$series = TheSeries::with('TheLatestEpisode')->get();
You can try it as:
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->take(1);
}])
->get();
Or try with limit as:
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->limit(1);
}])
->get();
$TheSeries = TheSeries::with(['TheEpisodes' => function($q) {
$q->orderBy('created_at', 'desc')->first();
}])
->get();
can't work?
Why don't you use the DB statement ?
DB::table('TheEpisodes')
->leftjoin('TheSeries','TheEpisodes.SeriesId','=','TheSeries.id')
->select('TheEpisodes.*','TheSeries.id as sId','TheSeries.name as sName',...)
->orderBy('TheEpisodes. addDate','desc')
->take(1)
->get();
You can try something like this in your TheSeries model: (it is easier)
public function getLatestEpisodeAttribute(){
$episode = TheEpisodes::where('series_id',$this->attributes['id'])
->latest()
->first();
if(!$episode){
return "no episodes for this Series";
}
return $episode;
}
On your controller just do the query normally without including anything related to TheSeries and you can access it values in your blade file like this:
//lets suppose there is a title attibute in the episodes
{{$TheSeries->latest_episode->title}}
//or a duration attribute
{{$TheSeries->latest_episode->duration}}
The best solution I found, was to create a one-to-one relationship with orderBy('addDate'), it works!!!
Reference: https://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
$TheSeries = TheSeries::with('TheEpisodes')->first();
Or
$TheSeries = TheSeries::with('TheEpisodes')->firstOrFail();
I have News model, and News has many comments, so I did this in News model:
public function comments(){
$this->hasMany('Comment', 'news_id');
}
But I also have field trashed in comments table, and I only want to select comments that are not trashed. So trashed <> 1. So I wonder is there a way to do something like this:
$news = News::find(123);
$news->comments->where('trashed', '<>', 1); //some sort of pseudo-code
Is there a way to use above method or should I just write something like this:
$comments = Comment::where('trashed', '<>', 1)
->where('news_id', '=', $news->id)
->get();
Any of these should work for you, pick the one you like the most:
Eager-loading.
$comments = News::find(123)->with(['comments' => function ($query) {
$query->where('trashed', '<>', 1);
}])->get();
You can inject the parameter to query function by use($param) method, that allows you to use dynemic query value at runtime.
Lazy-loading
$news = News::find(123);
$comments = $news->comments()->where('trashed', '<>', 1)->get();
I couldn't help but notice, though, that what you're probably trying to do is handle soft deleting, and that Laravel has built-in functionality to help you with that: http://laravel.com/docs/eloquent#soft-deleting
You can do simply in your eloquent model file.
do like this :
public function comments_with_deleted()
{
return $this->belongsTo('Comments', 'id')->where('deleted', 1);
}
public function comments()
{
return $this->belongsTo('Comments', 'id');
}
call like this :
// for show comments with deleted
$comments = News::find(123)->with('comments_with_deleted');
// for show comments without deleted
$comments = News::find(123)->with('comments');
rmobis's answer was what I needed, but it throws an error in current Laravel 5. You have to use it as an associatve array now:
$comments = News::find(123)->with(
['comments' => function ($query) {$query->where('trashed', '<>', 1);}]
);
Took me some time to figure it out, hope this will help others.
Read more in Laravel's Docs (5.6): https://laravel.com/docs/5.6/eloquent-relationships#querying-relations