Laravel ajax search with relations - php

I wanted to search the records after getting the relations. I tried with Laravel colletions. didnt work tough.
public function search($key){
$products = Product::with('unit', 'category', 'brand')->get();
$allproducts = collect($products);
$result = $allproducts->search($key);
Here I want the search to be done also based on category and brand.
If I cant you collection then how to do it the standard way.

I think using a query is the best approach since you dont need to load all products-categories-etc from the database and then try to find the matching ones.
Example query
Product::where('product_name', 'LIKE', "%$keyword%")
->orWhereHas('category', function ($q) use ($keyword) {
$q->where('category_name', 'LIKE', "%$keyword%");
//everything else you need to check
})->get();

Related

Trying to related tables columns and sums with Laravel eloquent

Ok.
I have three tables
products
--product_id
--product_name
--product_type_id
--price15
--price23
--description
--bonus_points
--image
productTypes
--product_type_id
--product_type_name
productQuantities
--id
--product_id
--warehouse_id
--quantity
Products are placed in different warehouses so I have to keep tracks of its numbers
And has relationships are like this
class Product extends Model
{
public function productType() {
return $this->belongsTo('App\Models\ProductType','product_type_id','product_type_id');
}
public function productQuantities() {
return $this->hasMany('App\Models\ProductQuantity','product_id','product_id');
}
}
What I want to get is all columns from products and product type name from productType, sum of quantity from productQuantities, so I can perform search on those column values later on with where().
How can I get these columns with Eloquent?
I know I could get them with raw SQL commands but I need to do this way for compatibility reasons.
I tried this way before I ask the question.
But model relations just stopped working with no errors. Values just got emptied out from the other parts of the page.
$products = Product::selectRaw('products.*, productTypes.product_type_name, sum(product_quantities.quantity) as quantitySum')
->leftjoin('productTypes','products.product_type_id','=','productTypes.product_type_id')
->leftjoin('productQuantities','products.product_id','=','productQuantities.product_id')
->where('products.product_id','like','%'.$searchID.'%')
->where('product_name', 'like', '%'.$searchName.'%')
->where('product_type_name', 'like', '%'.$searchType.'%')
->where(function($q) use ($searchPrice) {
$q->where('price15','like','%'.$searchPrice.'%')
->orwhere('price23','like','%'.$searchPrice.'%');
})
->where('points', 'like', '%'.$searchPoints.'%')
->groupBy('products.product_id')
->orderByRaw($query)
->paginate($paginateBy);
Working version before this was simple.
Product::leftjoin('productTypes','products.product_type_id','=','productTypes.product_type_id')
->select('products.*','productTypes.product_type_name')
->where('products.product_id','like','%'.$searchID.'%')
->where('product_name', 'like', '%'.$searchName.'%')
->where('product_type_name', 'like', '%'.$searchType.'%')
->where(function($q) use ($searchPrice) {
$q->where('price15','like','%'.$searchPrice.'%')
->orwhere('price23','like','%'.$searchPrice.'%');
})
->where('points', 'like', '%'.$searchPoints.'%')
->orderByRaw($query)
->paginate($paginateBy);
And I thought any kind of join methods doesn't seem to be working well with Eloquent relationship? But older one has leftjoin method as well.
I have not tested this (and am assuming you want to group on product_type_name but you should be able to do something along the lines of:
$results = Product::with(['productType','productQuantities'])
->select(DB::raw('products.*,
productType.product_type_name,
sum(productQuantities.quantity) as "QuantitySum"'))
->groupBy('productType.product_type_name')
->get();
OR
$results = DB::table('products')
->join('productType', 'productType.product_type_id', '=', 'products.product_type_id')
->join('productQuantities', 'productQuantities.product_id', '=', 'products.product_id')
->select(DB::raw('products.*,
productType.product_type_name,
productType.product_type_name,
sum(productQuantities.quantity) as "QuantitySum"'))
->groupBy('productType.product_type_name')
->get();
Then you should be able to access the aggregated quantities using (in a loop if you wanted) $results->QuantitySum.
you can get it with eager loading and aggregating. For example, you need to query products has product type name like "new product" and quantity greater than 1000:
Product::with("productType")
->whereHas("productType", function ($query) {
$query->where("product_type_name", "like", "new product");
})
->withCount(["productQuantities as quantity_count" => function ($query) {
$query->selectRaw("sum(quantity)");
}])
->having("quantity_count", ">", 1000)
->get();
you can get through relationship
$product->productType->product_type_name
and attribute:
$product->quantity_count
$products = Product::withsum('productQuantities','quantity')
->leftjoin('product_types','products.product_type_id','=','product_types.product_type_id')
Gives me the result that I wanted. And didn't break the other parts.
But I'm still confused why with() and withSum() didn't work together.
Is it because products belongs to productTypes maybe

Laravel - Query Model if values contain a certain string (taken from search input)

I am implementing a search using Laravel and Ajax. So I have a Product which belongs to a Tag and a Subcategory. On the other hand the Subcategory belongs to a Category. I want to check all of their properties (field values) and check if they contain the given string. With some searching I found out that I have to use LIKE. Here is what I tried:
$products = Product::where('name_en', 'LIKE', $search)->get();
However this will get the products if the search string matches exactly the value. I want to match if it contains it. How can I proceed with the belongsTo relationships? How can I check the propreties of Tag and Subcategory as well? How to chain everything together so I achieve the desired result? Thanks in advance.
you are doing one thing wrong, your query returns you exact matches because you given the exact string. But your query should be like this.
$products = Product::where('name_en', 'LIKE', '%'.$search.'%')->get();
Above query will gives your products which contains the searched string.
And if you want to search in relational tables then you can user laravel method join(). But there are one more method whereHas but I always avoiding this method, because it creates very complex query. which is very heavy. So you can use join() method which will add inner join with relational table.
Here is the example of join:
$products = Product::join('tags', function($builder) {
$builder->on('tags.id', '=', 'products.tag_id');
// here you can add more conditions on tags table.
})
join('sub_categories', function($builder) {
$builder->on('sub_categories.id', '=', 'products.tag_id');
// here you can add more conditions on subcategories table.
})
->where('name_en', 'LIKE', '%'.$search.'%')
->get();
This is the basic example, you can use this according to your requirement.
To add to Lakhwinder Singh’s answer, it might be worth wrapping it up in a scope that you can apply to your model:
class Product extends Model
{
public function scopeSearch($query, $keywords)
{
return $query->where('name_en', 'LIKE', '%'.$keywords.'%');
}
}
You can then use this scope like this:
$products = Product::search($keywords)->get();
Which means you don’t have to keep manually adding “LIKE” conditions throughout your application.
As an aside, Laravel’s introducing Scout, a driver-based full text search extension for Eloquent, in version 5.3.
What you want is to write an advanced query to search product based on related models too, so as previous suggestion by others, you have to write join statements.
Check my example code below, which is written to search members, the search string also will bring members if the string matches, members skills or positions, so this will surely help you.
$users = User::select('app_users.*')
->distinct()
->join('app_members', 'app_users.id', '=', 'app_members.app_users_id')
->leftJoin('app_members_jobtitles', 'app_members.id', '=', 'app_members_jobtitles.app_members_id')
->leftJoin('app_jobtitles', 'app_members_jobtitles.app_jobtitles_id', '=', 'app_jobtitles.id')
->leftJoin('app_members_tags', 'app_members.id', '=', 'app_members_tags.app_members_id')
->leftJoin('app_technologies', 'app_members_tags.app_technologies_id', '=', 'app_technologies.id')
->whereNull('app_users.activation')
->where('app_users.block','=',0)
->where(function ($query)use ($search) {
$query->orWhere('app_users.first_name', 'like', '%'.$search.'%')
->orWhere('app_users.last_name', 'like', '%'.$search.'%')
->orWhere('app_members.company', 'like', '%'.$search.'%')
->orWhere('app_members.job_title', 'like', '%'.$search.'%')
->orWhere('app_jobtitles.title', 'like', '%'.$search.'%')
->orWhere('app_technologies.title', 'like', '%'.$search.'%')
->orWhere('app_members.summary', 'like', '%'.$search.'%');
})
->get();
Note the following join in the above code, which is in your case category and sub category
->leftJoin('app_members_jobtitles', 'app_members.id', '=', 'app_members_jobtitles.app_members_id')
->leftJoin('app_jobtitles', 'app_members_jobtitles.app_jobtitles_id', '=', 'app_jobtitles.id')

Laravel - Using a nested relationship with the query builder

I am working on an API but its starting to get a bit slow now that the data is increasing. I am moving some of the queries so that they use the DB query builder.
I have my last one which has a nested query:
$artists = Artist::with('performances', 'performances.stage')->get();
I have got so far:
$artists = \DB::table('artists')
->leftJoin('performances', 'artists.id', '=', 'performances.artist_id')
->get();
But now need to do the second relationship which in the Performance model is:
public function stage()
{
return $this->hasOne('App\Models\Stage', 'id', 'stage_id');
}
Any help on how I do this?
yes you can use eloquent relationship with query builder like this
$artists = Artist::join('performances', 'artists.id', '=', 'performances.artist_id')
->all();
foreach($artists as $artist){
$data = $artist->stage()->first();
}
It is very well covered in the official documentation, please, refer to this section of Documentation
I think that you want to achieve something like this:
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
And also, please, read carefully this section

Laravel 5 Model::with() behavior

I'm trying to get a simple list of products with a given category, using Laravel 5's (L5) Model::with() method. But it seems that L5 ignores the category where clause.
The relation in my Product model:
public function categories(){
return $this->belongsToMany('App\Category', 'categories_products');
}
In my Controller:
public function getByCategory($slug){
$return = Product::with(array('categories' => function($query) use ($slug){
$query->where('slug', 'like', $slug);
}))->paginate(60);
dd($return);
}
The result is a list of every product in my database, instead of just a list of those with the given category slug.
I'v tried to hardcode in some different where clauses, but all seems to be ignored. Am I missing something?
Eloquent doesn't use joins to query related data when using with(), but instead uses separate queries. In your example, it first fetches products and then fetches related categories.
You need to use has() or whereHas() to return only those products that have categories (slugs?).
public function getByCategory($slug){
$return = Product::has('categories')->with(array('categories' => function($query) use ($slug){
$query->where('slug', 'like', $slug);
}))->paginate(60);
dd($return);
}
Or:
public function getByCategory($slug){
$return = Product::whereHas('categories', function($query) use ($slug){
$query->where('slug', 'like', $slug);
})->paginate(60);
dd($return);
}
whereHas() adds a subquery that counts the number of relations. You should use DB::getQueryLog() to see the SQL that Eloquent produces. Makes it a lot easier to figure out what's going on!
Do you need to have an advanced subquery? If slug is defined as a column in product you can do this:
$return = Product::where('slug', 'like', $slug)->paginate(60);
dd($return);
Or if using the relations your query would look like this:
$return = Product::where('slug', 'like', $slug)->categories->paginate(60);
dd($return);

More than one way to invert an Eloquent query?

Consider the following query:
$tickets = Account::with('tickets')->where('name','LIKE',"%{$search}%")->paginate(10);
This is how I inverted it:
$tickets = Ticket::whereHas('account', function($q) use ($search)
{
$q->where('name', 'LIKE', '%'. $search .'%');
})->paginate(10);
Is there another way to invert the first query?
I was looking for a way to execute the first query and receive only results from the tickets table. As it is now of course I will receive the tickets as a property of each account returned.
Answering your comment: yes, you can fetch tickets from 1st query. This is how to do it, though pagination is run in the context of accounts, so we will skip it and retrieve all the accounts:
// returns Eloquent Collection of Account models
$accounts = Account::with('tickets')->where('name','LIKE',"%{$search}%")->get();
// returns array of Ticket arrays (not models here)
$tickets = $accounts->fetch('tickets')->collapse();
As you can see it's possible but a bit cumbersome. Of course you can always prepare a helper method for this, but still you get arrays instead of models etc
So in fact I suggest using the 2nd solution, but here I'll give you something to make it nice and easy:
// Ticket model
public function scopeHasCategoryNameLike($query, $search)
{
$query->whereHas('categories', function($q) use ($search) {
$q->where('name', 'like', "%{$search}%");
});
}
Then it's as simple as this:
$tickets = Ticket::hasCategoryNameLike($search)->paginate(10);

Categories