orWhere in Laravel misbehaving when fetching data from relationship - php

I have this controller method that am using to search users in a given user group using email and name.
I have two groups customer and staff, when I search staff it returns customers too.
Here is the controller code
public function index(Request $request, UserGroup $group) { //group=staff
$users = $group->users();
if ($request->has('search') && $request->search) {
$users->where('name', 'LIKE', '%' . $request->search . '%')
->orWhere(function ($builder) use ($request) {
$builder->where('email', 'LIKE', '%' . $request->search . '%');
});
}
return response()->json($users->get());
}
Inside the UserGroup model here is the implementation of the code
class UserGroup extends Model{
//...
public function users(): BelongsToMany | User | Collection
{
return $this->belongsToMany(User::class, 'user_user_group');
}
//...
}
All I want is users with UserGroup staff and has name or email as typed in the search key.
Instead, the system is returning even users who do not have the staff user group.
What am I doing wrong?

You are doing it in the wrong way in writing the where clause. When you use orWhere outside, it tells laravel query to join the users table on the groups table and the first condition or the second condition. It should be join on group table and (the first condition or the second condition). It should wrap in the round brackets when it executes the query.
Your query should be:
$users = $group->users();
if ($request->has('search') && $request->search) {
$users->where(function ($builder) use ($request) {
$builder->orWhere('name', 'LIKE', '%' . $request->search . '%');
$builder->orWhere('email', 'LIKE', '%' . $request->search . '%');
});
}
I hope this will help you. For more information, I would suggest to read the laravel official documentation. Laravel Query Builder

You can also query this from the User::class perspective
User::query()
->where("name", "LIKE", "%{$request->search}%")
->orWhere("email", "LIKE", "%{$request->search}%")
->where(function(Builder $query) {
$query->whereRelation('#group', '#groupColumn','staff')
})->get();
This query will fetch the users that has the searched name or email with the group of staff.
I don't know the relationship of your usergroup and users just change the word with # to match the yours.
#group = relation of the UserGroup and User just like the users() to your UserGroup::class.
#groupColumn = column the staff belongs to.
Hope it helpful.

Related

Fetch data from same field name in two different tables and detect which filed name we mean

I have assigned search method for my application and i have made relationship between two tables "users " table and "posts" table both of them have same field name "created_at" and when i want to fetch data that when this post has been created it will bring the date in users table created_at field not in posts table created_at field.
All i want is how to tell the system make difference between which table field name i mean .
This date comes from created_at field in users table
I wand to come date in this created_at field in posts table
ListingController.php
public function search(Request $request){
$posts = Post::with(['comments','category','creator'])
->join('users', 'posts.created_by', '=', 'users.id')
->where('slug', 'LIKE', '%'.$request->search. '%')
->orWhere('users.name', 'LIKE', '%'.$request->search. '%')
->where('status',1)->orderBy('posts.id','DESC')->paginate(5);
return view('front.listing',compact('posts'));
}
listing.blade.php
<div class="article_date">
by: {{ $post->creator->name }} , {{$post->created_at->diffForHumans()}}
</div>
You are calling your relationship improperly. You should not use a join there. Use constrained eager loading instead
There will be no conflict in the created_at date since there will be no join.
While still keeping your search query inside the closure of the relationship.
$posts = Post::with(['comments', 'category'])
->with(['creator' => function ($query) use ($request) {
$query->where('name', 'LIKE', '%' . $request->search . '%');
}])
->where('slug', 'LIKE', '%' . $request->search . '%')
->where('status', 1)
->orderBy('id', 'DESC')
->paginate(5);

Laravel eloquent: How to filter with where() and whereHas()

I am trying to filter my users based on categories in which they have posted something and also the users directly by search string:
$users = User::when($search, function ($query) use ($request) {
$query->where('name', 'like', '%' . $request['search'] . '%')
->orWhere('email', 'like', '%' . $request['search'] . '%');
return $query;
})
->when($categories, function ($query) use ($request) {
return $query->whereHas('posts.category', function ($query) use ($request) {
$query->whereIn('name', $request['categories']);
});
})
->select('id', 'name', 'email')
->paginate(10);
When I filter for categories only (without $search) it is working as expected but as soon as I type a search string the result is filtered by the search string $request['search'] but ignores the category filtering completely.
So for example:
The user named "Jon" has a post in category "Lifestyle".
Another user named "Jonn" has a post in category "Sports".
Now if I filter for category only "Sports" I get the correct result -> "Jonn".
But if I filter additionally with the search string "Jon" the result is -> "Jon" and "Jonn" which is not the expected result because it should filter for "Sports" and name "%Jon%" so again the result should be only "Jonn".
I don't know how I can achieve that filter combination of where() and whereHas().
Your issue is nesting of the constraints. Translated to SQL, your query looks like this:
SELECT id, name, email
FROM users
WHERE name like '%foo%'
OR address like '%foo%'
OR email like '%foo%'
AND EXISTS (
SELECT *
FROM categories c
INNER JOIN posts p ON p.category_id = c.id
WHERE c.name IN ('bar', 'baz')
)
Note: pagination omitted
As you can see, it isn't quite clear what the WHERE statements are supposed to do. But that can be fixed rather easily by wrapping all of the search constraints in an extra where():
$users = User::when($search, function ($query) use ($request) {
$query->where(function $query) use ($request) {
$query->where('name', 'like', '%' . $request['search'] . '%')
->orWhere('address', 'like', '%' . $request['search'] . '%')
->orWhere('email', 'like', '%' . $request['search'] . '%');
});
})
->when($categories, function ($query) use ($request) {
$query->whereHas('posts.category', function ($query) use ($request) {
$query->whereIn('name', $request['categories']);
});
})
->select('id', 'name', 'email')
->paginate(10);
This way your query will look like this:
SELECT id, name, email
FROM users
WHERE
(
name like '%foo%'
OR address like '%foo%'
OR email like '%foo%'
)
AND EXISTS (
SELECT *
FROM categories c
INNER JOIN posts p ON p.category_id = c.id
WHERE c.name IN ('bar', 'baz')
)
A small note on the side: You don't have to return the $query from the callbacks.
You can try like this:
$users = User::where(function ($query) {
$query->when($search, function ($query) use ($request) {
$query->where('name', 'like', '%' . $request['search'] . '%')
->orWhere('address', 'like', '%' . $request['search'] . '%')
->orWhere('email', 'like', '%' . $request['search'] . '%');
})
$query->when($categories, function ($query) use ($request) {
$query->whereHas('posts.category', function ($query) use ($request) {
$query->whereIn('name', $request['categories']);
});
})
})
->select('id', 'name', 'email')
->paginate(10);
I think you don't need your return. But you can achieve what do you want if you put your when statements in one where function like this.
I didn't test this solution so if you have errors provide it in comment.
Good luck!

Laravel Search Reference Second Model/Collection/Table with orWhereHas?

I'm trying to implement search functionality within my blade for my Script model. It performs fine for everything related to searching directly within the Script collection/table. However, my users also will need to be able to enter in a Patient first_name or last_name and search for the script records within the Script table that belong to the Patient being searched for. These are connected with a hasMany/belongsTo relationship. I'm trying to reference data from the Patient table in the Script results.
I've tried (with help from SO already) to implement orWhereHas and reference the patients table, but something with my syntax must be off.
The error messages I'm getting from trying to reference 'patients' with orWhereHas returns:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'scripts.patients_id' in 'where clause' (SQL: select count(*) as aggregate from `scripts` where exists (select * from `patients` where `scripts`.`patients_id` = `patients`.`id` and (`first_name` like %% or `last_name` like %%)))
This probably would work if it referenced patient_id, which is the column name for the patients foreign key. I don't know if there is a way to do that?
When I change 'patients' to 'patient', I receive the error:
Call to undefined method App\Script::patient()
Models
Patient hasMany Script
Script belongsTo Patient (patient_id)
Script Blade
{{ Form::text('search', $search, ['class' => 'form-control form-control-sm', 'placeholder' => 'Search Scripts...']) }}
{{Form::submit('Search', ['class' => 'btn btn-primary btn-sm'])}}
ScriptController
$search = $request->search;
$patients = Patient::all();
$scripts = Script::
when($search, function ($query) use ($search) {
$query->where(function ($query) use ($search) {
$query
->where('prescribe_date', 'LIKE', '%' . $search . '%')
->orWhere('status', 'LIKE', '%' . $search . '%')
->orWhere('efax_reference', 'LIKE', '%' . $search . '%')
->orWhere('efax_confirmation', 'LIKE', '%' . $search . '%');
});
})
->orWhereHas('patients', function ($query) use ($search) {
$query->where('first_name', 'like', '%' . $search . '%')
->orWhere('last_name', 'like', '%' . $search . '%');
})
->paginate(25);
I expect that if someone searches for a patient name within the search input on the Scripts table/model, they are able to cross-reference the Patients table/model and display the Scripts records that are foreign_key'ed to those Patients.
EDIT:
Patient model
// One Patient has Many Scripts relationship
public function scripts() {
return $this->hasMany('App\Script');
}
Script model
// Many Scripts to One Patient relationship
public function patients() {
return $this->belongsTo('App\Patient');
}

Laravel Eloquent where in relationship may or may not have result

I've got this relation:
A Company may have one or more Employees.
I need to SELECT Companies based on the first_name of an Employee, but not show it when the first_name of the employee doesn't match.
This is what I currently have.
$companies = Company::with(array('employees' => function($q) {
$type = (!empty(Input::get('company_search_employee'))) ? '%' . Input::get('company_search_employee') . '%' : '%';
$q->where( 'employees.first_name', 'LIKE', $type);
}))->get();
The employee first_name must be neglected if the Company doesn't have any Employees
But it shows the Company data, even if the employee doesn't match. the employee just gets hidden.
How would I go on doing this?
Try that way:
$companies = Company::whereHas('employees', function($q){
$type = (!empty(Input::get('company_search_employee'))) ? '%' . Input::get('company_search_employee') . '%' : '%';
$q->where( 'first_name', 'LIKE', $type);
})->get();
or if it doesnt work add 'employees' to where clause:
$q->where('employees.first_name', 'LIKE', $type);

Laravel 4 - get json list one table with relations

I have a table customers and a table orders.
And I want to make an ajax call to get a list of all customers with name x and their orders.
I got the ajax function and my function to retrieve the customer looks like this:
$customers = Customer::where('first_name', 'LIKE', '%'.$name.'%')
->orWhere('last_name', 'LIKE', '%'.$name.'%')
->where('user_id', Auth::user()->id)
->get()
->toJson();
return $customers;
With this I can only get all customers, can I modify this to get also their orders?
I think I could do it with join or is there any magic function?
Thanks
You can use relationship: follow the link and create one to many relationship and use the relationship in your query.
http://laravel.com/docs/eloquent#relationships
$customers = Customer::with('orders')
->where('first_name', 'LIKE', '%'.$name.'%')
->orWhere('last_name', 'LIKE', '%'.$name.'%')
->where('user_id', Auth::user()->id)
->get();
return $customers->toJson();

Categories