Laravel Parameter Grouping (and/or where) - laravel

I upgraded Laravel to version 7, and when I do a query like this:
$users = User::where('name', '=', 'John')
->where(function ($query) {
$query->where('votes', '>', 100)
->orWhereNull('title');
})
->get();
it doesn't work as expected, and I got this error [SQL Server] Must specify table to select from
because the SQL should be like this:
select * from users where name = 'John' and (votes > 100 or title is null)
but when I debug the returned query it shows like this:
select * from users where name = 'John' and (select * votes > 100 or title is null) is null
The above query it just an example of my complex query, and I have a lot like this query in all of my project so I don't need a replacement, I just need to know how to fix it as it worked fine before upgrading

You can use whereRaw for the alternative method, for example
$users = Table::whereRaw(" name=? AND (votes=? OR title=?)", array(?,?,?))

$users = User::where('name', '=', 'John')
->where(function ($query) {
$query->from('users')
->where('votes', '>', 100)
->orWhereNull('title');
})
->get();

Related

Eloquent with query on relations with nested WHERE

I'm struggling with Eloquent with query on relation.
For example, I'm looking for only the client John who doesn't have transaction.
How can I do this with Eloquent?
Client model relation
public function transactions()
{
return $this->hasMany(Transaction::class);
}
$results = Client::whereDoesntHave('transactions', function ($query) use ($inputFirst, $period) {
$query->where('transactions.period_id', '=', $period->id)
->where('firstname', '=', $inputFirst);
})
->orderBy('id', 'desc')
->get();
A little help would be great.
Thanks
The issue with your code is that you are nesting the statements. The way you are doing Laravel is generating a SQL like this:
select * from `clients` where not exists
(select * from `transactions`
where `clients`.`id` = `transactions`.`client_id`
and `name` = John)
But the actual SQL code you're looking for is:
select * from `clients` where not exists
(select * from `transactions`
where `clients`.`id` = `transactions`.`client_id`)
and `name` = John)
For that your code should be:
$results = Client::whereDoesntHave('transactions')
->where('firstname', '=', $inputFirst)
->orderBy('id', 'desc')
->get();
*I didn't include the transactions.period_id, coz I wasn't sure if where you're looking to have it. But if it's meant to be inside the second select, leave in the nested statement, if not leave outside.

How to make query at laravel

I have sql like this
SELECT no_po FROM tpo_suppheader WHERE no_po not in (
SELECT no_po FROM tpo_supp_stok ).
How to code at laravel?
$datas = DB::table ('tpo_suppheader')
->join('tpo_suppdetil', 'tpo_suppheader.no_po', '=', 'tpo_suppdetil.no_po')
->join('tmsupplier', 'tpo_suppheader.suppid', '=', 'tmsupplier.id')
->select('tpo_suppheader.*', 'tpo_suppdetil.*', 'tmsupplier.nama_supp')
->where('tpo_suppheader.no_po','like',"%".$var_cari."%")
->whereNotIn('no_po', $data_dtl)
->get();
$datas = DB::table ('tpo_suppheader')
->join('tpo_suppdetil', 'tpo_suppheader.no_po', '=', 'tpo_suppdetil.no_po')
->join('tmsupplier', 'tpo_suppheader.suppid', '=', 'tmsupplier.id')
->select('tpo_suppheader.*', 'tpo_suppdetil.*', 'tmsupplier.nama_supp')
->where('tpo_suppheader.no_po','like',"%".$var_cari."%")
->whereNotIn('no_po', $data_dtl)
->get();
This will work for every query: use toSql() to know how the builder translates a query.
For example:
dd(DB::table('table1')->...->toSql());
will dump the translated query. Tinker with this until you get the desired result. Using this method of trial and error, I got this:
$query = DB::table('tpo_suppheader')->select('no_po') # SELECT no_po FROM tpo_suppheader
->whereNotIn('no_po', function ($subquery) { # WHERE no_po NOT IN (
$subquery->select('no_po')->from('tpo_supp_stok'); # SELECT no_po FROM tpo_supp_stok
}); # )
dd($query->toSql());
# "select "no_po" from "tpo_suppheader" where "no_po" not in (select "no_po" from "tpo_supp_stok")"
$results = $query->get();

Convert SQL query into Laravel query builder

I need to convert this query into laravel query builder
select * from employee where(( age = 25 and salary = 20000) or (age =30 and salary = 30000))
If you want to group where clauses you can nest them inside closures:
DB::table('employee')
->where(function ($query) {
$query->where('age', 25)->where('salary', 20000);
})
->orWHere(function ($query) {
$query->where('age', 30)->where('salary', 30000);
})
->get();
For more information have a look at Parameter Grouping in the documentation.
Can you try this,
$data = Model::where([["age", "25"],["salary", "20000"]])
->orWhere([["age", "30"],["salary", "30000"]])
->get();

Why query returns empty collection?

I have the following query was built by Laravel:
$res = Announcement::whereExists(function ($query) {
$query->select(DB::raw(1))
->from('announcement_category')->join('user_category', 'user_category.category_id', '=', 'announcement_category.category_id')
->where('user_category.user_id', '=', 1)
->where('announcement_category.announcement_id', '=', 'announcements.id');
});
dd($res->get());
The code above gives me empty collection: dd($res->get());.
The plain SQL code of this query is:
select * from `announcements` where exists (select 1 from
`announcement_category` inner join `user_category` on
`user_category`.`category_id` = `announcement_category`.`category_id` where `user_category`.`user_id` = 1
and `announcement_category`.`announcement_id` = announcements.id)
and `announcements`.`deleted_at` is null
If execute this directly in MySQL, I get two result rows.
But why dd($res->get()); retuns me empty?
I don't think there is a whereExists in eloquent model... try this:
$res = DB::table('announcement')->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('announcement_category')->join('user_category', 'user_category.category_id', '=', 'announcement_category.category_id')
->where('user_category.user_id', '=', 1)
->where('announcement_category.announcement_id', '=', 'announcements.id');
})->get();

Laravel eloquent and relationship

I have a code:
$response = $this->posts
->where('author_id', '=', 1)
->with(array('postComments' => function($query) {
$query->where('comment_type', '=', 1);
}))
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
And when I logged this query with:
$queries = \DB::getQueryLog();
$last_query = end($queries);
\Log::info($last_query);
In log file I see follow:
"select * from `post_comments` where `post_comments`.`post_id` in (?, ?, ?, ?) and `comment_type` <> ?"
Why is the question mark for comment_type in the query?
Update #1:
I replaced current code with following and I get what I want. But I'm not sure it is OK. Maybe exists many better, nicer solution.
$response = $this->posts
->where('author_id', '=', 1)
->join('post_comments', 'post_comments.post_id', '=', 'posts.id')
->where('comment_type', '=', 1)
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
Behind the scene the PDO is being used and it's the way that PDO does as a prepared query, for example check this:
$title = 'Laravel%';
$author = 'John%';
$sql = "SELECT * FROM books WHERE title like ? AND author like ? ";
$q = $conn->prepare($sql);
$q->execute(array($title,$author));
In the run time during the execution of the query by execute() the ? marks will be replaced with value passed execute(array(...)). Laravel/Eloquent uses PDO and it's normal behavior in PDO (PHP Data Objects). There is another way that used in PDO, which is named parameter/placeholder like :totle is used instead of ?. Read more about it in the given link, it's another topic. Also check this answer.
Update: On the run time the ? marks will be replaced with value you supplied, so ? will be replaced with 1. Also this query is the relational query, the second part after the first query has done loading the ids from the posts table. To see all the query logs, try this instead:
$queries = \DB::getQueryLog();
dd($queries);
You may check the last two queries to debug the queries for the following call:
$response = $this->posts
->where('author_id', '=', 1)
->with(array('postComments' => function($query) {
$query->where('comment_type', '=', 1);
}))
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
Update after clarification:
You may use something like this if you have setup relation in your Posts model:
// $this->posts->with(...) is similar to Posts::with(...)
// if you are calling it directly without repository class
$this->posts->with(array('comments' =. function($q) {
$q->where('comment_type', 1);
}))
->orderBy('created_at', 'DESC')->limit($itemno)->get();
To make it working you need to declare the relationship in your Posts (Try to use singular name Post if possible) model:
public function comments()
{
return $this->hasmany('Comment');
}
Your Comment model should be like this:
class Comment extends Eloquent {
protected $table = 'post_comments';
}

Resources