I would like to create a dynamic query with laravel ORM. In my example:
$query = Task::orderBy('customer_family_name');
if (array_key_exists('date', $request->filter)) {
if($request->filter['date'] != null) {
$query = $query->whereDate(....);
}
}
..... more filters
if (array_key_exists('search', $request->filter)) {
if(strlen($request->filter['search']) > 1) {
$searchText = $request->filter['search'];
$query = $query->where('customer_family_name', $searchText.'%');
}
}
}
$Tasks = $query->get();
But it is not working as expected. I thought that laravel will push all where elements, so that ALL where clauses must be met.
But the search for a family name is not working. Do i something wrong? How can i combine it correct? Is there a way to display the SQL Query with orm?
Edit: ok found out that is another problem, my query looks like:
"select * from [tasks] where [state_id] in (?, ?, ?, ?, ?, ?) and [customer_family_name] = ? and [regulator_user_id] in (?, ?, ?) or [regulator_user_id] is null order by [customer_family_name] asc"
And my filter:
if (array_key_exists('regulator', $request->filter)) {
$regulator_array = explode(",", $request->filter['regulator']);
if(is_array($regulator_array)) {
$query->whereIn('regulator_user_id', $regulator_array)->orWhereNull('regulator_user_id');
}
}
It looks like that the orWhereNull is the problem. I would like to return every row where
regulator_user_id is in that array
AND all where regulator_user_id IS NULL
But all other filters should work as expected.
How can i do that?
You should not be re-saving to your $query variable:
$query = $query->whereDate(....);
Change it to this (and for your other filters):
$query->whereDate(....);
Your final command :
$Tasks = $query->get();
Will then return the proper data according to the extra queries that were added to your $query object.
Ok ill found the correct solution:
Ill need my own query function to combine an Where with an orWhereNull:
if(is_array($regulator_array)) {
$query = $query->where(function($query) use($regulator_array) {
$query->whereIn('regulator_user_id', $regulator_array)
->orWhereNull('regulator_user_id');
});
}
Now the query works fine. Thanks for all the answers.
As mentioned from Jhonny Walker you shouldn't save every time the $query but use the $query->where each time.
One line before the $Tasks = $query->get();
try to debug the mysql command that is generated with the following command.
dd($query->toSql());
If you want paste the result in order to help you with the where clauses.
Or you can use this sweet eloquent method when(), so you don't have to use if statement and break the method chaining, instead you got fluent query building.
Eloquent when()
Related
Laravel Version: 5.6.39
PHP Version: 7.1.19
Database Driver & Version: mysql 5.6.43
Description:
When I chain where and orWhere in a model accessor to count related model , I get wrong result and here is my query. the count is returned strange result without filtering by the calling event id,
class Event extends Model
{
protected $table = 'events';
public function registrations()
{
return $this->hasMany('App\Components\Event\Models\Registration','event_id','id');
}
public function getSeatsBookedAttribute()
{
return $this->registrations()
->where('reg_status','=','Confirmed')
->orWhere('reg_status','=','Reserved')
->count();
}
}
Steps To Reproduce:
the following queries return me the expected results, however In my knowledge the first query should return the same result if i am not wrong, so i think this is a potential bug.
class Event extends Model
{
public function getSeatsBookedAttribute()
{
return $this->registrations()
->whereIn('reg_status', ['Confirmed', 'Reserved'])
->count();
}
}
class Event extends Model
{
public function getSeatsBookedAttribute()
{
return $this->registrations()
->where(function($query){
$query->where('reg_status','Confirmed')
->orWhere('reg_status','Reserved');
})
->count();
}
}
and here is the query dump,
here is the query when I donot explicit group it.
"select count(*) as aggregate from events_registration where (events_registration.event_id = ? and events_registration.event_id is not null and reg_status = ? or reg_status = ?) and events_registration.deleted_at is null "
and here is the query when i group it explicitly,
select count(*) as aggregate from events_registration where events_registration.event_id = ? and events_registration.event_id is not null and (reg_status = ? or reg_status = ?) and events_registration.deleted_at is null
The reason this happens is because you're chaining where() and orWhere(). What you don't see behind the scenes is a where event_id = :event_id applying to your query. You end up with a query that looks something like this:
select * from registrations where event_id = :event_id and reg_status = 'Confirmed' or reg_status = 'Reserved'
In normal SQL you'd want to put the last 2 conditions in parentheses. For Eloquent, you'd need to do something like this:
return $this->registrations()->where(function ($query) {
$query->where('reg_status', 'Confirmed')
->orWhere('reg_status', 'Reserved');
});
You can chain the toSql() method on these chains to see the difference. Note, that in this case, I believe whereIn() is the semantically correct thing to do.
Eloquent can handle this for you, though; scroll down to "Counting Related Models" in the Querying Relations part of the Eloquent Relationships docs:
$posts = App\Event::withCount([
'registrations as seats_booked_count' => function ($query) {
$query->where('reg_status','Confirmed')
->orWhere('reg_status','Reserved');
}
])->get();
I'm using Eloquent Query Builder to produce raw sql for me. My goal is to return a sql query like this
select * from accounts where id = '120'
now i tried
$query = \App\Account::query();
$query = $query->where('id', 120);
dd($query->toSql());
Output:
select * from `accounts` where `id` = ?
is there a way to display the value instead of (?) because this is not a valid query on the third party API that i'm using.
I'm expecting to produce a query on my first example that i give.
Note: I'm not querying on my database, i will use that raw query to pass on the 3rd party API. All i want is to produce only a raw sql. Any suggestion would be appreciated. Thanks!
Yes, you can print the parameters value by using getBindings()
print_r( $query->getBindings() );
Array
(
[0] => instructor
[1] => 1
)
Found a solution for this, maybe it can help to someone that has same problem with me.
You can check this link: https://gist.github.com/JesseObrien/7418983
public function getSql()
{
$builder = $this->getBuilder();
$sql = $builder->toSql();
foreach($builder->getBindings() as $binding)
{
$value = is_numeric($binding) ? $binding : "'".$binding."'";
$sql = preg_replace('/\?/', $value, $sql, 1);
}
return $sql;
}
I use this package : https://github.com/andersao/l5-repository
My user repository is like this :
public function getList($year)
{
$query = parent::findWhere(['year' => $year])
->orderBy('updated_at')
->paginate(10);
return $query;
}
When executed, there exist error like this :
Method orderBy does not exist.
Whereas I see in the documentation, there exist orderBy
How can I solve it?
If you'll look into the findWhere() method, you'll see that it executes query:
$model = $this->model->get($columns);
So, you'll not be able to use orderBy() or paginate() after findWhere().
findWhere exec the query and get your values using get().
try to change your code to this:
public function getList($year)
{
$query = parent::where('year', '=>', $year)
->orderBy('updated_at')
->paginate(10);
return $query;
}
use something like that for compound searches
$posts = $this->repository->scopeQuery(function($query){
return $query->orderBy('name','asc');
})->findByField('user_id',$dataForm['user_id']);
I am trying to do this
select notifications.id, reservations.number from
notifications
JOIN reservations
ON notifications.reservation_id = reservations.id
WHERE notifications.status = 1
using eloquent so I have this this
$await = Notification::with('Reservation')->
select('notifications.id', 'reservations.number')
->where('notifications.status', '=', 1)->get();
return Response::json($awaitLists);
In my Notification model
public function Reservation() {
return $this->belongsTO('Reservation');
}
In my Reservation Model
public function notification() {
return $this->hasMany('Notification');
}
So notification belongs to reservation while reservation has a 1 to many relationship
My question is why can't what I have tried works. I keep getting Unknown column 'reservation.number' but i do have column called number in the reservations table. I know they is a way to use eloquent relationship mapper to do this.
This should do it:
$notifications = Notification::where('status','=',1)->get();
foreach($notifications as $notification) {
$id = $notification->id;
$num = $notification->reservation->number;
$await = [$id,$num];
var_dump($await);
}
The error you're seeing is because eager loading relationships doesn't actually perform a join. It uses two separate queries, and then the relationship fields are assigned after the queries are run.
So, when you do Notification::with('Reservation')->get(), it is running two SQL statements, approximately:
Notification::with('Reservation')->get();
// select * from notifications;
// select * from reservations where id in (?, ?, ...);
You can see the actual queries run with a dd(DB::getQueryLog()), if you're interested.
How you move forward depends on what you need to do. If you need to duplicate your existing query exactly, then you'll need to manually perform the joins.
$notifications = Notification::select('notifications.id', 'reservations.number')
->join('reservations', 'notifications.reservation_id', '=', 'reservations.id`)
->where('notifications.status', '=', 1)
->get();
foreach($notifications as $notification) {
print_r($notification->number);
}
Otherwise, you can just use the objects as they are built by Laravel:
$notifications = Notification::with('Reservation')->where('status', '=', 1)->get();
foreach($notifications as $notification) {
print_r($notification->Reservation->number);
}
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';
}