Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Indemnity83's avatar

Eloquent Relationship HasOne with foreign key in THIS models table?

I'm struggling here with how to define a relationship cleanly, hoping somebody can lend some guidance.

I have a model, let's call it Package for simplicity and i have another Model Media (actually using Laravel Medialibrary).

Package hasMany Media (technically morphMany), as defined by the plugin in. That's great. But I want to also say that Package hasOne logo so I can easily use something like this:

$logo = Package::find(1)->logo;

The way I want to do this is to define a logo_id on my packages table that would be tied to the id of the media table (this just seems the most logical, maybe I'm wrong and somebody can politely educate me why). I also have a 'background' and 'icon' that I want to set up similarly.

However there doesn't seem to be any relationship in Laravel that lets me define a relationship where the 'foreign key' is in the parent table.

// in Package.php

    /**
     * A package has one logo media object
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function logo()
    {
        return $this->hasOne(Media::class, 'logo_id');
    }
// in Package.php

    /**
     * A modpack belongs to a logo media object
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function logo()
    {
        return $this->belongsTo(Media::class, 'logo_id');
    }

both of these definitions expect the logo_id is in the media table, which it's not (and hasMany doesn't seem to make any sense either).

help/advice is greatly appreciated. Maybe I need to create a hasA relation type?

0 likes
3 replies
willvincent's avatar

belongsTo would indicate the foreign key on the table for that model. hasMany/hasOne indicates the other model's table references this model's id.

For example, a post table might have fields like this:

id
title
body
author_id (foreign key referencing user id)

and comments like this:

id
post_id (foreign key referencing post)
comment 
author_id (foreign key referencing user id)

Models would look like this:

Post:

public function comments() {
  return $this->hasMany('App\Comment');
}

public function author() {
  return $this->belongsTo('App\User', 'author_id');
}

because posts have (potentially) many comments, and belong to/are owned by a specific user. The user's id is stored on the post table, comment ids are not.

Comment:

public function post() {
  // because we used post_id for the foreign key we don't need to specify it
  // as that is the assumed default: [snakeCased model name]_id
  return $this->belongsTo('App\Post'); 
}

public function author() {
  return $this->belongsTo('App\User', 'author_id');
}

because an individual comment belongs to only one post, and is owned by only one user, those foreign keys are stored in the comment table.

Finally, Users can have many posts and many comments, so on the User model:

public function posts() {
   // User's id is referenced in the post table as 'author id' so we must specify that here.
  return $this->hasMany('App\Post', 'author_id');
}

public function comments() {
  // Again, user's id is referenced in the comments table as 'author id' so we specify it here.
  return $this->hasMany('App\Comment', 'author_id');
}
1 like
willvincent's avatar
Level 54

It could be though, that what you really need is to simply specify what kind of media a thing is. So if on your media table you captured that something were a logo or background, or whatever. then you could still have the normal 'belongsTo' on the media side, and on your package side have the normal 'hasMany' but also do something like this:

public function media() {
  return $this->hasMany('App\Media');
}

public function logo() {
  return $this->media()->where('type', '=', 'logo')->get();
}

public function background() {
  return $this->media()->where('type', '=', 'background')->get();
}

To limit to one of any type, just include logic to prevent saving more than one logo for any given package, or add a 'unique' index on your media table on the package & type fields together, so that any package can only have one instance of each 'type'

2 likes
Indemnity83's avatar

@willvincent thanks!

Right in front of my nose.

Your last suggestion is in line with my goal, I'll work with that for now. I blame my oversight on working on it WAY too far into the night/morning.

Please or to participate in this conversation.