My laravel eloquent is like this :
public function search(Request $request)
{
$search = '';
$searchdate = '2017-04-27';
$reports = Report::whereHas('user', function($query) use ($search)
{
$query->where(function ($q) use($search)
{
$q->where('name' ,'LIKE' ,'%'.$search.'%');
});
})->orWhereHas('store', function($query) use ($search){
$query->where(function ($q) use($search)
{
$q->where('name', 'LIKE', '%'.$search.'%');
});
})->orWhereHas('category', function($query) use ($search)
{
$query->where(function ($q) use($search)
{
$q->where('name', 'LIKE' , '%'.$search.'%');
});
})->whereDate('created_at','=',$searchdate)->paginate(10);
}
When executed, the result is not match with the filter $searchdate
How can I solve it?
Update :
I debug the query, the result is like this :
SELECT *
FROM `reports` WHERE (
EXISTS (SELECT * FROM `users` WHERE `reports`.`user_id` = `users`.`id` AND (`name` LIKE '%%') AND `users`.`deleted_at` IS NULL)
OR EXISTS (SELECT * FROM `stores` WHERE `reports`.`reportable_id` = `stores`.`id` AND (`name` LIKE '%%') AND `stores`.`deleted_at` IS NULL)
OR EXISTS (SELECT * FROM `categories` WHERE `reports`.`category_id` = `categories`.`id` AND (`name` LIKE '%%') AND `categories`.`deleted_at` IS NULL)
AND DATE(`created_at`) = '2017-04-27'
)
AND `reports`.`deleted_at` IS NULL
Seems it does not work. Because
AND DATE(created_at) = '2017-04-27'
are inside the round brackets
So, it must change like this :
SELECT *
FROM `reports` WHERE (
EXISTS (SELECT * FROM `users` WHERE `reports`.`user_id` = `users`.`id` AND (`name` LIKE '%%') AND `users`.`deleted_at` IS NULL)
OR EXISTS (SELECT * FROM `stores` WHERE `reports`.`reportable_id` = `stores`.`id` AND (`name` LIKE '%%') AND `stores`.`deleted_at` IS NULL)
OR EXISTS (SELECT * FROM `categories` WHERE `reports`.`category_id` = `categories`.`id` AND (`name` LIKE '%%') AND `categories`.`deleted_at` IS NULL)
)
AND DATE(`created_at`) = '2017-04-27'
AND `reports`.`deleted_at` IS NULL
Which is my problem is how to change in eloquent laravel?
change the query to
$reports = Report::where(function($q) use ($search){
$q->whereHas('user', function($query) use ($search) {
$query->where(function ($q) use($search){
$q->where('name' ,'LIKE' ,'%'.$search.'%');
});
})->orWhereHas('store', function($query) use ($search){
$query->where(function ($q) use($search){
$q->where('name', 'LIKE', '%'.$search.'%');
});
})->orWhereHas('category', function($query) use ($search){
$query->where(function ($q) use($search){
$q->where('name', 'LIKE' , '%'.$search.'%');
});
});
})->whereDate('created_at','=',$searchdate)->paginate(10);
Hope it helps
Related
i have four table socket , office , container, project i need to join these table with primey table soket and add count in each table group by socket.id .
$data['socjetsreport'] = DB::table('socket')
->limit(5)
->join('attached', 'attached.socket_id', '=', 'socket.id', 'left outer')
->join('office', 'office.socket_id', '=', 'socket.id', 'left outer')
->join('container', 'container.socket_id', '=', 'socket.id', 'left outer')
->join('project', 'project.socket_id', '=', 'socket.id', 'left outer')
->join('users', 'users.id', '=', 'socket.employee_id', 'left outer')
->select('socket.id as id', 'socket.name as name', 'users.name as uname',
DB::raw("count(attached.socket_id) as attccount"))
->groupBy('socket.id')
->get();
I try to apply it in MySQL query and it run well but in laravel it just give me wrong count.
This is my MySQL query:
SELECT
users.name,
COUNT(*) AS attached,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN container ON socket.id = container.socket_id
WHERE socket.id = #id
) AS container,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN project ON socket.id = project.socket_id
WHERE socket.id = #id
) AS project,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN office ON socket.id = office.socket_id
WHERE socket.id = #id
) AS office
FROM socket
LEFT OUTER JOIN attached ON socket.id = attached.socket_id
LEFT OUTER JOIN users ON socket.employee_id = users.id WHERE socket.id = #id
GROUP BY users.name
Since LEFT OUTER JOIN is the same as LEFT JOIN you could just use the query builder's leftJoin() method.
For all the subqueries in your SELECT statement, you should use the query builder's selectSub() method.
/**
* Add a subselect expression to the query.
*
* #param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query
* #param string $as
* #return $this
*
* #throws \InvalidArgumentException
*/
public function selectSub($query, $as)
{
[$query, $bindings] = $this->createSub($query);
return $this->selectRaw(
'('.$query.') as '.$this->grammar->wrap($as), $bindings
);
}
The resulting query looks like this:
use Illuminate\Database\Query\Builder;
$data['socjetsreport'] = DB::table('socket')
->select('users.name')
->selectRaw('count(*) as attached')
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('container', 'socket.id', 'container.socket_id')
->where('socket.id', $socketId);
},
'container'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('project', 'socket.id', 'project.socket_id')
->where('socket.id', $socketId);
},
'project'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('office', 'socket.id', 'office.socket_id')
->where('socket.id', $socketId);
},
'office'
)
->leftJoin('attached', 'socket.id', 'attached.socket_id')
->leftJoin('users', 'socket.employee_id', 'users.id')
->where('socket.id', $socketId)
->groupBy('users.name')
->get();
Or like this if you are hung up on using LEFT OUTER JOIN.
use Illuminate\Database\Query\Builder;
$data['socjetsreport'] = DB::table('socket')
->select('users.name')
->selectRaw('count(*) as attached')
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('container', 'socket.id', '=', 'container.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'container'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('project', 'socket.id', '=', 'project.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'project'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('office', 'socket.id', '=', 'office.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'office'
)
->join('attached', 'socket.id', '=', 'attached.socket_id', 'left outer')
->join('users', 'socket.employee_id', '=', 'users.id', 'left outer')
->where('socket.id', $socketId)
->groupBy('users.name')
->get();
PHP 7.4's shorthand closures can make those selectSub statements look a bit smaller
->selectSub(
fn(Builder $query) => $query->selectRaw('count(*)')->from('socket')
->leftJoin('container', 'socket.id', 'container.socket_id')
->where('socket.id', $socketId),
'container'
)
You can try checking the SQL for yourself by replacing ->get() with toSql() and dumping the result.
I solve it by using this query
$socjetsreport = DB::table('socket')
->select("socket.id", "socket.name",
DB::raw("(SELECT COUNT(id) AS attchedcount FROM attached WHERE attached.socket_id=socket.id) as attchedcount"),
DB::raw("(SELECT COUNT(id) AS officecount FROM office WHERE office.socket_id=socket.id) as officecount"),
DB::raw("(SELECT COUNT(id) AS containercount FROM container WHERE container.socket_id=socket.id) as containercount"),
DB::raw("(SELECT COUNT(id) AS projectcount FROM project WHERE project.socket_id=socket.id) as projectcount"),
DB::raw("(SELECT users.name AS employname FROM users WHERE users.id=socket.employee_id) as employname")
)
->orderBy('socket.id', 'asc')
->get();
I have a query that should load all the posts with just their english translation.
If the user enter a keyword it return just the english post with a title containing that keyword.
if ($searchKeywords||$searchCategory){
$posts = Post::
select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->where(‘post_translations.locale','=','en')
->when($searchKeywords, function ($query, $searchKeywords) {
return $query->where('post_translations.title', $searchKeywords)->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
})
->when($searchCategory, function ($query, $searchCategory) {
return $query->where('category_id', '=', $searchCategory);
->paginate(20);
}
else
$posts = Post::select('id', 'title', 'category_id')->orderBy('title')->paginate(20);
The generated query is this one:
SELECT `post_translations`.`post_id` AS `id`, `post_translations`.`title` AS `title`, `category_id`
FROM `posts` inner join `post_translations`
ON `posts`.`id` = `post_translations`.`post_id`
WHERE `post_translations`.`locale` = 'en'
AND `post_translations`.`title` = 'About'
OR `post_translations`.`title` like 'About’
LIMIT 20 OFFSET 0
That return me all the 3 posts translations of the post About.
This because of the orWhere.
How can I change the eloquent query in order to generate a query like this?
SELECT `post_translations`.`post_id` AS `id`, `post_translations`.`title` AS `title`, `category_id`
FROM `posts` inner join `post_translations`
ON `posts`.`id` = `post_translations`.`post_id`
WHERE `post_translations`.`locale` = 'en'
AND (`post_translations`.`title` = ‘About' OR `post_translations`.`title` like 'About’ )
LIMIT 20 OFFSET 0
The question is not a duplicate of this one because I have one more level of subquery.
How do you wrap Laravel Eloquent ORM query scopes in parentheses when chaining?
Add both condition inside a where query like this:
if ($searchKeywords) {
$posts = Post::select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->where(‘post_translations.locale','=','en')
->where(function ($query) use ($searchKeywords) {
$query->where('post_translations.title', $searchKeywords)
->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
})
->paginate(20);
}
I have solved with this code:
if ($searchKeywords||$searchCategory){
$posts = Post::
select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->when($searchKeywords, function ($query, $searchKeywords) {
return $query->where('post_translations.locale','=','en')
->where(function ($query) use ($searchKeywords) {
$query->where('post_translations.title', $searchKeywords)->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
});
})
->when($searchCategory, function ($query, $searchCategory) {
return $query->where('post_translations.locale','=','en')
->where(function ($query) use ($searchCategory) {
$query->where('category_id', '=', $searchCategory);
});
})
->paginate(20);
}
else
$posts = Post::select('id', 'title', 'category_id')->orderBy('title')->paginate(20);
This is my filter query function
$keywords = [];
foreach($columns as $key => $value){
$keywords[] = [$key, 'LIKE', '%'.$value.'%'];
}
$query= $this->model ->orderBy('name', 'asc')->where('is_deleted', 0)->Where($keywords);
if($status=="yes")
$query= $query->where('status',1);
$query= $query->get();
return $query;
For the above function, i got the following query
select * from stores where is_deleted = 0 and status = 1 AND (name LIKE %r% AND address LIKE %r%) order by name asc
But i need Or instead of ANd in the like query
select * from `stores` where `is_deleted` = 0 and `status` = 1 AND (`name` LIKE %r% or `address` LIKE %r%) order by `name` asc
Please tell in which place i need to change?
You can use Where and orWhere.
$query= $this->model->orderBy('name', 'asc')->where('is_deleted', 0)->
orWhere($key, 'LIKE', '%'.$value.'%');
You have to group those orWhere() queries in one where() clause
$query = $this->model->where([
['is_deleted' => 0],
['status' => $status]
])
->where(function ($query) use($columns) {
foreach($columns as $key => $value) {
$query->orWehre($key, 'like', "%$value%");
}
})
->orderBy('name');
I have the following function
public function index(Request $request)
{
$results = Service::whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})
->whereHas('locations', function ($query) use ($request){
$ran = false;
foreach($request->locations as $location)
{
if(!$ran)
{
$query->Where('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
else
{
$query->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
$ran = true;
}
})
->with('types', 'contacts', 'locations.datetimes')->toSql();
dd($results);
}
which produces
select * from `services` where exists
(select * from `conditions` inner join `condition_service` on `conditions`.`id` = `condition_service`.`condition_id` where `services`.`id` = `condition_service`.`service_id` and `id` in (13, 14) and `conditions`.`deleted_at` is null)
and exists
(select * from `service_locations` inner join `service_location_service` on `service_locations`.`id` = `service_location_service`.`service_location_id` where `services`.`id` = `service_location_service`.`service_id` and
(`town` like 'manchester' or `city` like 'manchester' or `town` like 'crewe' or `city` like 'crewe')
and `service_locations`.`deleted_at` is null) and `services`.`deleted_at` is null
The problem is the whereHas on locations brings back all results, whereas I only want the result where the town/city is a match. Whats the best way to achieve this?
i think you need to group your location query using the callback which wraps the query in () and just process the first location directly remove it from array and iterate over loop. so below code should work in your case.
$results = Service::whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})
->whereHas('locations', function ($query) use ($request){
$locations = $request->locations;
$query->where(function($builder) use($locations){
$builder->where('town', 'like', '%' . $locations[0] . '%')
->orWhere('city', 'like', '%'.$locations[0].'%');
unset($locations[0]); //remove first item as it is processed
foreach($locations as $location) {
$builder->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
});
});
let me know if you find any problem with it. :)
I managed to figure it out late last night.
I moved the with() to the top of the query and made a callback on locations.
public static function newGetSearchResults($request)
{
return Service::with(['types', 'contacts', 'locations.datetimes', 'locations' => function($query) use($request) {
$ran = false;
foreach($request->locations as $location)
{
if(!$ran)
{
$query->Where('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
else
{
$query->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
$ran = true;
}
}])
->whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})->get();
}
I'm currently attempting to create a nested query, as follows:
public function getChallenge($user_id, $opponent_id)
{
$challenge = $this->challenges()
->where('open', true)
->where(function($query) use ($user_id, $opponent_id) {
$query->where('player_1', $user_id)
->where('player_2', $opponent_id);
})
->orWhere(function($query) use ($opponent_id, $user_id) {
$query->where('player_1', $opponent_id)
->where('player_2', $user_id);
})
->first();
return $challenge;
}
This creates the following query for example:
select * from `site_challenges_leagues`
where `site_challenges_leagues`.`league_id` = '1'
and `open` = '1'
and (`player_1` = '3' and `player_2` = '1')
or (`player_1` = '1' and `player_2` = '3')
limit 1
However, this always returns the first value in the table (where open is either 1 or 0), which is incorrect. For the query to be correct, it needs to contain both sets of AND queries in brackets, as follows:
select * from `site_challenges_leagues`
where `site_challenges_leagues`.`league_id` = '1'
and `open` = TRUE
and ((`player_1` = '3' and `player_2` = '1')
or (`player_1` = '1' and `player_2` = '3'))
limit 1
Is it possible to do this in Laravel? I attempted to do this; however, it failed:
public function getChallenge($user_id, $opponent_id)
{
$challenge = $this->challenges()
->where('open', true)
->where(function($q) use ($user_id, $opponent_id) {
$q->where(function($query) {
$query->where('player_1', $user_id)
->where('player_2', $opponent_id);
})
->orWhere(function($query) {
$query->where('player_1', $opponent_id)
->where('player_2', $user_id);
})
})
->first();
return $challenge;
}
Any help is greatly appreciated.
You were very close to the answer
$challenge = $this->challenges()
->where('open', true)
->where(function($q) use ($user_id, $opponent_id) {
$q->where(function($query) use ($opponent_id, $user_id){
$query->where('player_1', $user_id)
->where('player_2', $opponent_id);
})
->orWhere(function($query) use ($opponent_id, $user_id) {
$query->where('player_1', $opponent_id)
->where('player_2', $user_id);
});
})
->first();
Here are the differences between two codes
I needed the nested wheres to search for a user by multiple fields:
$users_query->where('company_uuid', '=', $company_uuid);
$users_query->where('is_active', '=', true);
$users_query->whereNested(function($query) use ($search){
/** #var Builder $query */
$query
->where('first_name', 'like', '%' . $search . '%')
->orWhere('last_name', 'like', '%' . $search . '%')
->orWhere('email', 'like', '%' . $search . '%');
});
Supposing you want to stack where layers without losing them on using orWhere
Wouldn't this be the same thing ?
$challenge = $this->challenges()
->where('open', true)
->whereNested(function($q) use ($user_id, $opponent_id) {
$query->where('player_1', $user_id)
->where('player_2', $opponent_id);
$query->orWhere('player_1', $opponent_id)
->where('player_2', $user_id);
})->first();
You can use where then additional where and pass conditions as arrays.
Between two where is 'And' operation.
Inside the second where is 'Or' operation.
Example:
$users = DB::table('users')->where('status', 'value')->where([
['age', '>', '10'],
['subscribed', '<>', '1'],
])->get();