Framework: Model factory resolves wrong parent key for models where owner key is set

Created on 13 Nov 2020  路  4Comments  路  Source: laravel/framework

  • Laravel Version: 8.14.0
  • PHP Version: 7.4.12
  • Database Driver & Version: sqlite3

Description:

I might be doing this the wrong way, but when using the new model factory system for a "belongsTo" relationship where the "owner key" is set, getKey() will be used to resolve the parent id instead of the relations owner key.

Steps To Reproduce:

CREATE TABLE "users" ("id" integer not null primary key autoincrement, "key" varchar not null);
CREATE TABLE "posts" ("id" integer not null primary key autoincrement, "user_key" varchar not null);
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class, 'user_key', 'key');
    }
}

Post::factory()->for(User::factory())->create()->user_key; // Will be 1 instead of e.g. "abc123" 
Post::factory()->for(User::factory())->create()->user; // null instead of "User" object
bug

All 4 comments

Hey there,

Can you first please try one of the support channels below? If you can actually identify this as a bug, feel free to report back and I'll gladly help you out and re-open this issue.

Thanks!

Hey @driesvints I think this is a bug.

If the relation is not using a parent model's primary key it will indeed fail.

A possible fix would be using the ->getOwnerKeyName() to use the parent key defined in the relationship and not assume the relation is defined using a model's primary key:

~~~php
public function attributesFor(Model $model)
{
$relationship = $model->{$this->relationship}();

return $relationship instanceof MorphTo ? [
    $relationship->getMorphType() => $this->factory->newModel()->getMorphClass(),
    $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()), // CHANGED
] : [
    $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()), // CHANGED
];

}

protected function resolver($key) // CHANGED
{
return function ($relationship) use ($key) { // CHANGED
if (! $this->resolved) {
return $this->resolved = $this->factory->create()->getAttribute($key); // CHANGED
}

    return $this->resolved;
};

}
~~~

I didn't send a PR because getOwnerKeyName() is available only on BelongsTo and MorphTo relations. On the other relation types this code could be run on, other relation have different method name for retrieving the analog related field, such as getLocalKeyName on HasOneOrMany base relation. So I am not sure which would be the best approach to handle this.

Thanks, I'll try to take a look into this later.

Fixed in next patch release.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SachinAgarwal1337 picture SachinAgarwal1337  路  3Comments

CupOfTea696 picture CupOfTea696  路  3Comments

shopblocks picture shopblocks  路  3Comments

felixsanz picture felixsanz  路  3Comments

kerbylav picture kerbylav  路  3Comments