I am writing a search functionality which basically looks up several tables.
The DB structure and relationships are as follows:
`users`
id
name
user_type_id
`user_type`
id
type
`user_nicknames`
id
user_id
nickname
User model has a \Illuminate\Database\Eloquent\Relations\BelongsTo relationship with user_type
and a \Illuminate\Database\Eloquent\Relations\HasMany relationship to user_nicknames
What I am trying to get is search against a specific searchTerm which can be found either in users table and in user_nicknames
This one is failing right now:
$user = $this->user->newQuery();
$user->whereHas('userType', function ($query) use ($filters) {
$query->where('type', $filters['type']);
});
$user->whereHas('userNickName', function ($query) use ($searchTerm) {
$query->where('custom_title', 'like', '%'.$nickname.'%');
});
Please take note that I'd prefer to use the eloquent relationships for this rather than multiple joins.
Any ideas?
In your User model add these functions:
public function scopeUserTypeFilter($query, $filters)
{
return $query->whereHas('user_type', function ($query) use ($filters) {
$query->where('type', $filters['type']);
});
}
public function scopeNicknameFilter($query, $nickname)
{
return $query->whereHas('user_nick_name', function ($query) use ($searchTerm) {
$query->where('custom_title', 'like', '%'.$nickname.'%');
});
}
Then you can use these on your controller:
public function search(Request $reqeust)
{
$users = User::where(function ($query) use ($request) {
return $query->when($request->filled('s'), function($query) use ($request) {
return $query->nicknameFilter($request->s);
});
})->where(function ($query) use ($request) {
return $query->when($request->filled('userType'), function($query) use ($request){
return $query->userTypeFilter($request->userType);
});
})->get();
}
Related
I'm not sure if there's a a way of doing this but I would like to add a where clause to the second with in my query but it doesn't work. It just returns all the votes as if the condition wasn't there. Any help would be appreciated.
public function PostId($request)
{
$post_id = $request->post_id;
$user_id = auth('api')->user();
$post = Post::with('categories')
->where('id', $post_id)
->with('votes')
->where('user_id', $user_id->id)
->first();
return $post;
}
You need to use closure in your with statement.
Also, I'd recommend using findOrFail() instead of where conditional for your query. Therefore, in case you pass a wrong post_id in your request an exception 404 will be thrown.
A nicer way to accomplish what you want could be:
public function PostId($request)
{
$post = Post::with(['categories', 'votes' => function($query){
$query->where('user_id', auth('api')->user()->id);
})
->findOrFail($request->post_id);
return $post;
}
$post = Post::find($post_id) // Find method will return only first record. no need to call ->first() explicitly.
->with([
'categories',
'votes'
])
For the ->where('user_id', $user_id->id), you dont need to do it in here as you already have defined the relation 'votes'.
Class Post
{
public function votes()
{
return $this->hasMany(Vote::class)->where('user_id', $this->user_id); // You can have the where condition here assuming you have user id field present in the Post model. Else you can keep it as below in your query
}
}
With user id in the runtime query
$post = Post::find($post_id)
->with([
'categories',
'votes' => function($query) use($user_id) {
$query->where('user_id', $user_id->id);
}
])
I am trying to create a search filter:
if ($request->has('street')) {
$streets->where('name', 'like', '%'.$request->street.'%');
}
if ($request->has('house')) {
$streets->whereHas('properties', function ($query) use ($request) {
$query->where('house_number', $request->house);
});
}
return $streets->get();
This currently gets the street data. I want to do something like this ?street=b-34&house=21 and to display the house data.
At the moment I am only getting the street data.
Your problems seems one every Laravel developer will go through one day. You forgot to return the query to the original constructor.
You have:
$streets->where('name', 'like', '%'. request('street') .'%');
when you should have:
$streets = $streets->where('name', 'like', '%'. request('street') .'%');
You are chaining the methods so you should have it return the original query constructor
EDIT Based on comments:
Besides from not returning the query as I stated above you are using whereHas as #thisiskelvin have pointed out in his answer.
You should be using
if (request()->has('street')) {
$streets = $streets->where('name', 'like', '%'. request('street') .'%');
}
if (request()->has('house')) {
$streets = $streets->with(['properties' => function ($query) {
$query->where('house_number', request('house'));
}]);
}
return $streets->get();
You should be able to achieve this by using the ->with() eloquent method. If the request has a house query, it will return all properties where the house_number equals the queried house number. Once found will attach to the returned results:
if (request()->has('street')) {
$streets = $streets->where('name', 'like', '%'. request('street') .'%');
}
if (request()->has('house')) {
$streets = $streets->with(['properties' => function ($query) {
$query->where('house_number', request('house'));
}]);
}
return $streets->get();
Note: I have changed $request to use the request() helper.
I have a problem with finding results. I search the 'name' records in the translation table for the occurrence of any of the words.
//Bad result:
return Product::whereIn('id', $ids)->whereHas('translations', function ($query) use ($findTextWildcards) {
foreach ($findTextWildcards as $value) {
$query->orWhere('name', 'like', "%{$value}%");
}
});
//good result but difficult query
return Product::whereIn('id', $ids)->where(function ($query) use ($findTextWildcards) {
foreach ($findTextWildcards as $value) {
$query->whereHas('translations', function ($q) use ($value){
$q->where('name', 'like', "%{$value}%");
});
}
});
Try with this one. Probably the problem was that you didn't wrap this with additional where closure.
return Product::whereIn('id', $ids)
->whereHas('translations', function ($query) use ($findTextWildcards) {
$query->where(function($query) use ($findTextWildCards) {
foreach ($findTextWildcards as $value) {
$query->orWhere('name', 'like', "%{$value}%");
}
});
});
I have problem write correct select with ordering for my Page table.
Actual implementation:
private function findPageBySlug($slug)
{
$page = Page::with('children')
->where('active', 1)
->where('slug', $slug)
->first();
if (property_exists($page, 'chidlren')) {
return $page->whereHas('children', function ($child) {
return $child->where('active', 1)->orderBy('order', 'asc');
});
} else {
return $page;
}
}
I need select all child of Page and order them by order column and also children can have another child and so on is no restriction to deepth.
Is posible to write select for this?
Thanks for your help
If you want to get all children and not filter pages by children, use with(). Instead of:
return $page->whereHas('children', function ($child) {
return $child->where('active', 1)->orderBy('order', 'asc');
});
Do:
return $page->with(['children' => function ($child) {
return $child->where('active', 1)->orderBy('order', 'asc');
}]);
If you want bith filter and load children, chain both wherHas() and with()
I have to call a soap service using laravel and done so correctly. This soap service requires me to send a login request prior to sending any other request.
The code I'm using works, but I want to improve by removing the login from all the functions and creating one function.
I tried changing the following for one function:
public function getcard($cardid)
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
$card = [
'CardId' => $cardid,
];
SoapWrapper::service('IS', function ($service) use ($data,$card) {
$service->call('Login', [$data]);
$cardinfo=$service->call('GetCard', [$card]);
dd($cardinfo->Card);
});
}
Into:
public function login()
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
//$service->call('Login', [$data]);
//return $service;
});
}
public function getcard($cardid)
{
$this->login();
$card = [
'CardId' => $cardid,
];
$cardinfo=$service->call('GetCard', [$card]);
dd($card);
}
But this doesn't work. I also tried it with the commented out part, but that doesn't work. Both options result in an error that it didn't find 'service'.
I know it has something to do with oop, but don't know any other option.
I took this as an example, but I probably implemented it wrong?
So my question is: How do I reuse the login part for all other functions?
Your return statement in the login() method is within the scope of that closure. You need to return the result of the closure as well.
return SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
});
EDIT:
To explain a little bit. You have a function:
SoapWrapper::service('IS' ,function() {}
Inside of a function : public function login()
If you need to return data from your login() method, and that data is contained within your SoapWrapper::service() method, then both methods need a return statement