From 55e2c1ff302f56b7eadf76f73ea074f53edfc406 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 30 Jan 2025 17:31:08 +0800 Subject: [PATCH] Fix Type Mismatch in Polymorphic Relationships When Using PostgreSQL fixes #54401 Signed-off-by: Mior Muhammad Zaki --- .../Eloquent/Relations/MorphOneOrMany.php | 14 +++++++- src/Illuminate/Database/Schema/Blueprint.php | 36 +++++++++++++++++++ src/Illuminate/Database/Schema/Builder.php | 18 +++++++--- .../Database/DatabaseSchemaBlueprintTest.php | 2 +- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php b/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php index 44531957d5b7..ea8fdf614044 100755 --- a/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Schema\Builder as SchemaBuilder; use Illuminate\Support\Str; /** @@ -58,7 +59,18 @@ public function addConstraints() if (static::$constraints) { $this->getRelationQuery()->where($this->morphType, $this->morphClass); - parent::addConstraints(); + if (is_null(SchemaBuilder::$defaultMorphKeyType)) { + parent::addConstraints(); + } else { + $query = $this->getRelationQuery(); + + $query->where($this->foreignKey, '=', transform($this->getParentKey(), fn ($key) => match (SchemaBuilder::$defaultMorphKeyType) { + 'uuid', 'ulid', 'string' => (string) $key, + default => $key, + })); + + $query->whereNotNull($this->foreignKey); + } } } diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index ca2eed4eb55b..603b2dedd657 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -1486,6 +1486,8 @@ public function morphs($name, $indexName = null) $this->uuidMorphs($name, $indexName); } elseif (Builder::$defaultMorphKeyType === 'ulid') { $this->ulidMorphs($name, $indexName); + } elseif (Builder::$defaultMorphKeyType === 'string') { + $this->stringableMorphs($name, $indexName); } else { $this->numericMorphs($name, $indexName); } @@ -1504,11 +1506,45 @@ public function nullableMorphs($name, $indexName = null) $this->nullableUuidMorphs($name, $indexName); } elseif (Builder::$defaultMorphKeyType === 'ulid') { $this->nullableUlidMorphs($name, $indexName); + } elseif (Builder::$defaultMorphKeyType === 'string') { + $this->nullableStringableMorphs($name, $indexName); } else { $this->nullableNumericMorphs($name, $indexName); } } + /** + * Add the proper columns for a polymorphic table using string as IDs (mixed of UUID/ULID & incremental integer). + * + * @param string $name + * @param string|null $indexName + * @return void + */ + public function stringableMorphs($name, $indexName = null) + { + $this->string("{$name}_type"); + + $this->string("{$name}_id"); + + $this->index(["{$name}_type", "{$name}_id"], $indexName); + } + + /** + * Add nullable columns for a polymorphic table using string as IDs (mixed of UUID/ULID & incremental integer). + * + * @param string $name + * @param string|null $indexName + * @return void + */ + public function nullableStringableMorphs($name, $indexName = null) + { + $this->string("{$name}_type")->nullable(); + + $this->string("{$name}_id")->nullable(); + + $this->index(["{$name}_type", "{$name}_id"], $indexName); + } + /** * Add the proper columns for a polymorphic table using numeric IDs (incremental). * diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 9af11e2e0836..bd1f2dcaab74 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -44,9 +44,9 @@ class Builder /** * The default relationship morph key type. * - * @var string + * @var string|null */ - public static $defaultMorphKeyType = 'int'; + public static $defaultMorphKeyType = null; /** * Create a new database Schema manager. @@ -81,8 +81,8 @@ public static function defaultStringLength($length) */ public static function defaultMorphKeyType(string $type) { - if (! in_array($type, ['int', 'uuid', 'ulid'])) { - throw new InvalidArgumentException("Morph key type must be 'int', 'uuid', or 'ulid'."); + if (! in_array($type, ['int', 'uuid', 'ulid', 'string'])) { + throw new InvalidArgumentException("Morph key type must be 'int', 'uuid', 'ulid', or 'string'."); } static::$defaultMorphKeyType = $type; @@ -108,6 +108,16 @@ public static function morphUsingUlids() static::defaultMorphKeyType('ulid'); } + /** + * Set the default morph key type for migrations to string as IDs (mixed of UUID/ULID & incremental integer). + * + * @return void + */ + public static function morphUsingString() + { + static::defaultMorphKeyType('string'); + } + /** * Create a database in the schema. * diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index f92672b2f63e..b5a0facade8f 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -19,7 +19,7 @@ class DatabaseSchemaBlueprintTest extends TestCase protected function tearDown(): void { m::close(); - Builder::$defaultMorphKeyType = 'int'; + Builder::$defaultMorphKeyType = null; } public function testToSqlRunsCommandsFromBlueprint()