How to use `whereDoesntHave` in laravel - laravel

I am trying to fetch users who don't have productions by checking the start date and end date ranges.
$production = Production::find($id);
$users = User::whereDoesntHave('productions', function ($query) {
return $query
->where(function ($q) {
return $q->whereBetween('start_at', [$production->start_at, $production->end_at])
->whereBetween('end_at', [$production->start_at, $production->end_at]);
});
})->get();
//User Production relation
public function productions()
{
return $this->belongsToMany(Production::class)
->withPivot(['created_at', 'updated_at']);
}
This returns users who are already assigned in other productions within current production start date and end date that and I only want to fetch users who has no production assignment within the current date range.

If I understand correctly, you have a Production model instance in the $production variable. You want all users that doesn't have production that overlap with the time range of the $production instance ?
Your current code (pasted from your question):
$production = Production::find($id);
$users = User::whereDoesntHave('productions', function ($query) {
return $query
->where(function ($q) {
return $q->whereBetween('start_at', [$production->start_at, $production->end_at])
->whereBetween('end_at', [$production->start_at, $production->end_at]);
});
})->get();
What you should do :
$production = Production::find($id);
$users = User::whereDoesntHave('productions', function ($query) use($production) {
return $query->whereBetween('start_at', [$production->start_at, $production->end_at])
->orWhereBetween('end_at', [$production->start_at, $production->end_at]);
});
})->get();
I think the key thing here is include both start and end range in the result vith a orWhereBetween condition instead of whereBetween. And don't forget to use the $production variable in the closure ;-)
If this is not what you are trying to do, please comment to explain more precisely what you want to do

You're passing the dates as parameters but you never actually use them,
this is how it should be:
$users = User::whereDoesntHave('productions', function ($query) use ($startDate, $endDate) {
return $query->where(function ($q) use ($startDate, $endDate) {
return $q->whereBetween('start_at', [$startDate,$endDate])
->orWhereBetween('end_at', [$startDate, $endDate]);
});
})
->get();

Related

how to use whereHas in laravel

I am new to laravel,
I need to create a query for the db,
$query = Deal::query();
I want to use the wherehas operator.
this is my code that is worked.
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus);
// \Log::info('The request precedence: '.$precedenceStatus);
}
I want to add this code also to the query
if($person) {
$query->whereHas('personnel', function ($subQuery) use ($person) {
$subQuery->where('id', '=', $person);
});
}
So I need to change the first code?
how I can convert the first code to wherehas?
the first code is from table called deal, the second section is from realtionship called personnel.
the second section worked in other places in the code, I just need to fix the first section and not understand what to write in the use
I try this and get error on the last }
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus)
-> when ($person, function($query) use($person) {
$query->whereHas('personnel', function ($query) use ($person) {
$query->where('id', '=', $person);
});
})
}
There is a method you can use called when(, so that you can have cleaner code. The first parameter if true will execute your conditional statement.
https://laravel.com/docs/9.x/queries#conditional-clauses
$result = $query
->where('precedence', '=', $precedenceStatus)
->when($person, function ($query) use ($person) {
$query->whereHas('personnel', fn ($q) => $q->where('id', '=', $person));
})
->get();
You should also be able to clean up your precedence code prior to that using when( to make the entire thing a bit cleaner.
Querying to DB is so easy in laravel you just need to what you want what query you want execute after that you just have to replace it with laravel helpers.Or you can write the raw query if you cant understand which function to use.
using,DB::raw('write your sql query').
Now Most of the time whereHad is used to filter the data of the particular model.
Prefer this link,[Laravel official doc for queries][1] like if you have 1 to m relation ship so u can retrive many object from one part or one part from many object.like i want to filter many comments done by a user,then i will right like this.
$comments = Comment::whereHas('user', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
$comments = Here will be the model which you want to retrive::whereHas('relationship name', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
you can also write whereHas inside whereHas.
[1]: https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence

Laravel query with relationship where have all ids

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.

Remove duplication of code in Laravel- optimize

I want to how to optimize my code below without affecting its output in laravel?
$user = User::with('voucherUtilizations.voucher')
->with('voucherUtilizations', function ($query) use($campaignId ,$date_from,$date_to) {
if ($campaignId && $date_from &&$date_to) {
return $query->where('campaign_id', $campaignId)
->whereDate('created_at', '>=', $date_from)
->whereDate('created_at', '<=', $date_to)
->whereNotNull('voucher_id');
}
if ($campaignId) {
return $query->where('campaign_id', $campaignId)
->whereNotNull('voucher_id',);
}
if ($date_from &&$date_to) {
return $query->whereDate('created_at', '>=', $date_from)
->whereDate('created_at', '<=', $date_to)
->whereNotNull('voucher_id');
}
})
->withCount(['voucherUtilizations as no_of_prizes_won' => function ($query) use($campaignId ,$date_from,$date_to){
//the same code above function
}])
->where('id', $id->id)
->first()
some functions are repeated all common how can I minimize this code
$user = User::where('id', $id)
->with('voucherUtilizations.voucher')
->with(['voucherUtilizations', function ($query) use($campaignId, $date_from, $date_to) {
$query->when(!empty($campaignId), function ($q1) use ($campaignId) {
return $q1->where('campaign_id', $campaignId);
});
$query->when(!empty($date_from), function ($q2) use ($date_from) {
return $q2->whereDate('created_at', '>=', $date_from);
});
$query->when(!empty($date_to), function ($q3) use ($date_to) {
return $q3->whereDate('created_at', '<=', $date_to);
});
}])
->withCount('voucherUtilizations')
->whereNotNull('voucher_id')
->first();
use when() or also put if condition ,and value exist in if condition then return values,so not added 2 times same condition.
Hope fully it will help to you easily.

I am getting data from my database using Datatable, but it takes too much time to load

I am getting data from products table, i have use paginate in datatable which shows only 50 records first time, and when pager update to 200 rows it takes 12 to 13sec. Here is my Code. Thanks in Advance
$query = Product::query();
$query->with('def_or_last_supplier','units','prouctImages','productType',
'productBrand','productSubCategory',
'supplier_products','productCategory')
->where('status',1)->orderBy('refrence_no', 'DESC');
if($request->default_supplier != '')
{
$supplier_query = $request->default_supplier;
$query = $query->whereIn('id', SupplierProducts::select('product_id')->where('supplier_id',$supplier_query)->pluck('product_id'));
}
if($request->prod_type != '')
{
$query->where('type_id', $request->prod_type)->where('status',1)->orderBy('refrence_no', 'DESC');
}
if($request->prod_category != '')
{
$query->where('category_id', $request->prod_category)->where('status',1)->orderBy('refrence_no', 'DESC');
}
if($request->filter != '')
{
if($request->filter == 'stock')
{
$query = $query->whereIn('id',WarehouseProduct::select('product_id')->where('current_quantity','>',0.005)->pluck('product_id'));
}
elseif($request->filter == 'reorder')
{
$query->where('min_stock','>',0);
}
}
Your query isn't that big but I think maybe result data size is a bit big
so what you can do is:
1: in my experience is to try to select your relations fields too you can do that like so
Source
Product::with('def_or_last_supplier',
'units:id,created_at,updated_at', // Just add :field_1,field_2,another_field
'prouctImages:id,field_1,field_2'
....
)
2: I suggest you to take advantage of lazy loading. if it's possible don't eager load all your relations at once load them where it's needed
Source
$products = Product::get()
In blade or somewhere in application
under a special condition I need to access to my relation, I can easily do that by
$products->load('my_relation_name')
3: I see a lot of orderyBy clauses in your code, what you can do is to index the fields that are searchable or needs to be ordered. it can be done by adding ->index() in your migration file
Source
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('refrence_no')->index();
$table->timestamps();
});
}
4: and finally you can always try to cache your queries to prevent unnecessary queries to database, this is how cache works
source
$products = Cache::remember('cache_name', $how_long_in_seconds, function () {
return Product::get();
});
And finally here is the eloquent way of your query
$query = \Cache::remember(json_encode($request->toArray()), 300, function () use ($request) {
return Product::with('def_or_last_supplier',
'units',
'prouctImages',
'productType',
'productBrand',
'productSubCategory',
'supplier_products',
'productCategory'
)
->where('status', 1)
->orderByDesc('refrence_no')
->when($request->filled('default_supplier'), function (Builder $query) use ($request) {
$productIds = SupplierProducts::select('product_id')->where('supplier_id', $request->input('default_supplier'))->pluck('product_id');
$query->whereIn('id', $productIds);
})->when($request->filled('prod_type'), function (Builder $query) use ($request) {
$query->where('type_id', $request->input('prod_type'))->where('status', 1)->orderByDesc('refrence_no');
})->when($request->filled('prod_category'), function (Builder $query) use ($request) {
$query->where('category_id', $request->input('prod_category'))->where('status', 1)->orderByDesc('refrence_no');
})->when($request->filled('filter'), function (Builder $query) use ($request) {
$query->when('stock' === $request->input('filter'), function (Builder $query) {
$query->whereIn('id', WarehouseProduct::select('product_id')->where('current_quantity', '>', 0.005)->pluck('product_id'));
})->when('reorder' === $request->input('filter'), function (Builder $query) {
$query->where('min_stock', '>', 0);
});
})->get();
});

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.

Resources