Skip to content

Commit

Permalink
Allow passing query Expression as column in Many-to-Many relationships (
Browse files Browse the repository at this point in the history
  • Loading branch information
plumthedev committed Mar 30, 2024
1 parent 978ee88 commit e52430b
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 29 deletions.
39 changes: 22 additions & 17 deletions src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,7 +66,7 @@ class BelongsToMany extends Relation
/**
* The pivot table columns to retrieve.
*
* @var array
* @var array<string|\Illuminate\Contracts\Database\Query\Expression>
*/
protected $pivotColumns = [];

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
*/
Expand All @@ -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
Expand All @@ -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
*/
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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<string, string> $column
* @param mixed $value
* @return $this
*
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
*/
Expand All @@ -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
*/
Expand All @@ -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)
Expand All @@ -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
*/
Expand Down Expand Up @@ -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 ($this->getGrammar()->isExpression($column)) {
return $column;
}

return str_contains($column, '.')
? $column
: $this->table.'.'.$column;
Expand Down
142 changes: 142 additions & 0 deletions tests/Database/DatabaseEloquentBelongsToManyExpressionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

namespace Illuminate\Tests\Database;

use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use PHPUnit\Framework\TestCase;

class DatabaseEloquentBelongsToManyExpressionTest extends TestCase
{
protected function setUp(): void
{
$db = new DB;

$db->addConnection([
'driver' => 'mysql',
'host' => 'mysql',
'database' => 'forge',
]);

$db->bootEloquent();
$db->setAsGlobal();

$this->createSchema();
}

public function testQualifiedExpression()
{
$this->seedData();

$tag = DatabaseEloquentBelongsToManyExpressionTestTestPost::findOrFail(1)
->tags()
->wherePivotNotIn(
new Expression("CONCAT(tag_id, '_', type)"),
['1_t1']
)
->first();

$this->assertEquals(2, $tag->getKey());
}

/**
* Setup the database schema.
*
* @return void
*/
public function createSchema()
{
$this->schema()->create('posts', fn (Blueprint $t) => $t->id());
$this->schema()->create('tags', fn (Blueprint $t) => $t->id());
$this->schema()->create('taggables', function (Blueprint $t) {
$t->unsignedBigInteger('tag_id');
$t->unsignedBigInteger('taggable_id');
$t->string('type' , 10);
$t->string('taggable_type');
}
);
}

/**
* Tear down the database schema.
*
* @return void
*/
protected function tearDown(): void
{
$this->schema()->drop('posts');
$this->schema()->drop('tags');
$this->schema()->drop('taggables');
}

/**
* Helpers...
*/
protected function seedData(): void
{
$p1 = DatabaseEloquentBelongsToManyExpressionTestTestPost::query()->create();
$p2 = DatabaseEloquentBelongsToManyExpressionTestTestPost::query()->create();
$t1 = DatabaseEloquentBelongsToManyExpressionTestTestTag::query()->create();
$t2 = DatabaseEloquentBelongsToManyExpressionTestTestTag::query()->create();
$t3 = DatabaseEloquentBelongsToManyExpressionTestTestTag::query()->create();

$p1->tags()->sync([
$t1->getKey() => ['type' => 't1'],
$t2->getKey() => ['type' => 't2'],
]);
$p2->tags()->sync([
$t2->getKey() => ['type' => 't2'],
$t3->getKey() => ['type' => 't3'],
]);
}

/**
* Get a database connection instance.
*
* @return \Illuminate\Database\ConnectionInterface
*/
protected function connection()
{
return Eloquent::getConnectionResolver()->connection();
}

/**
* Get a schema builder instance.
*
* @return \Illuminate\Database\Schema\Builder
*/
protected function schema()
{
return $this->connection()->getSchemaBuilder();
}
}

class DatabaseEloquentBelongsToManyExpressionTestTestPost extends Eloquent
{
protected $table = 'posts';
protected $fillable = ['id'];
public $timestamps = false;

public function tags(): MorphToMany
{
return $this->morphToMany(
DatabaseEloquentBelongsToManyExpressionTestTestTag::class,
'taggable',
'taggables',
'taggable_id',
'tag_id',
'id',
'id',
);
}
}

class DatabaseEloquentBelongsToManyExpressionTestTestTag extends Eloquent
{
protected $table = 'tags';
protected $fillable = ['id'];
public $timestamps = false;
}
Loading

0 comments on commit e52430b

Please sign in to comment.