Use Where in QueryBuilder - Laravel - laravel

I have a table Products(id,category_id,name);
I want to get a result like this query:
SELECT * FROM Products WHERE category_id=$category_id OR $category_id=0;
When I assign $category_id with 0 value => above query will return all records.
How do I write that query in Laravel?
I've try this:
Products::where(function($query) use ($category_id){
$query->where("category_id",$category_id)
->orWhere($category_id,0)
})->get();
But, Error:
It look like:
SELECT
*
FROM
`products`
WHERE
( `product_category_id` = 0 OR `0` = 0 )
And Print Error: Column not found: 1054 Unknown column '0' in 'where clause'
How to fix: '0' = 0 to 0 = 0?
Thanks!

Use when():
$products = Product::when($categoryId, function ($query, $categoryId) {
$query->whereCategoryId($categoryId);
})->get();
This method will call the function only if the condition is truthy. You could achieve the same thing with simple if statement:
$products = Product::query();
if ($categoryId) {
$products->whereCategoryId($categoryId);
}
$products = $products->get();
What you gain by using when() is that you don't break the chain, so you can use conditional query changes in one expression. It's useful if you want to just return it, for example.

use it
Products::where(function($query) use ($category_id){
$query->where("category_id",$category_id)
->orWhere("category_id",0);
})->get();

what's wrong with your code is this:
->orWhere($category_id,0)
you made the column part as variable.
try this code:
Products::where(function($query) use ($category_id){
return $query->where("category_id", $category_id)
->orWhere("category_id", 0);
})->get();

$product = new Products;
if ($category_id != 0) {
$product = $product->where("category_id", $category_id);
}
$products = $product->get();

Related

Laravel Call to a member function paginate() on array if join 3 queries from database

Found a solution how to connect 3 queries from the database. But I can't try pagination on information
My code:
$ads_default = AdModel::where('status','=', 1)->where('category_id','=',$this_category_id)->where('moderation','=', 0)->where('ad_diamond','=', 0)->where('ad_vip','=', 0)->orderBY('ad_pos', 'asc')->get();
$ads_vip = AdModel::where('status','=', 1)->where('category_id','=',$this_category_id)->where('moderation','=', 0)->where('ad_vip','=', 1)->orderBY('ad_vip_pos', 'asc')->get();
$ads_diamond = AdModel::where('status','=', 1)->where('category_id','=',$this_category_id)->where('moderation','=', 0)->where('ad_diamond','=', 1)->orderBY('ad_diamond_pos', 'asc')->get();
$merge_d_v = $ads_diamond->merge($ads_vip);
$merge_d_v_r = $merge_d_v->merge($ads_default);
$data['all_union_ads'] = $merge_d_v_r->all()->paginate(20);
You can't call paginate on collections, after you call get() on a query, it will resolve the query. This can simply be some orWhere() logic.
Note the logical grouping with the closures, to avoid it being category_id = 1 AND ad_vip = 1, where instead it will be similar to category_id = 1 AND (one_condition OR another condition).
AdModel::where('status', 1)
->where('category_id', $this_category_id)
->where('moderation','=', 0)
->where(function ($query) {
$query->orWhere(function ($query) {
$query->where('ad_diamond','=', 0)
->where('ad_vip','=', 0)->orderBY('ad_pos', 'asc');
})->orWhere('ad_vip','=', 1)
->orWhere('ad_diamond','=', 1);
})->paginate();

Laravel Repositories whereHas -- multiple

I have the following repository Products and each product can have many Categories and many Bidders
What I am trying to achieve is the following (Without Repository)
$products = Products::whereHas('categories', function ($category) {
})->whereHas('bidders', function ($bidder) {
})->get();
This works fine, however, I am trying to make it so that repositories are in place and you can still do the whereHas query, so in my repository I created a method:
public function whereHas($attribute, \Closure $closure = null)
{
return $this->model->whereHas($attribute, $closure);
}
This works well, but only if I am using one of them in my main query, whereas if I use multiple:
$products = $this->products->whereHas('categories', function ($category) {
$category->where('id', '=', 1);
})->whereHas('bidders', function($bidders) {
})->get();
I am getting the following error:
Unknown column 'has_relation'
Column not found: 1054 Unknown column 'has_relation' in 'where
clause' (SQL: select * from products where exists (select * from
categories inner join products_categories on categories.id =
products_categories.categories_id where products.id =
products_categories.products_id and id = 1) and (has_relation
= sections))
The issue I'm seeing is that its returning a collection of items on the first whereHas which means it cannot compute the second one. Any ideas to where I am going wrong?
You can pass a closure with multiple relations to whereHas
$products = $this->products->whereHas(['categories', function
($category) {
$category->where('id', '=', 1);
},'bidders' => function($bidders) {
}])->get();
It will work as expected.

Laravel database query – select inside foreach

I've got a problem with a query in Laravel 5.3. I've got tables
attendees->attendee_tag<-tags (M:N relation)
and I would like to get attendees ordered by multiple tags, so I wrote this:
$query = Attendee::where('attendees.event_id', '=', $event_id);
$i = 1;
foreach($order_by as $column) {
$tag = $this->tagService->findTagById($column['tag_id']);
$sort_value = $tag->value_type == 'string' ? 'value_string_'.$i : 'value_int_'.$i;
$query->join('attendee_tag as at_'.$i, function ($join) use ($tag, $i) {
$join->on('attendees.id', '=', 'at_'.$i.'.attendee_id')
->where('at_'.$i.'.tag_id', '=', $tag->id);})
->select('at_'.$i.'.id as relationId_'.$i, 'at_'.$i.'.value_string as value_string_'.$i, 'at_'.$i.'.value_int as value_int_'.$i, 'attendees.id as id')
->orderBy($sort_value, $column['order']);
$i++;
}
$attendees = $query->get();
But I'm getting following sql error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'value_string_1' in 'order clause' (SQL: select `at_2`.`id` as `relationId_2`, `at_2`.`value_string` as `value_string_2`, `at_2`.`value_int` as `value_int_2`, `attendees`.`id` as `id` from `attendees` inner join `attendee_tag` as `at_1` on `attendees`.`id` = `at_1`.`attendee_id` and `at_1`.`tag_id` = 3 inner join `attendee_tag` as `at_2` on `attendees`.`id` = `at_2`.`attendee_id` and `at_2`.`tag_id` = 4 where `attendees`.`event_id` = 1 order by `value_string_1` asc, `value_string_2` asc limit 1000 offset 0)
It seems, that laravel has some optimalization and passes the select on $join function only once, in last iteration (second iteration in my opinion, I got 2 tags in request)
I'm assuming that you have tags() relation within Attendee model!
I tested it and it's working fine
$attendees = Attendee::join('attendee_tag', 'attendee_tag.attendee_id', '=', 'attendees.id')
->join('tags', 'attendee_tag.tag_id', '=', 'tags.id')->where('event_id', $event_id);
$i = 1;
foreach ($order_by as $column) {
$tag = $this->tagService->findTagById($column['tag_id']);
$sort_value = $tag->value_type == 'string' ? 'value_string_' . $i : 'value_int_' . $i;
$attendees->orderBy('tags.' . $sort_value, $column['order']);
$i++;
}
$attendees->get();
PS: I don't know that $order_by is all about but I took the value of it from your code above, it should work just fine :) and I'm ready to tweak it for you if you want

Eloquent / Laravel - Putting a WHERE Clause on a Reference Table With Chained Relationships

I have the following relationship functions in my Job model:
public function resourceTypes(){
return $this->belongsToMany('ResourceType', 'job_requests');
}
public function resources(){
return $this->belongsToMany('Resource', 'jobs_resources')->withPivot('flow_type', 'resource_type_id');
}
I am able to get an object with data from both of the above relationships using:
$job = Job::findorfail($projectId);
$result = $job->with('resources.resourceTypes')->get();
I would like to put a where clause on the jobs_resources pivot table - specifically on the column flow_type.
How would I do this?
Try something like this:
$job = Job::with('resources' => function($q) {
$q->with('resourceTypes')->where('flow_type',2);
})->findorfail($projectId);
In above you will get only those resources with flow_type = 2
I ended up using the following statement:
Job::with(['resources' => function ($query){
$query->wherePivot('flow_type', '=', '1' );
}, 'resources.resourceTypes'])->where('id', $projectId)->firstOrFail();
$result = DB::table('job')
->join('job_resources', 'job.id', '=', 'job_resources.job_id')
->join('job_requests', 'job_resources.request_id', '=', 'job_requests.id')
->where('job_resources.flow_type', '=', CONDITION)
->get();
Your table data is not clear from your input, but this method (query builder) should work

Laravel how do I get the row number of an object using Eloquent?

I'd like to know the position of a user based on its creation date. How do I do that using Eloquent?
I'd like to be able to do something like this:
User::getRowNumber($user_obj);
I suppose you want MySQL solution, so you can do this:
DB::statement(DB::raw('set #row:=0'));
User::selectRaw('*, #row:=#row+1 as row')->get();
// returns all users with ordinal 'row'
So you could implement something like this:
public function scopeWithRowNumber($query, $column = 'created_at', $order = 'asc')
{
DB::statement(DB::raw('set #row=0'));
$sub = static::selectRaw('*, #row:=#row+1 as row')
->orderBy($column, $order)->toSql();
$query->remember(1)->from(DB::raw("({$sub}) as sub"));
}
public function getRowNumber($column = 'created_at', $order = 'asc')
{
$order = ($order == 'asc') ? 'asc' : 'desc';
$key = "userRow.{$this->id}.{$column}.{$order}";
if (Cache::get($key)) return Cache::get($key);
$row = $this->withRowNumber($column, $order)
->where($column, '<=',$this->$column)
->whereId($this->id)->pluck('row');
Cache::put($key, $row);
return $row;
}
This needs to select all the rows from the table till the one you are looking for is found, then selects only that particular row number.
It will let you do this:
$user = User::find(15);
$user->getRowNumber(); // as default ordered by created_at ascending
$user->getRowNumber('username'); // check order for another column
$user->getRowNumber('updated_at', 'desc'); // different combination of column and order
// and utilizing the scope:
User::withRowNumber()->take(20)->get(); // returns collection with additional property 'row' for each user
As this scope requires raw statement setting #row to 0 everytime, we use caching for 1 minute to avoid unnecessary queries.
$query = \DB::table(\DB::raw('Products, (SELECT #row := 0) r'));
$query = $query->select(
\DB::raw('#row := #row + 1 AS SrNo'),
'ProductID',
'ProductName',
'Description',
\DB::raw('IFNULL(ProductImage,"") AS ProductImage')
);
// where clauses
if(...){
$query = $query->where('ProductID', ...));
}
// orderby clauses
// ...
// $query = $query->orderBy('..','DESC');
// count clause
$TotalRecordCount = $query->count();
$results = $query
->take(...)
->skip(...)
->get();
I believe you could use Raw Expresssions to achieve this:
$users = DB::table('users')
->select(DB::raw('ROW_NUMBER() OVER(ORDER BY ID DESC) AS Row, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
However, looking trough the source code looks like you could achieve the same when using SQLServer and offset. The sources indicates that if you something like the following:
$users = DB::table('users')->skip(10)->take(5)->get();
The generated SQL query will include the row_number over statement.
[For Postgres]
In your model
public function scopeWithRowNumber($query, $column = 'id', $order = 'asc'){
$sub = static::selectRaw('*, row_number() OVER () as row_number')
->orderBy($column, $order)
->toSql();
$query->from(DB::raw("({$sub}) as sub"));
}
In your controller
$user = User::withRowNumber()->get();

Resources