xatnys0820's avatar

Difference between $model->newQuery()->where and $model->where

Hi guys, I am currently creating my own project using Laravel 12 when I stumbled to this part.

At first, I was using this code:

abstract class Controller
{
    protected function isModelExists(Model $model, string $column, string $value): bool
    {
        $obj = $model->where($column, $value)->first();

        return $obj;
    }
}

This code works but, I just found out that I should use the newQuery() before doing the where clause because it will cause an error but it didn't. And the output whether I use newQuery() before doing the where clause or not is still the same. I still get the instance of the model.

I just wanted to ask if there is any downside of not using newQuery() or will it cause any issue in the near future.

Thanks!

0 likes
12 replies
Glukinho's avatar

I think this is because you have a model already retrieved from DB and it doesn't have query builder methods (like where() or first()). But newQuery() method gives you another query builder ready to retrieve data from database.

Snapey's avatar

What is the context ? Why do you need to reference $model in this way? Don't you know what type of model it might be?

martinbean's avatar

@xatnys0820 That method seems pretty pointless to me.

Why do you need to check a model exists? Eloquent models already have an exists method, and such a method certainly doesn’t belong in a controller.

The result is also needlessly assigned to a variable. Why? Just return the result:

- $obj = $model->where($column, $value)->first();

- return $obj;
+ return $model->where($column, $value)->first();

However, the first method doesn’t return a bool like the return type declares; first either returns a model instance if it exists, or null if it does not. So your types are completely wrong. Again, this is where you would use the exists method available on Eloquent models instead, which does return a bool result:

return $model->where($column, $value)->exists(); // returns bool and not Model|null

So, it would be much more helpful to us if you showed us code that used this method. Because otherwise it just looks like a method created by someone unfamiliar with the Laravel framework and its functionality, given Laravel offers features like route–model binding to look up models based on route parameters, etc.

1 like
xatnys0820's avatar

@martinbean my apologies for providing only the partial code. My only goal here was to confirm whether there was a difference between $model->newQuery->where and $model->where.

SilenceBringer's avatar
Level 55

Answering you question - there are no downsides. If you didn't call newQuery - laravel will do it for you under the hood. You can see it by digging into the code of Model class

    public function __call($method, $parameters)
    {
        // ...

        return $this->forwardCallTo($this->newQuery(), $method, $parameters);
    }

If the method doesn't exist (like method where in your case) - it will just forward call to QueryBulder

1 like
xatnys0820's avatar

Thank you so very much guys for all of your replies! I was able to find the answer that I was looking for because of it.

martinbean's avatar

Thank you so very much guys for all of your replies! I was able to find the answer that I was looking for because of it.

@xatnys0820 …and yet no one knows what you were trying to do, nor what solution you ended up with, making this entire thread pointless.

1 like
Snapey's avatar

So you didn't want to explain what you were doing, and you didn't want to explain what you did different as a result of this discussion.

Don't worry, your skills and confidence will increase.

3 likes
xatnys0820's avatar

Here is the whole scenario:

I have this ExepnseController (a basic Create, Read, Update, Delete, Restore, Force Delete):

And this is the code inside my abstract Controller class:

And this is my global helper for the responseMessage:

My first goal here is to create a reusable code on the abstract controller that is not just for ExpenseController but for other controllers as well.

As you can see on the store function of ExpenseController I am using the $this->isModelExists with 3 parameters

  1. Empty model
  2. Unique column
  3. User input

My thinking here is, create a reusable function where it checks if the model exists or if it is soft deleted, if the model exists it returns the model, if soft deleted or trashed, return false.

Now, the reason I created this thread is because, I posted my abstract Controller code in the ChatGPT and asking if there are any potential issues in the code and it replied with, the function isModelExists might cause an error/issue because it is not creating a new builder for the model and it suggests I should use the

$model->newQuery(),

I was confused because my system is working properly so why did it says it will throw an error. So I created this post and was looking for other resources as well, until someone posted here that the newQuery() is automatically being called even if I don't explicitly call it because of the Laravel's magic function on the Eloquent class (I think):

function __call()

So I did a deep dive on the Eloquent class and figured out how it works.

But now I am going to refactor my abstract controller class because I also found out Yesterday that Service Containers is better because it is easier to test, and can be access throughout the system, and keep abstract controller class from being bloated.

NOTE: Most of the code here might be incorrect since I am now refactoring this two controllers and the CRUD function will be placed in Service Containers.

Another note: You guys might now be able to understand what I am trying to say because I'm still in a train of thought, and for that, my apologies in advance.

Snapey's avatar

@xatnys0820 If I could review;


    protected function isModelExists(Model $model, string $column, string $value): Model|bool
    {
        $obj = $model->where($column, $value)->first();

        if ($obj)
            return $model;

        if ($obj->trashed())
            return false;
    }

the if(obj->trashed()) will never be true because you only find non-trashed models.

It all feels over engineered.

Your forceDelete could be as simple as;

public function forceDelete($expenseId): JsonResponse
    {
        Expense::withTrashed()->where('id', $expenseId)->forceDelete();

        return response()->json([
                'message' => responseMessage('Expense', $expenseName, 'permanent_delete'),
                'icon' => 'success',
                'color' => config('response.json.color.success')
        }
    }

And in a stroke, eliminated two methods in your abstract class.

You also made your method idempotent (always returns the same response). If something goes wrong then an exception will be thrown, which you don't catch.

Rather than abstract classes, Traits are more commonly used, or extend the actual base controller.

Please or to participate in this conversation.