Described below is my getter in my Post model:
public function getTicketIdAttribute() {
return str_pad($this->id, 6, '0', STR_PAD_LEFT);
}
Note that the function simply adds 6 zero padding at the front of the Post's post_id.
Now, is it possible to use it in a query builder? Ihave tried the following:
Post::where(function($query) use ($keyword) {
$query->where('detail', 'LIKE', '%'. $keyword .'%')
->orWhereHas($query->getTicketIdAttribute(), 'LIKE', '%'. $keyword .'%');
});
What I'm trying to do is to have a search engine which look for Post object if:
$keyword matches the Post's detail
or
if $keyword matches the getTicketIdAttribute()
As pointed out by Jonas Staudenmeir I need to rewrite the code using PHP's LPAD(), so:
LPAD(id, 6, '0')
and to use it in Query Builder I need to apply it using the DB::raw method as there is no equivalent LPAD() method available right out the box. So, use it like this:
DB::raw("LPAD(id, 6, '0')")
Just a note that this means for this case the Model's getter Attribute cannot be used directly in Query Builder, so need to apply it this way.
Hope this help others, and I thank Jonas Staudenmeir for the help.
Related
I am doing data search function in php . I use mongoDB. I have a table user:
id name status
1 tèo 1
2 tý 2
In UserRepository.php :
<?php
namespace App\Repositories\Backend\MongoDB;
use App\Models\User;
use App\Repositories\MongoDBBaseRepository;
class UserRepository extends MongoDBBaseRepository
{
public function model()
{
return MoviePassContent::class;
}
public function search($keyword)
{
$search = $this->model->where('name', 'like', '%'.$keyword.'%')->get()->toArray();
// I tried more of these ways, but it still doesn't work
// $search = $this->model->where('name', 'like', $keyword.'%')->get()->toArray();
// $search = $this->model->where('name', 'like', '%'.$keyword)->get()->toArray();
}
}
If the $keyword I enter is : tèo, the result is correct. But when I enter $keyword as teo or t it returns null. Where did I go wrong. Please give me your opinion. Thank you.
It's not related to Laravel or PHP. It all depends on your database. Try to utilize a different collation charset on your database.
Example below changes your collation and charset to utf32. Careful, make sure to backup your database to prevent any data loss! This action may cause partial or full data loss:
ALTER DATABASE your_db_name
CHARACTER SET utf32]
COLLATE utf32_general_ci
You may tweak it to find the best fit for you, it depends on what kind of data you're inserting into the db.
Another safer alternative is to mitigate it on application layer. Define a scope which will help with special character conversions.
As for the: "searching by 't' but not appearing" issue, it seems like DBMS config problem. Even though it usually applies for FULLTEXT search, there might be some setting about min-length regarding LIKE queries.
Assuming you are using jenssegers/laravel-mongodb then you can achieve this with ->options to your query:
$search = $this->model
->where('name', 'like', '%'.$keyword.'%')
->options([
'collation' => [
'locale' => 'en',
'strength' => 1
]
])->get()->toArray();
Check https://www.mongodb.com/docs/v5.3/reference/collation/ for more details on the collation option
I have models 'Case' and 'Type' in many to many relation. 'Case' belongs to a 'Type'. Now I am trying to implement search and filter in my case index. I have already included the search in the query builder so that the keywords match multiple columns and also columns in related table. Now I am trying to incorportae a filter also. But when I am including that filter in query builder, it is not working. Including either search or filter is working but including both together is not.
Here is my code to include the search in query builder:
$key = $this->search;
$cases = Landcase::query()->orderBy('created_at', 'DESC');
$cases
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
This much is perfectly working. I can search in title, number and type's name. Now here is the additional code to include the filter with type id(s) so that only specific type's cases will be shown and the search will work on those only.
if($this->selected_types){
$ids= $this->selected_types;
$cases->whereHas('type', function ($query) use ($ids){
$query->whereIn('id', $ids);
});
}
this block is not affecting the collection. But if I comment out the first block, this works. How can I incorporate it to my existing query builder block?
You have several OR in your where clauses so adding the last part makes it something like:
WHERE … OR … OR (… AND …)
What you need is to group your orWheres:
$cases->where(function ($query) {
$query
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
});
More here: https://laravel.com/docs/9.x/queries#or-where-clauses
I want to ask for your help as I'm having challenges creating a Laravel query.
Here's my current code:
$products = Product::where(function($query) use ($searchTerm) {
$query->where('product_name', 'LIKE', "%$searchTerm%");
$query->orWhere('product_category', 'LIKE', "%$searchTerm%");
$query->orWhere('product_description', 'LIKE', "%$searchTerm%");
})
->where('lang', '=', 'en')
->orderBy('product_name', 'ASC')
->get();
If I have a product named Michael's Handsoap and the user searches using the keyword "Michaels", it returns nothing with the above query. Is there a way to remove special characters from database fields as you do the query? Any input would be appreciated.
Thanks!
EDIT: Figured that it's best just to add a product_name_search column that contains the product name stripped of special characters (using, for example, preg_replace("/[^A-Za-z0-9 ]/", '', $productName))
This way, $query->where('product_name_search', 'LIKE', "%$searchTerm%") can now be used. It adds another parameter, but at least you've covered them all now.
I have a Laravel Eloquent Model called EmailList and a model called Subscriber.
I defined the following hasMany relationship on the EmailList.
public function subscribers() {
return $this->hasMany(Subscriber::class);
}
This relationship works fine.
I now want to apply a where() condition on the parent and a where() condition on the child.
For example, I have two EmailLists, with a property id and a property called listname ("List A" and "List B"). My Subscriber model has four relevant properties: id, name, email and email_list_id.
Problem: I want to take all subscribers from a certain list, e.g. 'List A', and filter the subscribers with $query (search functionality with Livewire).
In order to accomplish this, I came up with the following code:
EmailList::where('listname', $listname)->first()
->subscribers()
->where('name', 'LIKE', '%'.$this->search.'%')
->orWhere('email', 'LIKE', '%'.$this->search.'%')
->paginate(50) //or get(), if you like
This code partially worked, because the subscribers were indeed filtered, but not the EmailList. In other words, I just searched all subscribers, rather than the subscribers on a specific list.
After a lot of searching I figured out that I should perhaps use a whereHas(), but when I tried that, it gave me only EmailLists and not Subscribers.
It'd be great if someone could help me filter this. Perhaps it's peanuts for an experienced developer😊, but it would help me a lot!
Update:
After seeing the answer by EricLandheer, I realised that (of course) I also have the EmailList id. Perhaps that makes it a little bit simpler, so that you can use find() instead of a where()->first() clause. (I use first() because the ‘listname’ is unique and I only want one instance. Is that correct?)
Currently, your code only returns the first EmailList. Did you that purposely, or do you only want the first EmailList that has the $listname?
Here is a solution using Eager loading with constraints that overrides the subscribers relation with a callback. The result should be all EmailLists with the $listname and the matching subscribers with the search.
EmailList::where('listname', $listname)
->with('subscribers', function($query) {
return $query->where('name', 'LIKE', '%'.$this->search.'%')
->orWhere('email', 'LIKE', '%'.$this->search.'%')
})->get();
Regarding question update
Indeed, using Listname is fine. If it is unique, no problem. You can use find() with an ID to, which is preferable if you're thinking of changing the name of the EmailList at a later stage.
Perhaps a more elegant / readable solution would be:
$emailList = EmailList::firstWhere('listname', $listname);
$emailList->subscribers = $emailList->subscribers()
->where('name', 'LIKE', '%'.$this->search.'%')
->orWhere('email', 'LIKE', '%'.$this->search.'%')
->get();
You could refactor the search to a model / service if you are using it in more places:
// EmailList model
public function subscribers()
{
return $this->hasMany(Subscriber::class);
}
public function searchSubscribers(string $search): Collection
{
return $this->subscribers()
->where('name', 'LIKE', '%'.$this->search.'%')
->orWhere('email', 'LIKE', '%'.$this->search.'%')
->get();
}
Final code
$emailList = EmailList::firstWhere('listname', $listname);
$emailList->subscribers = $emailList->searchSubscribers($this->search);
Got a question if anyone can help out.
I have a query that has a collection of parameters and displays some results.
DB::table()
->join()
->where()
->orderby()
->select()
->get();
But the wheres are generated by a form input in the view. Basically this is a bunch of filters to get a table of results. I want to paginate that. if I change the get() to paginate(), and call $result->links() in the template, it does indeeed paginate and generates a bunch of results for me, however the problem is that when you move away from page 1, the links are just a _GET parameter and all the filter input does not get applied.
Is there a way I can have the pagination AND filters going at the same time? What is the laravel way of handling that? Or would I have to build some way of handling filters and pages? Any tips on that?
Cheers!
* Solution *
The solution was to make the form use GET method and persist the old filters to the Pagination using ->appends() method. Below is the modified code to do that if anyone else is looking.
$results = $query->paginate($this->report->inputs->per_page);
$query = array_except( Input::query(), Paginator::getPageName() );
$results->appends($query);
Yes, here's how I do it:
$orderBy = Input::get('orderBy', 'created_at');
$order = Input::get('order', 'DESC');
$limit = Input::get('limit', 100);
$name = Input::get('name', '');
$result = DB::table()
->where('name', 'LIKE', '%' . $name . '%')
->orderBy($orderBy, $order)
->paginate($limit);