Laravel search with relations - laravel

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);

Related

How to filter by relation column Laravel

How to filter by relation column
tried this
->whereHas('customer',function ($query) use ($order){
$query->orderBy('first_name', $order);
})
and this
->with(['customer' => function ($query) use ($order) {
$query->orderBy('first_name', $order);
}])
Both did not work
with() are using eager loading, which turns this into two queries.
You need to use join() instead of with()
$orders = Order
::join('customers', 'order.customer_id', '=', 'customers.id')
->orderBy('customers.first_name')
->get();
OR
You may use sortBy() or sortByDesc() methods of Collection.
For example:
$orders = Order
::with('customer')
->get()
->sortBy('customer.first_name');
request {{host}}/admin/showcases?filterBy=companies:name it order by relation column companies
if simple {{host}}/admin/showcases?filterBy=name
it will be only orderBy("name")
->when(str_contains($filterBy, ':'),
function (Builder $query) use ($filterBy, $order, $columns) {
$table = explode(':', $filterBy)[0];
$key = Str::singular($table) . "_id";
$column = explode(':', $filterBy)[1];
$selfTable = $this->getTable();
$query->leftJoin($table, "$selfTable.$key", "$table.id")
->when($columns !== ['*'],
function ($query) use ($columns, $selfTable) {
$columns = array_map(function ($column) use ($selfTable) {
return "$selfTable.$column";
}, $columns);
$query->select($columns);
},
fn($query) => $query->select(["$selfTable.*"]))
->orderBy("$table.$column", $order);
},
function ($query) use ($filterBy, $order) {
$query->orderBy($filterBy, $order);
})

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

Search result with multiple tables relation in Laravel6

I'm trying to make search function with relation.
I could display all result using relation And an another code, simple one table search and get result works fine.
but I can't combine those two.
Could you teach me how to combine multiple tables into search function please?
public function order(Request $request)
{
$data = $request->all();
$products = Product::All();
$products = Product::with('categori')
->join('creators', 'creators.id', '=', 'products.creator_id')
->join('categoris', 'categoris.id', '=', 'products.categori_id')
->join('branches', 'branches.id', '=', 'products.br_id')
->join('users', 'users.id', '=', 'products.user_id')
->join('colors', 'colors.id', '=', 'products.color_id')
->get();
$products = Product::when($data['categori_id'], function ($query, $categori_id) {
return $query->where('categori_id', $categori_id);
})->
when($data['color_id'], function ($query, $color_id) {
return $query->where('color_id', $color_id);
})->get();
//return view('result_fb', compact('images'));
$data = array(
'title' => 'index',
'no' => 1,
'products' => $products,
);
return view('product.result', $data);
}
I am not sure if you need 'categori' relation to be loaded or not as there are few $products without it. For general, you can combine all your $products queries to one as follows:
$products = Product::join('creators', 'creators.id', '=', 'products.creator_id')
->join('categoris', 'categoris.id', '=', 'products.categori_id')
->join('branches', 'branches.id', '=', 'products.br_id')
->join('users', 'users.id', '=', 'products.user_id')
->join('colors', 'colors.id', '=', 'products.color_id')
->when($data['categori_id'], function ($query, $categori_id) use ($data) {
return $query->where('categori_id', $data['categori_id']);
})
->when($data['color_id'], function ($query) use ($data) {
return $query->where('color_id', $data['color_id']);
})->get();
If you want to use eager loading with eloquent rather then join, you can use like
$products = Product::with(['creator','categori','branch','user','color'])
->when($data['categori_id'], function ($query, $categori_id) use ($data) {
return $query->where('categori_id', $data['categori_id']);
})
->when($data['color_id'], function ($query) use ($data) {
return $query->where('color_id', $data['color_id']);
})->get();
But for that you Product model should have relations with same name as follows for categori relation:
class Product extends Model
{
public function categori()
{
return $this->belongsTo(Categori::class,'categori_id','id);
}
}
Same way you have to define for all relation used for product.
Try this:
$products = Product::with('categori')
->join('creators', 'creators.id', '=', 'products.creator_id')
->join('categoris', 'categoris.id', '=', 'products.categori_id')
->join('branches', 'branches.id', '=', 'products.br_id')
->join('users', 'users.id', '=', 'products.user_id')
->join('colors', 'colors.id', '=', 'products.color_id')
->when($data['categori_id'], function ($query, $categori_id) {
return $query->where('categori_id', $categori_id);
})
->when($data['color_id'], function ($query, $color_id) {
return $query->where('color_id', $color_id);
})
->get();

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.

Laravel eloquent order by subquery

I have a problem with ordering by columns in subquery (lastname, firstname).
I already tried this code as suggested by other posts:
->with(['customer' => function ($query) {
$query->orderBy("lastname", "asc")
->orderBy("firstname", "asc");
}])
Here my full code, but it doesn't work.
return Membership::forCompany($companyId)
->whereIn('state', ['ATTIVA', 'IN ATTESA DI ESITO', 'DA INVIARE'])
->where(function ($query) {
$query->where('end_date', '>=', Carbon::now()->toDateString())
->orWhereNull('end_date');
})
->with('federation')
->with(['customer' => function ($query) {
$query->orderBy("lastname", "asc")
->orderBy("firstname", "asc");
}]);
Here the relationships:
In customer model I have:
public function memberships() {
return $this->hasMany('App\Models\Membership');
}
In Membership model I have:
public function customer() {
return $this->belongsTo("App\Models\Customer");
}
Try orderBy() with join() like:
$memberships = \DB::table("memberships")
->where("company_id", $companyId)
->where(function ($query) {
$query->where('end_date', '>=', Carbon::now()->toDateString())
->orWhereNull('end_date');
})
->join("customers", "memberships.customer_id", "customers.id")
->select("customers.*", "memberships.*")
->orderBy("customers.lastname", "asc")
->get();
dd($memberships);
Let me know if you are still having the issue. Note, code not tested! so you may need to verify by yourself once.

Resources