Laravel whereRelation() not filtring the query - php

I need to select all offers where the merchant has status open and dd_merchant set to 1. The match between merchant and offer is done using a col named mid.
In my offer model I have the following:
public function merchants()
{
return $this->belongsTo(Merchant::class,'mid','mid');
}
My query looks like this:
$data['offers']= Offer::whereRelation('merchants', 'status', 'Open')
->whereRelation('merchants', 'dd_merchant', '1')
->where('status', '=', 'Loaded')
->where("start", "<=", date("Y-m-d"))
->where("end", ">=", date("Y-m-d"))
->where('category', '=', $request->category)
->orderBy('title','ASC')
->paginate(20);
For the sake of this test, I have one merchant set to status 'Closed' and has dd_merchant set to 0 in the database.
The query returned by this is
select * from `offers` where (exists (select * from `merchants` where `offers`.`mid` = `merchants`.`mid` and `status` = 'Open' and `merchants`.`deleted_at` is null) and exists (select * from `merchants` where `offers`.`mid` = `merchants`.`mid` and `dd_merchant` = '1' and `merchants`.`deleted_at` is null) and `status` = 'Loaded' and `start` <= '2022-10-03' and `end` >= '2022-10-03' ) and `offers`.`deleted_at` is null order by `title` asc limit 20 offset 0
For some reason, I am still getting offers from that merchant with the above query. Am I assuming wrong that whereRelation() filters the offers based on the condition on the second table? If so, how can I achieve my goal?
In one of the 27 variants of this I tried so fare, I used the wherehas() like this:
$data['offers']= Offer::
whereHas('merchants', function (Builder $query) {
$query->where('dd_merchant', '=', '1')->where('status', '=', 'Open');
})
->where('status', '=', 'Loaded')
->where("start", "<=", date("Y-m-d"))
->where("end", ">=", date("Y-m-d"))
->where('category', '=', $request->category)
->orderBy('title','ASC')
->paginate(20);
The above made a lot more sense to me but I get the same result ... I get offers that should not be there.

Related

Create subquery in NOT IN

I am using Laravel Framework 6.16.0.
I have the following sql query:
SELECT DISTINCT
`companies`.*
FROM
`companies`
LEFT JOIN `trx` ON `trx`.`companies_id` = `companies`.`id`
WHERE
`trx`.`transaction_date` >= 2020-11-12 AND companies.symbol NOT IN (SELECT DISTINCT
companies.symbol
FROM
`companies`
LEFT JOIN articles a ON a.companies_id = companies.id
WHERE
a.created_at >= 2020-11-12
ORDER BY
created_at
DESC)
ORDER BY
transaction_date
DESC
I have created the following eloquent query:
DB::connection('mysql_prod')->table('companies')->select('companies.symbol')
->leftJoin('trx', 'trx.companies_id', '=', 'companies.id')
->where('trx.transaction_date', '>=', Carbon::today()->subDays(1)->startOfDay())
->orderBy('transaction_date', 'desc')
->distinct()
->get('symbol');
However, I am not sure how to pack the in my eloquent query to get all the symbol back that should be excluded.
I highly appreciate your replies!
You should try something like this:
$date = Carbon::today()->subDays(1)->startOfDay();
DB::connection('mysql_prod')->table('companies')->select('companies.symbol')
->leftJoin('trx', 'trx.companies_id', '=', 'companies.id')
->where('trx.transaction_date', '>=', $date)
->whereNotIn('companies.symbol', function ($q) use ($date) => {
$q->select('companies.symbol')
->from('companies')
->leftJoin('articles', 'articles.companies_id', 'companies.id')
->where('articles.created_at', '>', $date)
->distinct()
->get()
})
->orderBy('transaction_date', 'desc')
->distinct()
->get();
It will provide a similar query as you mentioned.
Reference from here.
Also, you can read how to write sub Query from Laravel docs.
Check this one more good answer for that what you need.

Laravel 5.4: Converting a raw SQL query in Laravel Eloquent

I'm trying to rewrite this SQL query but I'm stuck at this point
The query is meant to join the projects table to the project_progress table by using a sub-query to only join on the latest entry
SELECT * FROM projects
JOIN project_progress ON project_progress.id =
(
SELECT id FROM project_progress
WHERE project_progress.project_id = projects.id
ORDER BY project_progress.created_at DESC
LIMIT 1
)
WHERE project_progress.next_action_date < NOW()
AND projects.status != 'Complete'
AND projects.member_id = 1
ORDER BY projects.title ASC
To:
$projects = App\Project::where('member_id', 1)
->join('project_progress', function ($join) {
$join->on('project_progress.id', '=', function ($query) {
$query->select('project_progress.id')
->from('project_progress')
->where('project_progress.project_id', 'projects.id')
->orderBy('project_progress.created_at', 'desc')
->limit(1);
});
})
->where('project_progress.next_action_date', '<', Carbon\Carbon::now())
->notCompleted()
->orderBy('projects.project_title', 'asc')
->get();
I think some thing is wrong with this line but I'm not sure how to write it
$join->on('project_progress.id', '=', function ($query) {
ErrorException (E_ERROR) strtolower() expects parameter 1 to be string, object given \vendor\laravel\framework\src\Illuminate\Database\Grammar.php
Use where():
$join->where('project_progress.id', '=', function ($query) {

How to do this in laravel, Subquery where count

I am trying to implement this query in Laravel
"SELECT * FROM jobs
WHERE status = 1 AND
taskerCount = (SELECT count(*) FROM jobRequests WHERE status = 1 AND job_id =
jobs.id)"
This is what i have tried;
Auth::user()->jobs
->where('status', 1)
->where('taskerCount', function ($q) {
$q->where('status', 1)
->where('job_id', $q->id)->count();
});
But I get the error:
Object of class Closure could not be converted to int.
Use a raw expression instead of -count().
You can view this documentation here
Example of a raw query from laravel:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
Make sure you define the count as a name.

convert SQL query to query builder style

Im trying days to understand how I can convert a SQL query to a query builder style in laravel.
My SQL query is:
$tagid = Db::select("SELECT `id` FROM `wouter_blog_tags` WHERE `slug` = '".$this->param('slug')."'");
$blog = Db::select("SELECT *
FROM `wouter_blog_posts`
WHERE `published` IS NOT NULL
AND `published` = '1'
AND `published_at` IS NOT NULL
AND `published_at` < NOW()
AND (
SELECT count( * )
FROM `wouter_blog_tags`
INNER JOIN `wouter_blog_posts_tags` ON `wouter_blog_tags`.`id` = `wouter_blog_posts_tags`.`tags_id`
WHERE `wouter_blog_posts_tags`.`post_id` = `wouter_blog_posts`.`id`
AND `id`
IN (
'".$tagid[0]->id."'
)) >=1
ORDER BY `published_at` DESC
LIMIT 10
OFFSET 0");
Where I now end up to convert to the query builder is:
$test = Db::table('wouter_blog_posts')
->where('published', '=', 1)
->where('published', '=', 'IS NOT NULL')
->where('published_at', '=', 'IS NOT NULL')
->where('published_at', '<', 'NOW()')
->select(Db::raw('count(*) wouter_blog_tags'))
->join('wouter_blog_posts_tags', function($join)
{
$join->on('wouter_blog_tags.id', '=', 'wouter_blog_posts_tags.tags_id')
->on('wouter_blog_posts_tags.post_id', '=', 'wouter_blog_posts.id')
->whereIn('id', $tagid[0]->id);
})
->get();
I have read that I can't use whereIn in a join. The error i now get:
Call to undefined method Illuminate\Database\Query\JoinClause::whereIn()
I realy dont know how I can convert my SQL to query builder. I hope when I see a good working conversion of my query I can understand how I have to do it next time.
This work for me:
DB::table('wouter_blog_posts')
->whereNotNull('published')
->where('published', 1)
->whereNotNull('published_at')
->whereRaw('published_at < NOW()')
->whereRaw("(SELECT count(*)
FROM wouter_blog_tags
INNER JOIN wouter_blog_posts_tags ON wouter_blog_tags.id = wouter_blog_posts_tags.tags_id
WHERE wouter_blog_posts_tags.post_id = wouter_blog_posts.id
AND id
IN (
'".$tagid."'
)) >=1")
->orderBy('published_at', 'desc')
->skip(0)
->take(10)
->paginate($this->property('postsPerPage'));
The following Query Builder code will give you the exact SQL query you have within your DB::select:
DB::table('wouter_blog_posts')
->whereNotNull('published')
->where('published', 1)
->whereNotNull('published_at')
->whereRaw('`published_at` < NOW()')
->where(DB::raw('1'), '<=', function ($query) use ($tagid) {
$query->from('wouter_blog_tags')
->select('count(*)')
->join('wouter_blog_posts_tags', 'wouter_blog_tags.id', '=', 'wouter_blog_posts_tags.tags_id')
->whereRaw('`wouter_blog_posts_tags`.`post_id` = `wouter_blog_posts`.`id`')
->whereIn('id', [$tagid[0]->id]);
})
->orderBy('published_at', 'desc')
->skip(0)
->take(10)
->get();
The subquery condition had to be reversed because you can't have a subquery as the first parameter of the where method and still be able to bind the condition value. So it's 1 <= (subquery) which is equivalent to (subquery) >= 1. The query generated by the above code will look like this:
SELECT *
FROM `wouter_blog_posts`
WHERE `published` IS NOT NULL
AND `published` = 1
AND `published_at` IS NOT NULL
AND `published_at` < Now()
AND 1 <= (SELECT `count(*)`
FROM `wouter_blog_tags`
INNER JOIN `wouter_blog_posts_tags`
ON `wouter_blog_tags`.`id` =
`wouter_blog_posts_tags`.`tags_id`
WHERE `wouter_blog_posts_tags`.`post_id` =
`wouter_blog_posts`.`id`
AND `id` IN ( ? ))
ORDER BY `published_at` DESC
LIMIT 10 offset 0
My process when creating more complex queries is to first create them and try them out in a SQL environment to make sure they work as indended. Then I implement them step by step with the Query Builder, but instead of using get() at the end of the query, I use toSql() which will give me a string representation of the query that will be generated by the Query Builder, allowing me to compare that to my original query to make sure it's the same.

Queries in Laravel Logic operators

I want to query Laravel DB for something like
SELECT * FROM `tickets`
WHERE (created_at > '2015-07-01'
AND created_at < '2015-07-31')
AND (state != 'Resolved'
OR state != 'closed'
OR state != 'Cancelled'
OR state != 'Solution Rejected')
ORDER BY `id` DESC
I tried using raw statements but doesn't seem to work. So I'm trying to use Laravel's own model functions to archive the same result, but I'm missing something... here's what I got so far.
Ticket::whereBetween('created_at', [$start, $end])
->whereIn('state',['Resolved','closed','Cancelled','Solution Rejected'])
->get();
PS: alternatively is there a way I can have this instead
SELECT * FROM `tickets`
WHERE ( (created_at > '2015-07-01'
AND created_at < '2015-07-31')
OR (updated_at > '2015-07-01'
AND updated_at < '2015-07-31') )
AND (state != 'Resolved'
OR state != 'closed'
OR state != 'Cancelled'
OR state != 'Solution Rejected')
ORDER BY `id` DESC
Try this one sir:
DB::table('tickets')
->whereBetween('created_at',[$start, $end])
->orWhere(function($query)
{
$query->where('state', '!=', 'Resolved')
->where('state', '!=', 'closed')
->where('state', '!=', 'Cancelled')
->where('state', '!=', 'Solution Rejected')
->orderBy('id', 'desc');
})
->get();

Categories