henninghorn's avatar

Eloquent: hasOne relation, get only ID (or certain columns)

I'm trying to just get the ID from a hasOne relation.

public function release()
    {
        return $this->hasOne('App\Release');
    }

If I run

Article::with(['release'])->find(123);

I get the article and the related release. But instead of getting all of the data from the release table I only want to get the ID.

public function release()
    {
        return $this->hasOne('App\Release')->select(['id']);
    }

The above doesn't work. The release relation is null. If I enable query logs and copy paste the query Laravel is making I can get the ID.

Is this a bug, that I can't only select the id? If I change it to this:

public function release()
    {
        return $this->hasOne('App\Release')->select(['id', 'article_id']);
    }

then it works.

0 likes
12 replies
bimalshah72's avatar

@henninghorn

Instead of using Array, do following

Article::with('release')->find(123);
public function release()
    {
        return $this->hasOne('App\Release')->select('id');
    }
1 like
igorblumberg's avatar

I don't know if you are using the Release model in other locations, but you could add the following to your model:

class Realease extends Model
{
       protected $visible = ['id','other_column'];
}

Some other properties that might help are:

  • "hidden" (the opposite of visible, The attributes that should be hidden in arrays)
  • "with" (when you want to eager load the relation in all queries to the model)
  • "appends" (when you have an accessor and want to load it in all queries)

I hope that's what you need.

henninghorn's avatar

@bimalshah72

Still the same result - null. But if I do this:

public function release()
    {
        return $this->hasOne('App\Release')->select('id', 'article_id');
    }

Then I get the relation.

henninghorn's avatar

@igorblumberg

That will do the trick.

But it doesn't change the query.

// Should be this, if I wanted all columns, but I only want the ID column
select * from `releases` where `releases`.`article_id` in (?)

If I add "->select('releases.id')" then the query changes to this

select `releases`.`id` from `releases` where `releases`.`article_id` in (?)

But this doesn't give me the release relation, only null.

pmall's avatar
pmall
Best Answer
Level 56

Your first example doesnt work because you dont include the foreign key to article in the select. It would work with select(['id', 'article_id']). It is because the eager loading mechanism needs the article_id to associate the release with its article.

Why only loading the id ? it mess up with your relationship, what if in some other case you want more info about the release ? If it is for presentation concerns (display it as json) you should use a presenter to format your data before displaying it. If it is for performance concerns, the performance gains are negligible and does not worth it.

3 likes
igorblumberg's avatar

@henninghorn another option is the leave the release method returning the relation

public function release()
    {
        return $this->hasOne('App\Release');
    }

and then create an accessor to releaseid, maybe like this:

public function getReleaseidAttribute()
{
$release = $this->release();
return $release->id;
}

Then, you can use both the relation or the accessor (if you need the full release or only the id):

public function anymethod()
{
$article = Article::find(1);
$relation = $article->release;
$id = $article->releaseid;
}
henninghorn's avatar

Hi guys, @igorblumberg @pmall

Thanks for the input. I thought that the more specific your query would be, the better performance.

I am using presenters, so I can easily sort the extra data away from the output.

pmall's avatar

@henninghorn

I thought that the more specific your query would be, the better performance.

You can always have better performance. It is a trade off between performance and ease of use. A good rule is : deal with performance issues when you actually start to have performance issues.

Please or to participate in this conversation.