I'd like to use some custom logic inside a where() condition (Laravel 5.8), e.g. the below. The where() parameter will change depending on a variable. The code below doesn't work, but gives you an idea of what I'm trying to achieve. How can I get the desired result?
\App\Model::where(function ($query) use ($quantity, $price_criteria) {
if ($model->threshold_1 <= $quantity) {
$compare = $model->price_1
} elseif ($model->threshold_2 <= $quantity) {
$compare = $model->price_2
} else {
$compare = $model->price_3
}
$query->where($compare, "<=", $price_criteria)
}->orWhere...
If I understand your question correctly, you can use a whereRaw() query to build your conditions, or build separate query conditions with where()/whereOr().
Using whereRaw(),
\App\Model::whereRaw("price_1 <= ? AND threshold_1 <= ?", [$price_criteria, $quantity])
->orWhereRaw("price_2 <= ? AND threshold_2 <= ?", [$price_criteria, $quantity])
->orWhereRaw("price_3 <= ?", [$price_criteria]);
Or using Eloquent,
\App\Model::where(function($query) use ($quantity, $price_criteria) {
$query->where("price_1", "<=", $price_criteria)
->where("threshold_1", "<=", $quantity);
})->orWhere(function($query) use ($quantity, $price_criteria) {
$query->where("price_2", "<=", $price_criteria)
->where("threshold_2", "<=", $quantity);
})->orWhere(function($query) use ($quantity, $price_criteria) {
$query->where("price_3", "<=", $price_criteria)
});
Simply, you can't. All you do here is building a SQL query that returns data and you can't access the data before fetching it. In order to actually filter on data this way you will need to retrieve all data first and filter the collection.
You should pass $model as parameter like $quantity and $price_criteria (and add some punctuation :)):
\App\Model::where(function ($query) use ($quantity, $price_criteria, $model) {
if ($model->threshold_1 <= $quantity) {
$compare = $model->price_1;
} elseif ($model->threshold_2 <= $quantity) {
$compare = $model->price_2;
} else {
$compare = $model->price_3;
}
$query->where($compare, "<=", $price_criteria);
})->orWhere();
Hope that helps.
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'd like to ask whether is possible to use where method on multiple collections at once.. better to say, is possible to symplify this code somehow, please? Thank you so much.
$posts_per_weeks = array();
for($i = 10; $i > 0; $i--) {
$my_posts = $user->posts()
->where('created_at', '>', Carbon::now()->subWeek($i))
->where('created_at', '<=', Carbon::now()->subWeek($i-1))
->count();
$all_posts = Post::all()
->where('created_at', '>', Carbon::now()->subWeek($i))
->where('created_at', '<=', Carbon::now()->subWeek($i-1))
->count();
array_push($posts_per_weeks, [$my_posts, $all_posts - $my_posts]);
}
Method posts() looks like this:
public function posts()
{
if($this->hasAnyRole('broker|super-agent')) {
$posts = $this->teams()->get()->map(function ($team) {
return $team->posts->all();
});
if($this->hasRole('broker')) {
$posts->push($this->hasMany('App\Post', 'broker_id')->get());
}
return $posts->collapse()->unique();
} elseif($this->hasRole('admin')) {
return Post::all();
} else {
return $this->hasMany('App\Post', 'agent_id')->get();
}
}
You can use groupByto group the results
$maxWeeksBack = 10;
$myPostCount = $user->posts()->where('created_at', '>', Carbon::now()->subWeek($maxWeeksBack))
->select('id', DB::raw('count(*) as total'))
->groupBy(DB::raw('WEEK(created_at)'))
->get();
$postCount = Post::where('created_at', '>', Carbon::now()->subWeek($maxWeeksBack))
->select('id', DB::raw('count(*) as total'))
->groupBy(DB::raw('WEEK(created_at)'))
->get();
This returns the count of posts and myposts as an array, sorted by week. You would need to merge the two arrays to have your array, but this is a much cleaner approach to get the count of the posts.
DISCLAMER: Haven't tested it live, but it should work
You could use QueryScopes.
Post.php
class Post
{
public function scopeFromWeek($query, $week) {
return $builder->where('created_at', '>', Carbon::now()->subWeek($week))
->where('created_at', '<=', Carbon::now()->subWeek($week-1));
}
}
In your controller (or wherever you use this code)
$posts_per_weeks = [];
for($i = 10; $i > 0; $i--) {
$my_posts = $user->posts()->fromWeek($i)->count();
$all_posts = Post::fromWeek($i)->count();
array_push($posts_per_weeks, [$my_posts, $all_posts - $my_posts]);
}
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 would like to construct a series of eloquent WHERE clauses dependent on the search parameters I collect from a json object.
Something like this (never mind the syntax of object,,, it is an interpretation only to demonstrate):
$searchmap = "
{
"color": "red",
"height": "1",
"width": "2",
"weight": "",
"size": "",
}";
I then take the object and decode to get a search array...
$search = json_decode($searchmap, true);
If my weight and size are set to null or are an 'empty string' I would have eloquent code that looks like this..
$gadgets = Gadget::where('color', '=', $search['color'])
->where('height', '=', $search['height'])
->where('width', '=', $search['width'])
->paginate(9);
If they have a value then eloquent code would look like this..
$gadgets = Gadget::where('color', '=', $search['color'])
->where('height', '=', $search['height'])
->where('width', '=', $search['width'])
->where('weight', '=', $search['weight'])
->where('size', '=', $search['size'])
->paginate(9);
Is there a way to accomplish this dynamically.
I suppose the question should be ins there a way to chain eloquent where clauses dynamically based on a given parameter?
In a pseudo context I am looking to do something like this
$gadgets = Gadget::
foreach ($search as $key => $parameter) {
if ( $parameter <> '' ) {
->where($key, '=', $parameter)
}
}
->paginate(9);
Can chaining of where clauses be created in some way similar to this?
Thank you for taking the time to look at this!
UPDATE:
I also came up with something like this that seems to work well but i would like to welcome suggestions if improvement is a good idea.
$gadgets = New Gadget();
foreach ($search as $key => $parameter) {
if($parameter != ''){
$gadgets = $gadgets->where($key, '=', $parameter);
}
}
$gadgets = $gadgets->paginate(9);
FINAL
And thanks to #lukasgeiter below I think I will go with this
$gadgets = Gadget::whereNested(function($query) use ($search) {
foreach ($search as $key => $value)
{
if($value != ''){
$query->where($key, '=', $value);
}
}
}, 'and');
$gadgets = $gadgets->paginate(9);
That's easy. Laravel's where function allows you to pass in an array of key value pairs.
$searchmap = array(
'color' => 'red',
'height' => '1'
// etc
);
$gadgets = Gadget::where($searchmap)->paginate(9);
If you are curious, that's the relevant part of the source (\Illuminate\Database\Query\Builder)
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
// If the column is an array, we will assume it is an array of key-value pairs
// and can add them each as a where clause. We will maintain the boolean we
// received when the method was called and pass it into the nested where.
if (is_array($column))
{
return $this->whereNested(function($query) use ($column)
{
foreach ($column as $key => $value)
{
$query->where($key, '=', $value);
}
}, $boolean);
}
// many more lines of code....
}
Edit
To have more control over it (e.g. changing the "=" to another comparison operator) try using the code laravel uses internally directly:
$gadgets = Gadget::whereNested(function($query) use ($searchmap)
{
foreach ($searchmap as $key => $value)
{
if($value != ''){
$query->where($key, '=', $value);
}
}
}, 'and')->paginate(9);
For anyone who needs it, here's a modified version of lukasgeiter's answer that solves the 'variable number of wheres' problem while also allowing (1) different operators for each where clause and (2) the capacity to also use whereIn for when one of your "wheres" must be able to match one of multiple values (the function below detects when an array of values is passed and, thus, uses whereIn instead of where).
The $paramSets variable assignment at the beginning (below) essentially describes how to use it.
$paramSets = [
"size"=>["op"=>"=","values"=>"large"],
"production_date"=>["op"=>">=","values"=>"2015-12-31"],
"color"=>["op"=>"=","values"=>["red","blue"]],
"name"=>["op"=>"like","values"=>"M%"]
];
$items = db::table('products')
->whereNested(function($query) use ($paramSets) {
foreach ($paramSets as $k => $v)
{
if ($v != ''){
if (is_array($v["values"])){
$query->whereIn($k,$v["values"]);
}
else {
$query->where($k, $v["op"], $v["values"]);
}
}
}
}, 'and');
dd($items->get());
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.