From the URL I am getting either a $filter, $month or both. When I for example want to show only results for November, everything works fine using the code below. But, when a filter has been set ALSO, then it shows all results (also from other months), if the filter has been found in the database.
What I want to achieve is, show only the results if the filter that was set has been found in the database, AND if a month has been set, only show the results from that particular month also.
My Section and Expense models have a belongsTo and hasMany relation respectively.
$query = Section::query();
If a filter has been set:
if ($filter !== 'false') {
$query->with(['expenses' => function ($query) use ($month) {
$query->where('date', 'LIKE', '%2015-' . Carbon::parse($month)->format('m') . '%');
}]);
}
If a month has been set:
if ($month !== 'false') {
$query->with(['expenses' => function ($query) use ($filter) {
$query->where('type', '=', $filter);
}]);
}
I also tried the following code:
if ($filter !== 'false') {
$query->with('expenses')->whereHas('expenses', function($query) use ($filter) {
$query->where('type', '=', $filter);
});
}
if ($month !== 'false') {
$query->with('expenses')->whereHas('expenses', function($query) use ($month) {
$query->where('date', 'LIKE', '%2015-' . Carbon::parse($month)->format('m') . '%');
});
}
$expenses = $query->orderBy('section', 'asc')->paginate(25);
I use a foreach on the $expenses in my view to display the results.
With the way you have it, you're overwriting one eager loading constraint with another. You need to move all your checks into one eager loading constraint, like so:
$query->with(['expenses' => function ($q) use ($month, $filter) {
if ($month !== 'false') {
$q->where('date', 'LIKE', '%2015-' . Carbon::parse($month)->format('m') . '%');
}
if ($filter !== 'false') {
$q->where('type', '=', $filter);
}
}]);
You need to combine both, else the last one is messing with the state set by the first eloquent model.
For a quick fix and understand whats happening try:
if ($filter !== 'false') {
$query = $query->with('expenses')->whereHas('expenses', function($query) use ($filter) {
$query->where('type', '=', $filter);
});
}
if ($month !== 'false') {
$query = $query->with('expenses')->whereHas('expenses', function($query) use ($month) {
$query->where('date', 'LIKE', '%2015-' . Carbon::parse($month)->format('m') . '%');
});
}
$expenses = $query->orderBy('section', 'asc')->paginate(25);
In the correct scenenario you should handle all AND conditions inside the inner closure.
it can be done using
$query->where(condition1);
$query->where(condition2);
or
$query->where(condition1)
->where(condition2);
Take a look to this answer and this documentation
Related
I am new to laravel. I want to execute my where condition if my variable value is not null. I tried the below code exactly not getting an idea of what to do.
$search = $array['search'];
$id = $array['id'];
$Data = Model::where('id', '=', $id)
if($search != '') {
//I want to include where condition here
}
Use Conditional Clauses
Model::when($search !== null, function ($query) use ($search) {
return $query->where('column', $search);
})
Your $Data variable is an Eloquent Query Builder object so you can add conditions to it for the query you are building:
if ($search) {
$Data->where('field', $search);
}
I'm using multiple where conditions and orWhere conditions to filter data. Also, I'm using whereBetween to set date range. As filters I'm sending from and to dates, array list of filters. But when I check with more filters I'm getting all data that not in the date range. Here is a filter data sample.
'from_date' => date("Y-m-d"),
'to_date' => date("Y-m-d"),
'filters' => ['', "pending","won","cancel"]
here is my code
$this->model
->leftJoin('vehicle_details', 'vehicle_details.id', '=', 'vehicles.vehi_details_id')
->leftJoin('makes', 'makes.model_id', '=', 'vehicle_details.model_id')
->when(($attbutes->from_date || $attbutes->to_date), function ($q) use ($attbutes) {
return $q->whereBetween('vehicles.auc_date', [$attbutes->from_date, $attbutes->to_date]);
})
->when(($attbutes->filters && isset($attbutes->filters[0])), function ($q) use ($attbutes) {
if (strtolower($attbutes->filters[0]) == "pending") {
return $q->whereNull('vehicles.vehi_details_id');
} elseif (strtolower($attbutes->filters[0]) == "1" || strtolower($attbutes->filters[0]) == "0") {
return $q->whereRaw('LOWER(`vehicles`.`won_status`)= ?', intval($attbutes->filters[0]));
} else {
return $q->whereRaw('LOWER(`vehicle_details`.`status`)= ?', strtolower($attbutes->filters[0]));
}
})
->when(($attbutes->filters), function ($q) use ($attbutes) {
foreach ($attbutes->filters as $index => $filter) {
if ($index > 0) {
if (isset($filter[$index]) && strtolower($attbutes->filters[$index]) == "pending") {
$q->orWhereNull('vehicles.vehi_details_id');
} elseif (isset($filter[$index]) && (strtolower($attbutes->filters[$index]) == "1" || strtolower($attbutes->filters[$index]) == "0")) {
$q->orWhereRaw('LOWER(`vehicles`.`won_status`)= ?', intval($attbutes->filters[$index]));
} else {
$q->orWhereRaw('LOWER(`vehicle_details`.`status`)= ?', strtolower($attbutes->filters[$index]));
}
}
}
})
->get([
'vehicles.*',
'vehicle_details.color as vehi_color',
'vehicle_details.grade as vehi_grade',
'makes.model_name',
]);
Is there anything wrong with the code?
User can submit a search form with an input such as dog cat +bird. This is expected to return all Posts with title containing (dog OR cat) AND bird.
I believe I need to append dog and cat in a subquery such as :
protected function orLike(string $column, string $value): Builder
{
return $this->builder
->where(function (Builder $subQuery) use ($column, $value) {
$subQuery->orWhere($column, 'like', '%'.$value.'%'); // dog
//$subQuery->orWhere($column, 'like', '%'.$value.'%'); // cat
//$subQuery->orWhere($column, 'like', '%'.$value.'%'); etc...
});
}
above orLike is my function in a loop that runs for each parsed optional search term (dog, cat)
How do I make it so that each optional terms (dogor cat) APPEND to the $subquery with an orWhere() for each term?
Right now, obviously, it fires a new where() with a single subquery for each term.
Not sure if I am clear enough. Basically I am trying to build a rather simple search input where users can type +bird -cat dog duck meaning bird MUST be in the title AND cat MUST NOT be in the title AND (contains dog OR duck)
edit: additional info as per request in the comments
/*
usage: return $this->parseLike('title', '+dog cat -bird elephant duck');
*/
protected function parseLike(string $column, string $value)
{
// [...] irrelevant code
/*
$terms is a collection of terms, such as:
+dog
cat
-bird
elephant
duck
*/
return $terms
->unique()
->map(function (string $term) {
switch (\substr($term, 0, 1)) {
case '+':
return ['like' => \substr($term, 1)]; // function "like()" is called for terms with operator "+" such as "+dog"
case '-':
return ['notLike' => \substr($term, 1)]; // function "notLike()" is called for terms with operator "-" such as "-bird"
default:
return ['orLike' => $term]; // function "orLike()" is called for terms with no operator, such as "elephant" or "duck" or "cat"
}
})
->each(function ($combination) use ($column) {
collect($combination)
->reject(function ($term) {
return empty($term);
})
->each(function (string $term, string $operator) use ($column) {
return $this->{$operator}($column, $term);
});
});
}
Look at this example, I believe you can integrate it to your code.
All you need is to create arrays with values you 1.need, 2.don't need, 3.may be
$mustBe = ['dog', 'cat'];
$mustNotBe = ['bird'];
$mayBe = ['tiger', 'lion'];
$model = SomeModel::query();
foreach ($mustBe as $term){
$model->where('title', 'like', '%'. $term . '%');
}
foreach ($mustNotBe as $term){
$model->where('title', 'not like', '%'. $term . '%');
}
if($mayBe){
$model->where(function ($query) use ($mayBe) {
foreach ($mayBe as $term){
$query->orWhere('title', 'like', '%'. $term . '%');
}
});
}
$result = $model->get();
dd($result);
// this builder will return something like this
$result = SomeModel::where('title', 'like', '%dog%') // dog must be
->where('title', 'like', '%cat%') // cat must be
->where('title', 'not like', '%bird%') // bird must not be
->where(function ($query) {
$query->orWhere('title', 'like', '%tiger%') // tiger may be
->orWhere('title', 'like', '%lion%'); // lion may be
})
->get();
I would like to get all the Report models where the relation ReportUpload's property of status equals 0 or where the ReportUpload relation doesn't exist. The Report and ReportUpload models have a one to one relationship, ReportUpload belongs to a Report.
Somewhat unsure how to go about this using eloquent's relationship constraints or any other method. Any help would be appreciated.
Here's my current code:
// initial query
$reports = Report::whereHas('link', function($query) {
$query->where('status', 'complete');
})->with('student', 'course', 'institution', 'reportUpload');
// apply constraint
if ($request->has('uploadStatus')) {
$uploadStatus = $request->has('uploadStatus'); // 0 or 1
if ($uploadStatus === 0) {
$reports = $reports
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
} else {
$reports = $reports->whereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
}
}
The code does not produce the desired results.
Edit
Trying this approach but not sure if it's correct:
$reports = $reports
->where(function ($query) use ($uploadStatus) {
$query
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
});
First, there are some mistakes in your initial code.
1 - You're checking if the request has an uploadStatus. Then, $uploadStatus == $request->has which will always be true.
if ($request->has('uploadStatus')) {
$uploadStatus = $request->has('uploadStatus');
So I guess you might want:
if ($request->has('uploadStatus')) {
$uploadStatus = $request->input('uploadStatus');
2 - You're comparing strictly $uploadStatus === 0 which might not work because the request might return a string '0' and not an integer, so you should either compare with == or cast $uploadStatus to (int).
After this, I think the code you added in your question works as expected:
$reports = $reports
->where(function ($query) use ($uploadStatus) {
$query
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
});
Because the where encapsulating the query will put it between parentheses.
Try to separate the queries. whereDoesntHave might be counting negatively with the orWhereHas even if it is an or statement:
$reportsNoUpload = $reports
->whereDoesntHave('reportUpload')->get();
$reportsIncomplete = $reports
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
})->get();
$reports = $reportsNoUpload->merge($reportsIncomplete);
I can do this in Code Igniter:
$this->db->select();
$this->from->('node');
if ($published == true)
{
$this->db->where('published', 'true');
}
if (isset($year))
{
$this->db->where('year >', $year);
}
$this->db->get();
How can this code be translated so that it works in Laravel?
In Fluent you can do:
$query = DB::table('node');
if ($published == true)
$query->where('published', '=', 1);
if (isset($year))
$query->where('year', '>', $year);
$result = $query->get();
As of Laravel 5.2.27, you can avoid breaking the chain by writing your conditions as so:
$query = DB::table('node')
->when($published, function ($q) use ($published) {
return $q->where('published', 1);
})
->when($year, function($q) use ($year) {
return $q->where('year', '>', $year);
})
->get();
To use Eloquent,just swap $query = DB::table('node') with Node:: but realize if both conditions fail, you'll get everything in the table back unless you check for some other condition before querying the db/model or from within the query itself.
Note the that $published and $year must be in local scope to be used by the closure.
You can make it more concise and readable by creating a macro. See: Conditionally adding instructions to Laravel's query builder
Here is how you can accomplish your query:
$year = 2012;
$published = true;
DB::table('node')
->where(function($query) use ($published, $year)
{
if ($published) {
$query->where('published', 'true');
}
if (!empty($year) && is_numeric($year)) {
$query->where('year', '>', $year);
}
})
->get( array('column1','column2') );
To find more information, I recommend reading through Fluent and Eloquent in the Laravel docs.
http://laravel.com/docs/database/fluent
I have not seen it here. You can even start your query like
$modelQuery = Model::query();
and then chain other query command afterwards. Maybe it will be helpful for someone new.
You can use Model::when() in Condition or you can create Builder::micro()
For Example
$results = Model::where('user_id', Auth::id())
->when($request->customer_id, function($query) use ($request){
return $query->where('customer_id', $request->customer_id);
})
->get();
If You need to create micro for a condition then. follow below instruction.
Write thic code in your serverice provider
Builder::macro('if', function ($condition, $column, $operator, $value) {
if ($condition) {
return $this->where($column, $operator, $value);
}
return $this;
});
Use Like Below Example
$results = Model::where('user_id', Auth::id())
->if($request->customer_id, 'customer_id', '=', $request->customer_id)
->get();
Ref: themsaid
If you need to use Eloquent you can use it like, I'm not sure that whereNotNull is the best use but I couldn't find another method to return what we really want to be an empty query instance:
$query = Model::whereNotNull('someColumn');
if(x < y)
{
$query->where('column1', 'LIKE', '%'. $a .'%');
}else{
$query->where('column2', 'LIKE', '%'. $b .'%');
}
$results = $query->get();
This way any relationships still work, for example in your view you can still use
foreach($results as $result){
echo $result->someRelationship()->someValue;
}
There is a good amount of info on here http://daylerees.com/codebright/eloquent-queries about this sort of stuff.
In Laravel > 5.2 you can use when():
$results = DB::table('orders')
->where('branch_id', Auth::user()->branch_id)
->when($request->customer_id, function($query) use ($request){
return $query->where('customer_id', $request->customer_id);
})
->get();
Docs: https://laravel.com/api/5.8/Illuminate/Contracts/Container/Container.html#method_when
Blog post: https://themsaid.com/laravel-query-conditions-20160425/
for eloquent query i used following that executes only if where condition has value
->where(function($query) use ($value_id)
{
if ( ! is_null($value_id))
$query->where('vehicle_details.transport_type_id', $value_id);
})
We can write like this (More precise way):
$query = DB::table('node')->when($published, function ($q, $published) {
return $q->where('published', 1);
})->when($year, function($q, $year) {
return $q->where('year', '>', $year);
})->get()
Not mentioned in Laravel docs. Here is pull request.