diff --git a/src/Eloquent/HybridRelations.php b/src/Eloquent/HybridRelations.php index e2270f218..1047bbfa4 100644 --- a/src/Eloquent/HybridRelations.php +++ b/src/Eloquent/HybridRelations.php @@ -2,6 +2,7 @@ namespace MongoDB\Laravel\Eloquent; +use Illuminate\Database\Eloquent\Concerns\HasRelationships; use Illuminate\Database\Eloquent\Model as EloquentModel; use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Support\Str; @@ -24,14 +25,16 @@ trait HybridRelations * Define a one-to-one relationship. * * @param class-string $related - * @param string $foreignKey - * @param string $localKey + * @param string|null $foreignKey + * @param string|null $localKey * @return \Illuminate\Database\Eloquent\Relations\HasOne + * + * @see HasRelationships::hasOne() */ public function hasOne($related, $foreignKey = null, $localKey = null) { // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { + if (! self::isMongoDBModel($related)) { return parent::hasOne($related, $foreignKey, $localKey); } @@ -57,7 +60,7 @@ public function hasOne($related, $foreignKey = null, $localKey = null) public function morphOne($related, $name, $type = null, $id = null, $localKey = null) { // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { + if (! self::isMongoDBModel($related)) { return parent::morphOne($related, $name, $type, $id, $localKey); } @@ -74,24 +77,24 @@ public function morphOne($related, $name, $type = null, $id = null, $localKey = * Define a one-to-many relationship. * * @param class-string $related - * @param string $foreignKey + * @param string $foreignPivotKey * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function hasMany($related, $foreignKey = null, $localKey = null) + public function hasMany($related, $foreignPivotKey = null, $localKey = null) { // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { - return parent::hasMany($related, $foreignKey, $localKey); + if (! self::isMongoDBModel($related)) { + return parent::hasMany($related, $foreignPivotKey, $localKey); } - $foreignKey = $foreignKey ?: $this->getForeignKey(); + $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey(); $instance = new $related; $localKey = $localKey ?: $this->getKeyName(); - return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey); + return new HasMany($instance->newQuery(), $this, $foreignPivotKey, $localKey); } /** @@ -103,11 +106,13 @@ public function hasMany($related, $foreignKey = null, $localKey = null) * @param string $id * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\MorphMany + * + * @see HasRelationships::morphMany() */ public function morphMany($related, $name, $type = null, $id = null, $localKey = null) { // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { + if (! self::isMongoDBModel($related)) { return parent::morphMany($related, $name, $type, $id, $localKey); } @@ -129,12 +134,14 @@ public function morphMany($related, $name, $type = null, $id = null, $localKey = * Define an inverse one-to-one or many relationship. * * @param class-string $related - * @param string $foreignKey - * @param string $otherKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey * @param string $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * + * @see HasRelationships::belongsTo() */ - public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null) + public function belongsTo($related, $foreignPivotKey = null, $relatedPivotKey = null, $relation = null) { // If no relation name was given, we will use this debug backtrace to extract // the calling method's name and use that as the relationship name as most @@ -144,15 +151,15 @@ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relat } // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { - return parent::belongsTo($related, $foreignKey, $otherKey, $relation); + if (! self::isMongoDBModel($related)) { + return parent::belongsTo($related, $foreignPivotKey, $relatedPivotKey, $relation); } // If no foreign key was supplied, we can use a backtrace to guess the proper // foreign key name by using the name of the relationship function, which // when combined with an "_id" should conventionally match the columns. - if ($foreignKey === null) { - $foreignKey = Str::snake($relation).'_id'; + if ($foreignPivotKey === null) { + $foreignPivotKey = Str::snake($relation).'_id'; } $instance = new $related; @@ -162,9 +169,9 @@ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relat // actually be responsible for retrieving and hydrating every relations. $query = $instance->newQuery(); - $otherKey = $otherKey ?: $instance->getKeyName(); + $relatedPivotKey = $relatedPivotKey ?: $instance->getKeyName(); - return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation); + return new BelongsTo($query, $this, $foreignPivotKey, $relatedPivotKey, $relation); } /** @@ -175,6 +182,8 @@ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relat * @param string $id * @param string $ownerKey * @return \Illuminate\Database\Eloquent\Relations\MorphTo + * + * @see HasRelationships::morphTo() */ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null) { @@ -214,23 +223,20 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null * Define a many-to-many relationship. * * @param class-string $related - * @param string|null $collection - * @param string|null $foreignKey - * @param string|null $otherKey - * @param string|null $parentKey - * @param string|null $relatedKey - * @param string|null $relation + * @param string|null $collection + * @param string|null $foreignPivotKey + * @param string|null $relatedPivotKey + * @param string|null $parentKey + * @param string|null $relatedKey + * @param string|null $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + * + * @see HasRelationships::belongsToMany() */ - public function belongsToMany( - $related, - $collection = null, - $foreignKey = null, - $otherKey = null, - $parentKey = null, - $relatedKey = null, - $relation = null - ) { + public function belongsToMany($related, $collection = null, $foreignPivotKey = null, $relatedPivotKey = null, + $parentKey = null, $relatedKey = null, $relation = null) + { + // If no relationship name was passed, we will pull backtraces to get the // name of the calling function. We will use that function name as the // title of this relation since that is a great convention to apply. @@ -239,12 +245,12 @@ public function belongsToMany( } // Check if it is a relation with an original model. - if (! is_subclass_of($related, MongoDBModel::class)) { + if (! self::isMongoDBModel($related)) { return parent::belongsToMany( $related, $collection, - $foreignKey, - $otherKey, + $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relation @@ -255,11 +261,15 @@ public function belongsToMany( // First, we'll need to determine the foreign key and "other key" for the // relationship. Once we have determined the keys we'll make the query // instances as well as the relationship instances we need for this. - $foreignKey = $foreignKey ?: $this->getForeignKey().'s'; + $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey().'s'; $instance = new $related; - $otherKey = $otherKey ?: $instance->getForeignKey().'s'; + if ($relatedPivotKey === $relation) { + throw new \LogicException(sprintf('In %s::%s(), the key cannot be identical to the relation name "%s". The default key is "%s".', static::class, $relation, $relation, $instance->getForeignKey() . 's')); + } + + $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey().'s'; // If no table name was provided, we can guess it by concatenating the two // models using underscores in alphabetical order. The two model names @@ -277,8 +287,8 @@ public function belongsToMany( $query, $this, $collection, - $foreignKey, - $otherKey, + $foreignPivotKey, + $relatedPivotKey, $parentKey ?: $this->getKeyName(), $relatedKey ?: $instance->getKeyName(), $relation @@ -304,10 +314,19 @@ protected function guessBelongsToManyRelation() */ public function newEloquentBuilder($query) { - if (is_subclass_of($this, MongoDBModel::class)) { + if (self::isMongoDBModel(static::class)) { return new Builder($query); } return new EloquentBuilder($query); } + + /** + * @param class-string $model + * @return bool + */ + final protected static function isMongoDBModel(string $model): bool + { + return is_subclass_of($model, MongoDBModel::class); + } }