From 61f9c2be893b0e33d0280347f683cdde4a57e0fa Mon Sep 17 00:00:00 2001 From: Oliver Nybroe Date: Fri, 3 Sep 2021 09:09:17 +0200 Subject: [PATCH 1/3] Add support for disallowing class morphs and thus requiring morph maps --- .../Database/ClassMorphViolationException.php | 29 +++++++++++ .../Eloquent/Concerns/HasRelationships.php | 23 +++++++++ .../Database/EloquentStrictMorphsTest.php | 51 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 src/Illuminate/Database/ClassMorphViolationException.php create mode 100644 tests/Integration/Database/EloquentStrictMorphsTest.php diff --git a/src/Illuminate/Database/ClassMorphViolationException.php b/src/Illuminate/Database/ClassMorphViolationException.php new file mode 100644 index 000000000000..a637393a2a0c --- /dev/null +++ b/src/Illuminate/Database/ClassMorphViolationException.php @@ -0,0 +1,29 @@ +model = $class; + } +} diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php index 5262d4305273..030ff111605a 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Eloquent\Concerns; use Closure; +use Illuminate\Database\ClassMorphViolationException; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -52,6 +53,13 @@ trait HasRelationships */ protected static $relationResolvers = []; + /** + * Prevents morph relationships without a morph map. + * + * @var bool + */ + protected static $preventClassMorphs = false; + /** * Define a dynamic relation resolver. * @@ -67,6 +75,17 @@ public static function resolveRelationUsing($name, Closure $callback) ); } + /** + * Prevent morphs to be used without a mapping. + * + * @param bool $prevent + * @return void + */ + public static function preventClassMorphs($prevent = true) + { + self::$preventClassMorphs = $prevent; + } + /** * Define a one-to-one relationship. * @@ -731,6 +750,10 @@ public function getMorphClass() return array_search(static::class, $morphMap, true); } + if (self::$preventClassMorphs) { + throw new ClassMorphViolationException($this); + } + return static::class; } diff --git a/tests/Integration/Database/EloquentStrictMorphsTest.php b/tests/Integration/Database/EloquentStrictMorphsTest.php new file mode 100644 index 000000000000..8e8958bfbc83 --- /dev/null +++ b/tests/Integration/Database/EloquentStrictMorphsTest.php @@ -0,0 +1,51 @@ +expectException(ClassMorphViolationException::class); + + $model = TestModel::make(); + + $model->getMorphClass(); + } + + public function testStrictModeDoesNotThrowExceptionWhenMorphMap() + { + $model = TestModel::make(); + + Relation::morphMap([ + 'test' => TestModel::class, + ]); + + $morphName = $model->getMorphClass(); + $this->assertEquals('test', $morphName); + } + + protected function tearDown(): void + { + parent::tearDown(); + + Relation::morphMap([], false); + Model::preventClassMorphs(false); + } +} + +class TestModel extends Model +{ +} From c8c7476d55bc102960e104699a214fa1eab5c67f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Sep 2021 09:40:41 -0500 Subject: [PATCH 2/3] move method --- .../Database/ClassMorphViolationException.php | 2 +- .../Eloquent/Concerns/HasRelationships.php | 20 +------------ .../Database/Eloquent/Relations/Relation.php | 28 +++++++++++++++++++ .../Database/EloquentStrictMorphsTest.php | 4 +-- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/Illuminate/Database/ClassMorphViolationException.php b/src/Illuminate/Database/ClassMorphViolationException.php index a637393a2a0c..6594d2d90238 100644 --- a/src/Illuminate/Database/ClassMorphViolationException.php +++ b/src/Illuminate/Database/ClassMorphViolationException.php @@ -22,7 +22,7 @@ public function __construct($model) { $class = get_class($model); - parent::__construct("No morph map found for model [{$class}] while class morphs is disabled."); + parent::__construct("No morph map defined for model [{$class}]."); $this->model = $class; } diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php index 030ff111605a..a4612b462ecb 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php @@ -53,13 +53,6 @@ trait HasRelationships */ protected static $relationResolvers = []; - /** - * Prevents morph relationships without a morph map. - * - * @var bool - */ - protected static $preventClassMorphs = false; - /** * Define a dynamic relation resolver. * @@ -75,17 +68,6 @@ public static function resolveRelationUsing($name, Closure $callback) ); } - /** - * Prevent morphs to be used without a mapping. - * - * @param bool $prevent - * @return void - */ - public static function preventClassMorphs($prevent = true) - { - self::$preventClassMorphs = $prevent; - } - /** * Define a one-to-one relationship. * @@ -750,7 +732,7 @@ public function getMorphClass() return array_search(static::class, $morphMap, true); } - if (self::$preventClassMorphs) { + if (Relation::requiresMorphMap()) { throw new ClassMorphViolationException($this); } diff --git a/src/Illuminate/Database/Eloquent/Relations/Relation.php b/src/Illuminate/Database/Eloquent/Relations/Relation.php index 7fe9f3e9fa82..41fdf28ce567 100755 --- a/src/Illuminate/Database/Eloquent/Relations/Relation.php +++ b/src/Illuminate/Database/Eloquent/Relations/Relation.php @@ -57,6 +57,13 @@ abstract class Relation */ public static $morphMap = []; + /** + * Prevents morph relationships without a morph map. + * + * @var bool + */ + protected static $requireMorphMap = false; + /** * The count of self joins. * @@ -376,6 +383,27 @@ protected function whereInMethod(Model $model, $key) : 'whereIn'; } + /** + * Prevent polymorphic relationships from being used without model mappings. + * + * @param bool $requireMorphMap + * @return void + */ + public static function requireMorphMap($requireMorphMap = true) + { + static::$requireMorphMap = $requireMorphMap; + } + + /** + * Determine if polymorphic relationships require explicit model mapping. + * + * @return bool + */ + public static function requiresMorphMap() + { + return static::$requireMorphMap; + } + /** * Set or get the morph map for polymorphic relations. * diff --git a/tests/Integration/Database/EloquentStrictMorphsTest.php b/tests/Integration/Database/EloquentStrictMorphsTest.php index 8e8958bfbc83..b8ea15128bea 100644 --- a/tests/Integration/Database/EloquentStrictMorphsTest.php +++ b/tests/Integration/Database/EloquentStrictMorphsTest.php @@ -13,7 +13,7 @@ protected function setUp(): void { parent::setUp(); - Model::preventClassMorphs(); + Relation::requireMorphMap(); } public function testStrictModeThrowsAnExceptionOnClassMap() @@ -42,7 +42,7 @@ protected function tearDown(): void parent::tearDown(); Relation::morphMap([], false); - Model::preventClassMorphs(false); + Relation::requireMorphMap(false); } } From e315b646eb1dc65fd38669b760179752dc66f119 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Sep 2021 09:43:22 -0500 Subject: [PATCH 3/3] formatting - add method --- .../Database/Eloquent/Relations/Relation.php | 14 ++++++++++++++ .../Database/EloquentStrictMorphsTest.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Relations/Relation.php b/src/Illuminate/Database/Eloquent/Relations/Relation.php index 41fdf28ce567..afb5ba8aea08 100755 --- a/src/Illuminate/Database/Eloquent/Relations/Relation.php +++ b/src/Illuminate/Database/Eloquent/Relations/Relation.php @@ -404,6 +404,20 @@ public static function requiresMorphMap() return static::$requireMorphMap; } + /** + * Define the morph map for polymorphic relations and require all morphed models to be explicitly mapped. + * + * @param array|null $map + * @param bool $merge + * @return array + */ + public static function enforceMorphMap(array $map, $merge = true) + { + static::requireMorphMap(); + + return static::morphMap($map, $merge); + } + /** * Set or get the morph map for polymorphic relations. * diff --git a/tests/Integration/Database/EloquentStrictMorphsTest.php b/tests/Integration/Database/EloquentStrictMorphsTest.php index b8ea15128bea..ec9f038e520c 100644 --- a/tests/Integration/Database/EloquentStrictMorphsTest.php +++ b/tests/Integration/Database/EloquentStrictMorphsTest.php @@ -37,6 +37,20 @@ public function testStrictModeDoesNotThrowExceptionWhenMorphMap() $this->assertEquals('test', $morphName); } + public function testMapsCanBeEnforcedInOneMethod() + { + $model = TestModel::make(); + + Relation::requireMorphMap(false); + + Relation::enforceMorphMap([ + 'test' => TestModel::class, + ]); + + $morphName = $model->getMorphClass(); + $this->assertEquals('test', $morphName); + } + protected function tearDown(): void { parent::tearDown();