Laravel query with relationship where have all ids - laravel

I try to do a query where I get a room where I have all the services.
With my current code I get the room that has at least one of the services from the array, because I am using whereIn.
$rooms = Room::select([
'rooms.id',
'rooms.available_from',
'rooms.available_till',
'rooms.description',
'rooms.room_type_id',
'room_types.name as room_types_name',
])
->leftJoin('room_types', 'room_types.id', 'rooms.room_type_id')
->with('services','reserved_rooms')
->when($request->services, function ($query) use ($request) {
$query->whereHas('services', function ($q) use ($request) {
$q->whereIn('id', $request->services);
});
})
->orderByDesc('id')
->get();

If you want to match all services, you can use for loop to create AND query.
Try below code:
->when($request->services, function ($query) use ($request) {
foreach ($request->services as $key => $service) {
$query->whereHas('services', function ($q) use ($service) {
$q->where('id', $service);
});
}
})

->when($request->services, function ($query) use ($request) {
$query->whereHas('services', function ($q) use ($request) {
$q->selectRaw('count(distinct id)')->whereIn('id', $request->services);
}, '=', count($request->services)
);
})
Changed it,not tested but this is the general idea: get the rows where the COUNT of distinct ids is equal to the length of array.The array should be unique.

Related

Why is the model loading while whereHas is false?

I have this query:
$result = PortingItem::whereHas('porting', function ($query) {
$query->whereIn('status', [
Porting::STATUS_REQUESTED,
Porting::STATUS_ACCEPTED,
Porting::STATUS_DELAYED,
]);
})->where(function ($query) use ($numbers) {
$query->whereBetween('phone_number_start', [$numbers[0], $numbers[1]])
->orWhereBetween('phone_number_end', [$numbers[0], $numbers[1]]);
})->orWhere([
['phone_number_start', '<=', $numbers[0]],
['phone_number_end', '>=', $numbers[1]],
])->get();
The PortingItem model still returns the query result while the porting relation is empty. I don't understand why this happens.
This is my Porting model relation
public function items()
{
return $this->hasMany(PortingItem::class);
}
This is my PortingItem model relation:
public function porting()
{
return $this->belongsTo(Porting::class);
}
You should always group orWhere calls in order to avoid unexpected behavior when global scopes are applied.
$result = PortingItem::whereHas('porting', function ($query) {
$query->whereIn('status', [
Porting::STATUS_REQUESTED,
Porting::STATUS_ACCEPTED,
Porting::STATUS_DELAYED,
]);
})->where(function ($query) use ($numbers) {
$query->where(function ($query) use ($numbers) {
$query->whereBetween('phone_number_start', [$numbers[0], $numbers[1]])
->orWhereBetween('phone_number_end', [$numbers[0], $numbers[1]]);
})->orWhere(function ($query) use ($numbers) {
$query->where('phone_number_start', '<=', $numbers[0])
->where('phone_number_end', '>=', $numbers[1]);
});
})->get();
https://laravel.com/docs/8.x/queries#logical-grouping

How to put where condition on nested relation in laravel?

I have table Invoice which has relation with consignment hasMany
public function consignments_invoices()
{
return $this->hasMany('App\Models\Admin\Consignment', 'invoice_id');
}
and consignment has relation hasMany with Charge
public function charges()
{
return $this->hasMany('App\Models\Admin\Charge', 'object_id')->where('model', 'Consignment');
}
Here in charge I want to put where clause on attribute rate_zone_id
How i can achieve this?
I have existing query like
Invoice
::when(!empty($request->customer_id), function ($q) use ($request) {
return $q->where('customer_id', $request->customer_id);
})
->where('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date)
->with('consignments_invoices')->
->when(!empty($request->rate_zone_id), function ($q) use ($request) {
//Here is want to use where condition on rate zone id
})
->get();
For this you can use whereHas(). whereHas() can take a nested relationships using dot notation (consignments_invoices.charges) so you would have something like:
$query->whereHas('consignments_invoices.charges', function ($query) use ($request) {
$query->where('rate_zone_id', $request->rate_zone_id);
});
Furthermore, when() passes the first parameter as the second argument to the callback so you could have something like:
->when($request->rate_zone_id, function ($q, $rateZoneId) {
$q->whereHas('consignments_invoices.charges', function ($q) use ($rateZoneId) {
$q->where('rate_zone_id', $rateZoneId);
});
})
This would mean the entire query would look something like:
Invoice
::when($request->customer_id, function ($q, $customerId) {
return $q->where('customer_id', $customerId);
})
->when($request->rate_zone_id, function ($q, $rateZoneId) {
$q->whereHas('consignments_invoices.charges', function ($q) use ($rateZoneId) {
$q->where('rate_zone_id', $rateZoneId);
});
})
->where('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date)
->get();
The above will limit the Invoices to the specific rate_zone_id (when it's supplied), however, it won't constrain the consignments_invoices.
For this, you will need to constrain the consignments_invoices relationship in the with() call as well:
->with([
'consignments_invoices' => function ($query) use ($request) {
$query->with('charges')->when($request->rate_zone_id, function ($q, $rateZoneId) {
$q->whereHas('charges', function ($q) use ($rateZoneId) {
$q->where('rate_zone_id', $rateZoneId);
});
});
},
])
This will mean your entire query will look like:
Invoice
::with([
'consignments_invoices' => function ($query) use ($request) {
$query->with('charges')->when($request->rate_zone_id, function ($q, $rateZoneId) {
$q->whereHas('charges', function ($q) use ($rateZoneId) {
$q->where('rate_zone_id', $rateZoneId);
});
});
},
])
->when($request->customer_id, function ($q, $customerId) {
return $q->where('customer_id', $customerId);
})
->when($request->rate_zone_id, function ($q, $rateZoneId) {
$q->whereHas('consignments_invoices.charges', function ($q) use ($rateZoneId) {
$q->where('rate_zone_id', $rateZoneId);
});
})
->where('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date)
->get()
You can use nested relationship
Invoice::when(!empty($request->customer_id), function ($q) use ($request) {
return $q->where('customer_id', $request->customer_id);
})->where('start_date', '>=', $request->start_date)->whereDate('end_date', '<=', $request->end_date)
->when(!empty($request->rate_zone_id), function ($q) use ($request) {
$q->whereHas('consignments_invoices.charges',function ($query) use ($request){
$query->where('rate_zone_id',$request->rate_zone_id);
});
})->get();
$invoice = Invoice::with('charges')
->when(!empty($request->customer_id), function ($q) use ($request) {
return $q->where('customer_id', $request->customer_id);
})
->whereDate('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date);
if (!empty($request->rate_zone_id)) {
$invoice->whereHas('charges', function ($query1)use($request) {
$query1->where('rate_zone_id', $request->rate_zone_id);
});
}
$invoices = $invoice->get()->toArray();
Or
$invoices = Invoice::with('charges')
->when(!empty($request->customer_id), function ($q) use ($request) {
return $q->where('customer_id', $request->customer_id);
})
->whereDate('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date)
->when(!empty($request->rate_zone_id), function ($query1) use ($request) {
$rate_zone_id = $request->rate_zone_id;
$query1->whereHas('charges', function ($query2)use($rate_zone_id) {
$query2->where('rate_zone_id', $rate_zone_id );
});
})->get()->toArray();
$invoice = Invoice::when(!empty($request->customer_id), function ($q) use ($request) {
return $q->where('customer_id', $request->customer_id);
})
->whereDate('start_date', '>=', $request->start_date)
->whereDate('end_date', '<=', $request->end_date);
if (!empty($request->rate_zone_id)) {
$rate_zone_id = $request->rate_zone_id;
$invoice->with(['charges', function ($query1)use($rate_zone_id) {
$query1->where('rate_zone_id', $rate_zone_id);
}]);
}
$invoices = $invoice->get()->toArray();
use whereHas=> https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence

Laravel remove parent if related collection

I have a model with a related collection
now im doing this query
$data = DeliveryPartner::when($filter, function ($q) use ($request) {
})
->with(['orders' => function ($query) {
$query
->where('delivery_partner_invoice_id', '=', '')
->orWhereNull('delivery_partner_invoice_id')
->whereIn('status', ['payment-accepted', 'completed', 'full-refund', 'partial-refund']);
}])->get();
Now i am wondering. If the orders returns empty is it posible to remove this parent from the collection?
I Know i can do this after the eloquent query with a loop. But is it possible to do this in the query?
we cant completely remove that parent ( with index ) BUT you can set those to null using transform() like this;
$data = DeliveryPartner::when($filter, function ($q) use ($request) {
})
->with(['orders' => function ($query) {
$query
->where('delivery_partner_invoice_id', '=', '')
->orWhereNull('delivery_partner_invoice_id')
->whereIn('status', ['payment-accepted', 'completed', 'full-refund', 'partial-refund']);
}])->get()->transform(function($item){
if(!$item->orders->count() ){
return;
}
return $item;
});
Note: this will not completely remove those parents but it will set them to empty.

Laravel search with relations

I have the following relations in my Laravel application
class Item extends Model
{
public function category()
{
return $this->belongsTo('App\Category');
}
}
and
class Category extends Model
{
public function item()
{
return $this->hasMany('App\Item');
}
}
I want to implement search functionality so I have created the following Eloquent query:
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query->where('item_name','LIKE','%'.$q.'%')
->orWhere('item_description','LIKE','%'.$q.'%');
})
->paginate(10);
This is working as expected and returns the search results for 'q' based on the name and the description of the item.
As a next step, I would like to also search for the category_name. Because of the relation, I have the category_id stored in the Items table, but I would like to use the category_name in my Eloquent query.
Anyone could provide some help?
Based on feedback received, I tried:
Suggestion 1:
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query
->where('item_name', 'LIKE' ,'%'.$q.'%')
->orWhere('item_description', 'LIKE' ,'%'.$q.'%');
})
->whereHas('category', function (Category $query) use ($q) {
$query->where('category_name', $q);
})
=> this gives following error message:
Argument 1 passed to
App\Http\Controllers\ItemController::App\Http\Controllers{closure}()
must be an instance of App\Http\Controllers\App\Category, instance of
Illuminate\Database\Eloquent\Builder given
Suggestion 2:
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query
->where('item_name', 'LIKE' ,'%'.$q.'%')
->orWhere('item_description', 'LIKE' ,'%'.$q.'%');
})
->whereHas('category', function ($query) use ($q) {
$query->where('category_name', $q);
})
=> this does not result any search result anymore (also not for item_name and item_description).
Solution
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query
->where('item_name', 'LIKE' ,'%'.$q.'%')
->orWhere('item_description', 'LIKE' ,'%'.$q.'%');
})
->orWhereHas('category', function ($query) use ($q) {
$query->where('category_name', $q);
})
->sortable(['id' => 'desc'])
->paginate(10);
As you already described relation to Category in your Item model, you have to use just whereHas method:
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query->where('item_name','LIKE','%'.$q.'%')
->orWhere('item_description','LIKE','%'.$q.'%');
})
->orWhereHas('category', function ($query) use ($q) {
$query->where('category_name', 'LIKE', "%$q%");
})
->paginate(10);
You could add a whereHas and constrain it. For example:
$items = Item::where('item_type', '=', 'type1')
->where(function($query) use ($q) {
$query->where('item_name','LIKE','%'.$q.'%')
->orWhere('item_description','LIKE','%'.$q.'%');
})
->whereHas('category', function($query) {
$query->where('category_name', 'name');
})
->paginate(10);

How to use whereHas for ignoring empty array in eager loading

I don't know how to ignore empty array values in my case. In my query I'm eager loading messages, but it gives me
messages => []
with my query when deleted_from_sender is set to 1 (but I want to ignore it for pagination)
static::with(['messages' => function ($q) use ($id) {
return $q->where(function ($q) use ($id) {
$q->where('user_id', $id)
->where('deleted_from_sender', 0);
})
->orWhere(function ($q) use ($id) {
$q->where('user_id', '!=', $id);
$q->where('deleted_from_receiver', 0);
})->latest();
}])->get();
I tried has and whereHas with the same query and whereNotNull, but no luck.

Resources