diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 9fe0d301918b..15d3108caeed 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Eloquent\Relations; use Closure; +use Illuminate\Contracts\Database\Query\Expression; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -65,7 +66,7 @@ class BelongsToMany extends Relation /** * The pivot table columns to retrieve. * - * @var array + * @var array */ protected $pivotColumns = []; @@ -356,7 +357,7 @@ public function as($accessor) /** * Set a where clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @param string $boolean @@ -372,7 +373,7 @@ public function wherePivot($column, $operator = null, $value = null, $boolean = /** * Set a "where between" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param array $values * @param string $boolean * @param bool $not @@ -386,7 +387,7 @@ public function wherePivotBetween($column, array $values, $boolean = 'and', $not /** * Set a "or where between" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param array $values * @return $this */ @@ -398,7 +399,7 @@ public function orWherePivotBetween($column, array $values) /** * Set a "where pivot not between" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param array $values * @param string $boolean * @return $this @@ -411,7 +412,7 @@ public function wherePivotNotBetween($column, array $values, $boolean = 'and') /** * Set a "or where not between" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param array $values * @return $this */ @@ -423,7 +424,7 @@ public function orWherePivotNotBetween($column, array $values) /** * Set a "where in" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $values * @param string $boolean * @param bool $not @@ -439,7 +440,7 @@ public function wherePivotIn($column, $values, $boolean = 'and', $not = false) /** * Set an "or where" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -454,7 +455,7 @@ public function orWherePivot($column, $operator = null, $value = null) * * In addition, new pivot records will receive this value. * - * @param string|array $column + * @param string|\Illuminate\Contracts\Database\Query\Expression|array $column * @param mixed $value * @return $this * @@ -494,7 +495,7 @@ public function orWherePivotIn($column, $values) /** * Set a "where not in" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $values * @param string $boolean * @return $this @@ -519,7 +520,7 @@ public function orWherePivotNotIn($column, $values) /** * Set a "where null" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param string $boolean * @param bool $not * @return $this @@ -534,7 +535,7 @@ public function wherePivotNull($column, $boolean = 'and', $not = false) /** * Set a "where not null" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param string $boolean * @return $this */ @@ -546,7 +547,7 @@ public function wherePivotNotNull($column, $boolean = 'and') /** * Set a "or where null" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param bool $not * @return $this */ @@ -558,7 +559,7 @@ public function orWherePivotNull($column, $not = false) /** * Set a "or where not null" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @return $this */ public function orWherePivotNotNull($column) @@ -569,7 +570,7 @@ public function orWherePivotNotNull($column) /** * Add an "order by" clause for a pivot table column. * - * @param string $column + * @param string|\Illuminate\Contracts\Database\Query\Expression $column * @param string $direction * @return $this */ @@ -1558,11 +1559,15 @@ public function getPivotColumns() /** * Qualify the given column name by the pivot table. * - * @param string $column - * @return string + * @param string|\Illuminate\Contracts\Database\Query\Expression $column + * @return string|\Illuminate\Contracts\Database\Query\Expression */ public function qualifyPivotColumn($column) { + if ($column instanceof Expression) { + return $column; + } + return str_contains($column, '.') ? $column : $this->table.'.'.$column; diff --git a/tests/Database/DatabaseEloquentMorphToManyTest.php b/tests/Database/DatabaseEloquentMorphToManyTest.php index 32ccbe4b6705..80f1f4a23377 100644 --- a/tests/Database/DatabaseEloquentMorphToManyTest.php +++ b/tests/Database/DatabaseEloquentMorphToManyTest.php @@ -5,18 +5,14 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphToMany; +use Illuminate\Database\Query\Expression; +use Mockery\Adapter\Phpunit\MockeryTestCase as TestCase; use Mockery as m; -use PHPUnit\Framework\TestCase; use stdClass; class DatabaseEloquentMorphToManyTest extends TestCase { - protected function tearDown(): void - { - m::close(); - } - - public function testEagerConstraintsAreProperlyAdded() + public function testEagerConstraintsAreProperlyAdded(): void { $relation = $this->getRelation(); $relation->getParent()->shouldReceive('getKeyName')->andReturn('id'); @@ -30,7 +26,7 @@ public function testEagerConstraintsAreProperlyAdded() $relation->addEagerConstraints([$model1, $model2]); } - public function testAttachInsertsPivotTableRecord() + public function testAttachInsertsPivotTableRecord(): void { $relation = $this->getMockBuilder(MorphToMany::class)->onlyMethods(['touchIfTouching'])->setConstructorArgs($this->getRelationArguments())->getMock(); $query = m::mock(stdClass::class); @@ -43,7 +39,7 @@ public function testAttachInsertsPivotTableRecord() $relation->attach(2, ['foo' => 'bar']); } - public function testDetachRemovesPivotTableRecord() + public function testDetachRemovesPivotTableRecord(): void { $relation = $this->getMockBuilder(MorphToMany::class)->onlyMethods(['touchIfTouching'])->setConstructorArgs($this->getRelationArguments())->getMock(); $query = m::mock(stdClass::class); @@ -59,7 +55,7 @@ public function testDetachRemovesPivotTableRecord() $this->assertTrue($relation->detach([1, 2, 3])); } - public function testDetachMethodClearsAllPivotRecordsWhenNoIDsAreGiven() + public function testDetachMethodClearsAllPivotRecordsWhenNoIDsAreGiven(): void { $relation = $this->getMockBuilder(MorphToMany::class)->onlyMethods(['touchIfTouching'])->setConstructorArgs($this->getRelationArguments())->getMock(); $query = m::mock(stdClass::class); @@ -75,14 +71,39 @@ public function testDetachMethodClearsAllPivotRecordsWhenNoIDsAreGiven() $this->assertTrue($relation->detach()); } - public function getRelation() + public function testQueryExpressionCanBePassedToDifferentPivotQueryBuilderClauses(): void + { + $value = 'pivot_value'; + $column = new Expression("CONCAT(foo, '_', bar)"); + $relation = $this->getRelation(); + /** @var Builder|m\MockInterface $builder */ + $builder = $relation->getQuery(); + + $builder->shouldReceive('where')->with($column, '=', $value, 'and')->times(2)->andReturnSelf(); + $relation->wherePivot($column, '=', $value); + $relation->withPivotValue($column, $value); + + $builder->shouldReceive('whereBetween')->with($column, [$value, $value], 'and', false)->once()->andReturnSelf(); + $relation->wherePivotBetween($column, [$value, $value]); + + $builder->shouldReceive('whereIn')->with($column, [$value], 'and', false)->once()->andReturnSelf(); + $relation->wherePivotIn($column, [$value]); + + $builder->shouldReceive('whereNull')->with($column, 'and', false)->once()->andReturnSelf(); + $relation->wherePivotNull($column); + + $builder->shouldReceive('orderBy')->with($column, 'asc')->once()->andReturnSelf(); + $relation->orderByPivot($column); + } + + public function getRelation(): MorphToMany { [$builder, $parent] = $this->getRelationArguments(); return new MorphToMany($builder, $parent, 'taggable', 'taggables', 'taggable_id', 'tag_id', 'id', 'id'); } - public function getRelationArguments() + public function getRelationArguments(): array { $parent = m::mock(Model::class); $parent->shouldReceive('getMorphClass')->andReturn(get_class($parent));