eloquent filter result based on foreign table attribute - laravel

I'm using laravel and eloquent.
Actually I have problems filtering results from a table based on conditions on another table's attributes.
I have 3 tables:
venue
city
here are the relationships:
a city has many locations and a location belongs to a city.
a location belongs to a venue and a venue has one location.
I have a city_id attribute on locations table, which you may figured out from relationships.
The question is simple:
how can I get those venues which belong to a specific city?
the eloquent query I expect looks like this:
$venues=Venue::with('location')->where('location.city_id',$city->getKey());
Of course that's not gonna work, but seems like this is common task and there would be an eloquent command for it.
Thanks!

A couple of options:
$venues = Venue::whereIn('location_id', Location::whereCityId($city->id)->get->lists('id'))
->get();
Or possibly using whereHas:
$venues = Venue::whereHas('location', function($query) use ($city) {
$query->whereCityId($city->id);
})->get();

It is important to remember that each eloquent query returns a collection, and hence you can use "collection methods" on the result. So as said in other answers, you need a Eager Loading which you ask for the attribute you want to sort on from another table based on your relationship and then on the result, which is a collection, you either use "sortBy" or "sortByDesc" methods.
You can look at an example below:
class Post extends Model {
// imagine timpestamp table: id, publish, delete,
// timestampable_id, timestampble_type
public function timestamp()
{
return $this->morphOne(Timestamp::class, 'timestampable');
}
}
and then in the view side of the stuff:
$posts = App\Post::with('timestamp')->get(); // we make Eager Loading
$posts = $posts->sortByDesc('timestamp.publish');
return view('blog.index', compact('posts'));

Related

Eloquent with diferent relationships for same table

i need some hints and what is the best way and pratice with laravel to solve this problem!
I have a main table called colors with the fields category_id and type and i have in my models 2 relationships for the category_id, the bluecategory and redcategory.
In some cases i use the id from bluecategory and in other exemples i use the redcategory id.
Now in one page i want to shows all the results from colors, but i can't do this $data->bluecategory or $data->redcategory because i dont'no what record is using what relationship.
My ideia was using a function to send parameters category_id and type and inside the function discover what is the correspondente relashion and return the correct result.
But how i can handle this?
Sorry its a bit confuse!
You could add a scope to your modal and than chain it to your existing query.
Example
public function scopeName($query, $catID, $type)
{
return $query->where('category_id, $catID)
->where('type', $type);
}

Laravel: How to retrieve this nested model

In Laravel I have ModelA, ModelB and ModelC. ModelA has many ModelB. ModelB has many ModelC. I want to retrieve all ModelC for a selection of ModelA. How do I do this?
I tried the following:
$models = ModelC::with(['modelB','modelB.modelA' => function ($query) {
$query->where('owner', 123);
}])->get();
But the first query in that case is select * from model_c. Obviously not the result I am looking for.
Imagine that you were received 100 objects from the database, and each record had 1 associated model (i.e. belongsTo). Using an ORM would produce 101 queries by default; one query for the original 100 records, and additional query for each record if you accessed the related data on the model object. In pseudo code, let’s say you wanted to list all published authors that have contributed a post. From a collection of posts (each post having one author) you could get a list of author names like so:
$posts = Post::published()->get(); // one query
$authors = array_map(function($post) {
// Produces a query on the author model
return $post->author->name;
}, $posts);
We are not telling the model that we need all the authors, so an individual query happens each time we get the author’s name from the individual Post model instances.
Eager Loading
As I mentioned, ORMs “lazy” load associations. If you intend to use the associated model data you can trim that 101 query total to 2 queries using eager loading. You just need to tell the model what you need it to load eagerly.
Here’s an example from the Rails Active Record guide on using eager loading. As you can see, the concept is quite similar to Laravel’s eager loading concept.
$posts = Post::with('author')->limit(100)->get();
I find that I receive better understanding by exploring ideas from a wider perspective. The Active Record documentation covers some examples that can further help the idea resonate.
I managed to solve this with nested whereHas calls as follows:
$models = modelC::whereHas('modelB', function ($query) {
$query->whereHas('modelA', function ($query) {
$query->where('owner', 123);
});
})->get();
Laravel to the rescue, yet again!

How to retrieve data through model?

I have Order model with another relation OrderPhoto:
public function OrderPhoto()
{
return $this->hasMany('App\OrderPhoto');
}
In turn OrderPhoto model has relation:
public function Photo()
{
return $this->belongsToMany('App\Photo');
}
So, how to get data from OrderModel with related data from third model Photo?
I guess this:
Order::with("OrderPhoto.Photo")->get();
to retrieve only data from Photo model for each Order
So, each Order has some OrderPhotos. Relationship is one to many.
But one item from OrderPhotos is related with primary key from table Photos. It is one to one relation.
My result query should be:
select `photos`.*, `ordersphoto`.`Orders_Id` from `photos` inner join `ordersphoto` on `ordersphoto`.`Photos_Id` = `photos`.`Id` where `ordersphoto`.`Orders_Id` in (1);
How to use hasManyThrough for this query?
Just having a quick look at your relationships it looks like you could create a hasManyThrough relationship on the order Model.
public function Photo {
return $this->hasManyThrough('App\OrderPhoto', 'App\Photo')
}
You may need to add the table keys to make it work
This will allow you to do:
Order::with("Photo")->get();
You can see more details here https://laravel.com/docs/5.5/eloquent-relationships#has-many-through
Update
Try this
public function Photo {
return $this->hasManyThrough('App\Photo', 'App\OrderPhoto', 'Order_id', 'Photos_id', 'id', 'id')
}
It is a little hard to get my head around your DB structure with this info but you should hopefully be able to work it out. This may also help
https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_hasManyThrough

Retrieve model with its recursive relationship in a single model

I have two Models, Account and Contractor. An account(user) can be a contractor so I created a recursive relationship. The Contractor model look like this.
class Contractor extends Model
{
protected $table = "contractors";
public function user()
{
return $this->belongsTo("\App\Account", "user_id", "id");
}
}
Having in mind that an account has the next fields
Account
id
passwd
name
I would like to retrieve contractors with its fields and the user fields merged just as if they where the same, like that:
Contractor
id
passwd
name
contractor_type
I don't want to do:
\App\Contractor::with('user')->first();
I would like to retrieve all fields with
\App\Contractor::first()
is possible to achieve that by overriding some method or in some tricky way?
You need a join statement to achieve this. Something along these lines (there is inner, left and right join, I'm not sure which one suits you best).
$contractor = \App\Contractor::join('users', 'contractors.user_id', '=', 'accounts.id')
->select('accounts.id', 'contractors.user_id', 'accounts.id', 'accounts.passwd', 'accounts.name', 'contractors.contractor_type')
->first();
I assumed table names "accounts" and "contractors".
I think your query like:
$rsltContractors = \App\Contractor::selectRaw('accounts.id', 'accounts.passwd', 'accounts.name','contractors.id','contractors.user_id','contractors.name','contractors.contractor_type')
->join('accounts', 'accounts.id', '=', 'contractors.user_id')->first();
Hope this work for you!

Laravel complicated relationship

My laravel app structed like this:
People: Model & Controller
Pets: Model & Controller. Has many to many relationship with People.
Abiilities: Model & Controller.
People_pets: People pets. (pivot table with people_id and pet_id). Also has 4 columns of abbility1_id, abbility2_id, abbility3_id and abbility4_id.
Now.. I built an API method to return the user pets, and it looks like this:
public function pets()
{
return $this->belongsToMany(Pet::Class, 'user_pets')->withPivot(
'abbility1_id',
'abbility2_id',
'abbility3_id',
'abbility4_id'
)->orderBy('position','asc');
}
which makes $user->pets returns the user list of pets, with the pivot information of user_pets and the abbility ids.
The question
What I want to do is add a json object to this method result named "abbilities" and get the data of the abbilities there such as name and description from the Abbilities model/controller.
How I can add the abbilities information to my query out of just the ID's?
The output of the current API CALL:
Desired output: array of Abbilities inside every pet object with the detail of the attack_id's inside $user->pets->pivot->attack1_id
You could just use the query builder and orWhere to grab the data. I don't think you can access it through relationships with the way you have it set up.
I don't know how you access your pet ability ids, but I'm guessing it's like $pet->abbility1_id.
$abbilities = Abbilities::where('id', '=', $pet->abbility1_id)
->orWhere('id', '=', $pet->abbility2_id)
->orWhere('id', '=', $pet->abbility3_id)
->orWhere('id', '=', $pet->abbility4_id)
->get();

Resources