Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add where condition to eloquent model on relation?

I want to add where condition to my Model when with('category') is called. My relations are like this:

class Post extends Model
{
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}

Now I use this code to display post categories:

Post::where('slug', $slug)->with('category')->get();

I want to add where condition to Post Model when with('category') is called. I should display only posts.status== published if with('category') is called.

I think return $this->belongsTo(Category::class); is where i should add my where condition, but this doesn't work:

return $this->query()->where('posts.status', 'published')->getModel()->belongsTo(User::class)

How can I add where condition to all post queries if with('category') is called? I know Laravel query scopes, but i think there is a simpler way we can use. (perhaps on $this->belongsTo(Category::class))

like image 889

People also ask

How do you put condition in a relationship in Laravel?

Sometime might be you need to add where condition with your relation model then you can simply use whereHas() as i provide bellow example with laravel 6, laravel 7, laravel 8 and laravel 9 app. $q->where('name', '=', $search); })->get(); dd($users);

Does Laravel have relation?

has() is to filter the selecting model based on a relationship. So it acts very similarly to a normal WHERE condition. If you just use has('relation') that means you only want to get the models that have at least one related model in this relation.

Is eloquent an ORM?

Eloquent is an object relational mapper (ORM) that is included by default within the Laravel framework. An ORM is software that facilitates handling database records by representing data as objects, working as a layer of abstraction on top of the database engine used to store an application's data.

How do you define a relationship in Laravel?

To define a relationship, we need first to define the post() method in User model. In the post() method, we need to implement the hasOne() method that returns the result. Let's understand the one to one relationship through an example. First, we add the new column (user_id) in an existing table named as posts.


2 Answers

Relationships are implemented using additional queries. They are not part of the base query, and do not have access to modify the base query, so you cannot do this inside the relationship method.

The best way to do this is with a query scope:

Class:

class Post extends Model
{
    public function scopeWithCategory($query)
    {
        return $query->with('category')->where('status', 'published');
    }
}

Query:

$posts = Post::where('slug', $slug)->withCategory()->get();

Edit

Based on your comment, I think you've probably asked the wrong question. You may want to post another question explaining what you have setup, and what you need to do, and see if anyone has any suggestions from there.

However, to answer this specific question, I believe you should be able to do this using a global query scope. This is different than a local scope described in my original answer above.

Global query scopes are applied when get() is called on the Eloquent query builder. They have access to the query builder object, and can see the items that have been requested to be eager loaded. Due to this, you should be able to create a global query scope that checks if category is to be eager loaded, and if so, add in the status constraint.

class Post extends Model
{

    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        // make sure to call the parent method
        parent::boot();

        static::addGlobalScope('checkCategory', function(\Illuminate\Database\Eloquent\Builder $builder) {
            // get the relationships that are to be eager loaded
            $eagers = $builder->getEagerLoads();

            // check if the "category" relationship is to be eager loaded.
            // if so, add in the "status" constraint.
            if (array_key_exists('category', $eagers)) {
                $builder->where('status', 'published');
            }
        });
    }
}

The code above shows adding in a global scope using an anonymous function. This was done for ease and clarity. I would suggest creating the actual scope class, as described in the documentation linked above.

like image 199
patricus Avatar answered Sep 28 '22 05:09

patricus


This should work:

Post::where(['slug' => $slug, 'status' => 'published'])->with('category')->get();
like image 38
Everton Mendonça Avatar answered Sep 28 '22 04:09

Everton Mendonça