Skip to content
7 changes: 6 additions & 1 deletion src/Eloquent/HybridRelations.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null
$this->newQuery(),
$this,
$id,
$ownerKey,
$ownerKey ?: $this->getKeyName(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2783 suggests that this broke MorphTo relationships when a model uses a custom primary key.

#3011 includes a proposed fix.

$type,
$name,
);
Expand All @@ -236,6 +236,11 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null

$ownerKey ??= $instance->getKeyName();

// Check if it is a relation with an original model.
if (! is_subclass_of($instance, MongoDBModel::class)) {
return parent::morphTo($name, $type, $id, $ownerKey);
}

return new MorphTo(
$instance->newQuery(),
$this,
Expand Down
6 changes: 5 additions & 1 deletion src/Relations/MorphTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ public function addConstraints()
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$this->query->where($this->ownerKey, '=', $this->parent->{$this->foreignKey});
$this->query->where(
$this->ownerKey,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the $this->getKeyName() fallback in HybridRelations::morphTo and allowing MorphTo to be constructed with a null $ownerKey would result in an exception here when null is used in place of a string field name.

The relevant fix in #3011 is to fall back to $this->getForeignKeyName() here, which respects a custom primary key name on parent.

'=',
$this->getForeignKeyFrom($this->parent),
);
}
}

Expand Down
5 changes: 5 additions & 0 deletions tests/Models/Photo.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ public function hasImage(): MorphTo
{
return $this->morphTo();
}

public function hasImageWithCustomOwnerKey(): MorphTo
{
return $this->morphTo(ownerKey: 'cclient_id');
}
}
19 changes: 19 additions & 0 deletions tests/RelationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,25 @@ public function testMorph(): void
$relations = $photos[1]->getRelations();
$this->assertArrayHasKey('hasImage', $relations);
$this->assertInstanceOf(Client::class, $photos[1]->hasImage);

// inverse
$photo = Photo::query()->create(['url' => 'https://graph.facebook.com/hans.thomas/picture']);
$client = Client::create(['name' => 'Hans Thomas']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hans-thomas: Was there a particular reason you used Photo::query()->create() above but Client::create() here? Best I could tell, they resolve to the same Builder::create() method.

Client::create() just ends up forwarding the call through Model::__call().

$photo->hasImage()->associate($client)->save();

$this->assertCount(1, $photo->hasImage()->get());
$this->assertInstanceOf(Client::class, $photo->hasImage);
$this->assertEquals($client->_id, $photo->hasImage->_id);

// inverse with custom ownerKey
$photo = Photo::query()->create(['url' => 'https://graph.facebook.com/young.gerald/picture']);
$client = Client::create(['cclient_id' => (string) (new ObjectId()), 'name' => 'Young Gerald']);
$photo->hasImageWithCustomOwnerKey()->associate($client)->save();

$this->assertCount(1, $photo->hasImageWithCustomOwnerKey()->get());
$this->assertInstanceOf(Client::class, $photo->hasImageWithCustomOwnerKey);
$this->assertEquals($client->cclient_id, $photo->has_image_with_custom_owner_key_id);
$this->assertEquals($client->_id, $photo->hasImageWithCustomOwnerKey->_id);
}

public function testHasManyHas(): void
Expand Down