Guy Mazouz
Guy Mazouz

Reputation: 437

Laravel models to return null relation?

I am writing a website for photo posts and I have these functions relating likes (they determine if the user is liking the specific post or not)

Post Model:

public function likes()
{
    return $this->hasMany('Like');
}

public function isLiked()
{
    return $this->likes()->where('user_id', Auth::user()->id);
}

Post Controller function for example:

public function postsByType($type)
{
    if($this->user){
        $posts = Post::with('isLiked')->where('type', '=', $type)->paginate(12);
    } else {
        $posts = Post::where('type', '=', $type)->paginate(12);
    }
    return $posts;
}

Is there any way to return null in MODEL function when user is not logged in, without running a query?

I want to avoid writing that if in post controller

I thought about the following solution but it's not working...

public function isFollowing()
{
    return $this->setRelation('isFollowing', null);

}

getting this error: Call to undefined method Illuminate\Database\Query \Builder::addEagerConstraints()

Upvotes: 3

Views: 1752

Answers (1)

lukasgeiter
lukasgeiter

Reputation: 152860

Since you probably always want to fetch the relation (except if there's no user logged in) I suggest you do something like this in your model:
(I also renamed the relationship to liked, you'll see later why)

public function newQuery(){
    $query = parent::newQuery();
    if(Auth::check()){
        $query->with('liked');
    }
    return $query;
}

Now every time a query is run with the model with('isLiked') will be added if the user is logged in.

One problem remains though. If you access isLiked the query will be run anyways. And even for every post because it's not eager loaded. You can fix that by adding an attribute accessor:

public function getIsLikedAttribute(){
    if(Auth::guest) return false;
    return ! $this->liked->isEmpty();
}

So in your view you can just do this:

@if($post->isLiked)

Note: It would be nicer to move the things inside newQuery() to a global scope. Make sure to check out how to do that in the documentation if you're interested.

Here's an example with a scope. Create a class, let's call it LikedScope:

class LikedScope implements Illuminate\Database\Eloquent\ScopeInterface {
    public function apply(Builder $builder, Model $model){
        if(Auth::check()){
            $builder->with('liked');
        }
    }

    public function remove(Builder $builder, Model $model){

    }
}

And then add it to your model:

public static function boot(){
    parent::boot();
    static::addGlobalScope(new LikedScope);
}

Upvotes: 3

Related Questions