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);
Related
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')
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
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();
In plain English: I have three tables. subscription_type which has many email_subscriptions which has many emails.
I'm trying to select all email_subscription records that have a particular subscription_type, that also don't have any associated email records that have a status of Held.
The particular bit I am stuck on is only returning email_subscriptions which have zero emails (with an additional where clause stacked in there described above).
Using Eloquent, I've been able to get a bit of the way, but I don't have any idea how to select all the records that have a relationship count of zero:
$subscriptionsWithNoCorrespondingHeldEmail = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})-; // What do I chain here to complete my query?
Additionally, is this even possible with Eloquent or will I need to use Fluent syntax instead?
You can use the has() method to query the relationship existence:
has('emails', '=', 0)
Eg:
$tooLong = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})->has('emails', '=', 0)->get();
Edit
You can do more advanced queries with the whereHas() and whereDoesntHave() methods:
$tooLong = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})
->whereDoesntHave('emails', function ($query) {
$query->where('status', '=', 'whatever');
})->get();
OK what I have under stand from your Question is you would like to have a All Emails which have
specific subscription_type, Zero(0) association and status = status
If yes so you canuse array in where statement.
Like:
$q->->where(array('status' => 'status','subscription_type'=>'what ever you want));
Currently I have this whereHas in a collection of my model:
$query = self::whereHas('club', function($q) use ($search)
{
$q->whereHas('owner', function($q) use ($search)
{
$q->where('name', 'LIKE', '%'. $search .'%');
});
});
I was under the impression the code above could be as such:
$query = self::whereHas('club.owner', function($q) use ($search)
{
$q->where('name', 'LIKE', '%'. $search .'%');
});
I'm aware this is already a lot of power, but even then, if I have a nested relationship 5 levels deep, things will get ugly.
Update:
As stated in the comments, I ended up not making my question clear, I apologize.
I will try to use a simple example, consider $owner->club->membership->product->package, now from owners I want to search a certain package, it would be something like this:
$query = self::whereHas('club', function($q) use ($search)
{
$q->whereHas('membership', function($q) use ($search)
{
$q->whereHas('product', function($q) use ($search)
{
$q->whereHas('package', function($q) use ($search)
{
$q->where('alias', 'LIKE', '%'. $search .'%');
});//package
});//product
});//membership
});//club
Is this correct? Is there a shortcut?
Update: the PR has been just merged to 4.2, so now it's possible to use dot nested notation in has methods ( ->has('relation1.relation2) ->whereHas('relation1.relation2, .. )
Your question remains a bit unclear or you misunderstand whereHas() method as it is used to filter models (users in this case) and get only those that have related models fitting search conditions.
It seems that you want to find Packages from the context of a given User, so no need to use whereHas method.
Anyway depending on the relations (1-1,1-m,m-m) this can be easy or pretty hard and not very efficient. As I stated, loading nested relations means that for every level of nesting comes another db query, so in this case you end up with 5 queries.
Regardless of the relations you can invert this chain like this, as it will be easier:
edit: This is not going to work atm as whereHas() doesn't process dot nested relations!
// given $user and $search:
$packages = Package::where('alias','like',"%$search%")
->whereHas('product.membership.club.user', function ($q) use ($user) {
$q->whereId($user->id);
})->get();
As you can see this is much more readable, still runs 5 queries.
Also this way you get $packages, which is a single Collection of the models you wanted.
While from the context of a user you would get something like this (depending on the relations again):
$user
|-club
| |-membership
| | |-product
| | | |-packages
| | |-anotherProduct
| | | |-packages
| | |-yetAnotherProduct
| | |-packages
| |-anotherMembership
.....
You get the point, don't you?
You could fetch the packages from the Collection, but it would be cumbersome. It's easier the other way around.
So the answer to your question would be simply joining the tables:
// Let's assume the relations are the easiest to handle: 1-many
$packages = Package::where('alias','like',"%$search%")
->join('products','packages.product_id','=','products.id')
->join('memberships','products.membership_id','=','memberships.id')
->join('clubs','memberships.club_id','=','clubs.id')
->where('clubs.user_id','=',$user->id)
->get(['packages.*']); // dont select anything but packages table
Of course you can wrap it in a nice method so you don't have to write this everytime you perform such search.
Performance of this query will be definitely much better than separate 5 queries shown above. Obviously this way you load only packages, without other related models.
Just building upon Jarek's answer one may add a scope utility to their model:
public function scopeWhereRelatedIs($query, $relation, $related) {
$query->whereHas(
$relation,
function ($query) use ($related) {
$query->where('id', '=', $related->id);
}
);
}
With this in place, the usage becomes really elegant, such as:
$packages = Package::where('alias','like',"%$search%")
->whereRelatedIs('product.membership.club.user', $user)
->get();