Laravel Scope using different Model (has a relationship) - laravel

Trying to get a scope setup that will let me filter through one model (Properties), based on a column (name) in another model (Users).
I have a relationship setup to where a User has many properties, and a Property belongs to a user.
Right now I've tried this...
public function scopeFilterByUser($query, $user)
{
return $query->join('users', function($join) use ($user)
{
$join->on('properties.user_id', '=', 'users.id')
->where('name', 'LIKE', '%'.$user.'%');
});
}
which "works" except that it breaks my Accessor on the Property model.
Both the Property and User have their own tables, with columns for address, city, state, and zipcode. Only the Property has a full_address column which I'm using an accessor for like this...
public function getFullAddressAttribute($value)
{
$address = $this->address;
$city = $this->city;
$state = $this->state;
$zipcode = $this->zipcode;
return $address.', '.$city.', '.$state.', '.$zipcode;
}
Both of these are in the Property model. For some reason when accessing the $property->full_address after filtering the properties with this filterByUser.. full_address is returning the combined values from the User table and not the Property table.
I'm wondering what the best way would be to set this up so I can filter Properties by the Users name. The property DOES have a user_id column, but I'm looking to filter based on user name.
It all works if I rename all of the property address fields to prop_address, prop_city, etc... but this is not ideal.
Any ideas? I've tried everything I can think of.

Can you try changing your scope to this :
public function scopeFilterByUser($query, $user)
{
return $query->whereHas('users', function($q) use ($user) {
$q->where('name', 'like', '%' . $user . '%');
});
}
You can then use it as
$properties = Property::filterByUser('name')->get();
// Then
$properties->first()->full_address
Let me know in the comments if this doesn't work :)

Related

laravel multiple relation query

I have 3 model in my project and i want to get a query or collection result as i say:
JewelsItem model:
protected $table = 'jewel_items';
public function jewel(){
return $this->belongsTo('App\Models\Jewel');
}
public function sellInvoice(){
return $this->belongsTo(SellInvoice::class,'sell_invoice_id');
}
2.Jewel model:
public function jewelsItems(){
return $this->hasMany('App\Models\JewelsItem');
}
3.sellInvoice model:
protected $table = "sell_invoices";
public function jewelsItems(){
return $this->hasMany(JewelsItem::class,'buy_invoice_id');
}
Query: i want to get all jewelsitems that has no sellInvoice and has jewel name like 'something'.
note: jewel model has a name attribute .and i want to add name of jewel to first of all the collection's items of result.
i know how to get all jewel with name of 'something' :
Jewel::where('name','like','something'.'%')->get();
but i can't get all jewelItem related to it an add name of jewel to first of them.
To look for condition in another relationship you can use whereHas() and whereDoesntHave()
$name = 'something';
$items = JewelsItem::whereHas('jewel', function($query) use ($name) {
$query->where('name', 'LIKE', "%{$name}%");
})
->whereDoesntHave('sellInvoice')
->with('jewel')
->get();
It reads: getting jewel items where jewel has $name in the name field
and doesn't have sell invoice
then add related jewel to the jewel item.
When you want to retrieve the name of the jewel, you can access its property like so.
foreach($items as $item) {
echo $item->jewel->name;
}

How I can get full query from relation

I have model Company with relation
public function contact()
{
return $this->belongsToMany(\App\Models\Contact::class, 'company_contacts', 'company_id', 'contact_id')
->wherePivot('primary', '=', true)->withPivot('primary');
}
I need to sort items with contact.last_name column.
So I build query
$scope = $scope->leftJoin('company_contacts', "companies.id", '=', "company_contacts.company_id")
->leftJoin('contacts', "company_contacts.contact_id", '=', "contacts.id")
->groupBy(['companies.id', 'contacts.id'])
->select(['companies.*', 'contacts.last_name'])
->order('contacts.last_name');
But this should be more universal
Value of names and tables I can get from $relation
$relation = $scope->getRelation('contact');
$key = $relation->getQualifiedOwnerKeyName(); // etc
And then substitute values into the scope-builder. But I can not get this part::
->wherePivot('primary', '=', true)->withPivot('primary')
I found public property pivotWheres of BelongsToMany class and I can use that.
$relation->pivotWheres

Creating a search filter and one of the fields is from other table

So I`m trying to create a search filter.
And i have created this:
$data = Input::all();
$ads = new Ads();
if($data['description']) {
$ads = $ads->where('description', 'LIKE', '%' . $data['description'] . '%');
}
if($data['min']) {
$ads = $ads->where('price', '>', $data['min']);
}
if($data['max']) {
$ads = $ads->where('price', '<', $data['max']);
}
And I have $data['city']. But the problem is that $ads dont have a city, it has userID and every user has its city. How would i do something like this but it would actualy search the user that owns this ad and check if the city is like the searched one.
I tried somthing like this:
$resultAds = $ads;
if($data['city']) {
foreach($resultAds as $rAds) {
if($rAds->compareCity($data['city'])) {
$resultAds[] = $rAds;
}
}
}
$resultAds = $resultAds->paginate(12);
But that dosnt work.
It gives this error:
Symfony \ Component \ Debug \ Exception \ FatalErrorException (E_ERROR)
Call to a member function compareCity() on a non-object
And $rAds is for somereason boolean? I think thats becouse i havent done the ->get(); but i cannot do that becouse i would like to paginate the results.
Thanks for help.
So now my problem is to create a realationship to get city field out of users table.
Ads table
id
description
price
userID
User table
id
username
password
phone
city
I need to make a releationship so i could get all the ads with the requested city based on what city has the user with the userID taken from ads table.
You would want to use whereHas as long as your relationship methods are setup correctly.
if($data['city'])
{
$ads->whereHas('users', function($q) use ($data)
{
$q->where('city', $data['city']);
});
}
If the ad to user is a belongsTo relationship, then you don't need a table or even to setup a relationship just to filter on city field from users table. You can do a join on the users table. I am assuming your user table is called users:
if($data['city']) {
$ads = $ads->join('users', 'users.id', '=', 'city.userID')
->where('users.city','like','%'.$data['city'].'%');
}
The advantage of a join over whereHas is that it has no subquery that is executed against every row of the ads table, making it more efficient. Additionally, if you always join the users table, whether there is a filter on city or not, then you can pull the city from the users into the ads without having to do additional queries or with() relationships. Like this:
$ads = $ads->join('users', 'users.id', '=', 'city.userID')
if($data['city']) {
$ads = $ads->where('users.city','like','%'.$data['city'].'%');
}
later:
$result = $ads->get(['ads.*', (new Illuminate\Database\Query\Expression('users.city')]);
or if you are going to paginate the result:
$result = $ads->paginate($pageSize, [
'ads.*',
(new Illuminate\Database\Query\Expression('users.city'))
]);
every $ad that is in the $result will have a $ad->city field.
So the answer is like this:
if($data['city']) {
$ads = $ads->whereHas('users', function ($q) use($data) {
$q->where('city','LIKE','%'.$data['city'].'%');
});
}
This is the solution that worked. It is kinda taken from the other answer.
And I needed to add this in the model:
public function users() {
return $this->belongsTo('User');
}
And now it works.
Thanks everyone for help!

How to get relationship with union in eloquent?

How can I make relationship with union in laravel eloquent? I've already tried two different approaches.
User::with(['url' => function($query) use(&$some_property) {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $some_property);
$query = $query->union($favouriteUrls);
}]);
In the first attempt there wasn't any union in the query. Then I tried to move the logic to the model.
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $this->some_property);
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
It has executed successfully but $this->some_property was set inside the query to the null value.
I can't create two separate relationship in this case. It has to be one with union. How can I fix it?
If you call that relation as User::with('urls') you will get that $this->some_property doesn't exists, because the object itself doesn't exists. But if you call the urls() method on an object, it should work. Something like this:
$user = User::find(1);
$user->urls; // here $this->some_property should have a value
Assuming you're calling the urls() method from a User object, the $this->some_property should give you the value. If for some reason you cannot access a property directly on an Eloquent model you can always refer to the attributes[] array inside of the model. For example
// calling
$this->some_property
// should be the same as
$this->attributes['some_property']
Fetch all the users joining with the condition
Assuming users is the table for all the users, in your query you could change $this->some_property with 'users.some_property' and everything should work as expected, for each user it will query based on that property. Here is the code:
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', 'users.some_property');
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
And then just call the method like this:
User::with('urls')->get();

Laravel eloquent: get data with model wherePivot equal to custom field

I have an eloquent object Performer that has Albums and Albums have Images
Here is setup:
Model Performer->albums():
public function albums()
{
return $this->belongsToMany('Album','performer_albums','performer_id','album_id');
}
Model Album->images()
public function images()
{
return $this->belongsToMany('Image','album_images','album_id','image_id')->withPivot(['type','size']);
}
I have performer object stored as such:
$performer = Performer::where...->first();
Now I need to get Performer's Albums with images where size is 'large'
So to avoid nesting queries, can I use with()?
I tried
$performer->albums()
->with('images')
->wherePivot('size','large')
->get();
But laravel tells me it's trying to use wherePivot for Performer-Album relationship (M-2-M)
PS. I am also aware that I can do this,
$performer = Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
but question remains the same.
You need eager load constraints:
$performer->albums()
->with(['images' => function ($q) {
$q->wherePivot('size','large');
}])
->get();
And btw, no, you can't do this:
Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
instead you could do:
Performer::with(['albums.images' => function ($q) {
$q-> .....-conditions for additional fields in album_images....
}])->get();

Resources