Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions src/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace Awobaz\Compoships\Database\Query;

use Illuminate\Database\MySqlConnection;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\Query\Builder as BaseQueryBuilder;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Support\Arr;

class Builder extends BaseQueryBuilder
Expand All @@ -21,17 +24,39 @@ public function whereIn($column, $values, $boolean = 'and', $not = false)
{
// Here we implement custom support for multi-column 'IN'
if (is_array($column)) {
$inOperator = $not ? 'NOT IN' : 'IN';
$prefix = $this->getConnection()->getTablePrefix();

foreach ($column as &$value) {
$value = $prefix.$value;
$connection = $this->getConnection();
// Check if we can use optimized row value expressions
if (
($connection instanceof MySqlConnection ||
$connection instanceof PostgresConnection ||
$connection instanceof SQLiteConnection) &&
Arr::every($values, fn ($value) => !in_array(null, $value, true))
) {
$inOperator = $not ? 'NOT IN' : 'IN';
$prefix = $connection->getTablePrefix();

foreach ($column as &$value) {
$value = $prefix.$value;
}

$columns = implode(',', $column);
$tuplePlaceholders = '('.implode(',', array_fill(0, count($column), '?')).')';
$placeholderList = implode(',', array_fill(0, count($values), $tuplePlaceholders));
$this->whereRaw("({$columns}) {$inOperator} (VALUES {$placeholderList})", Arr::flatten($values), $boolean);

return $this;
}

$columns = implode(',', $column);
$tuplePlaceholders = '('.implode(', ', array_fill(0, count($column), '?')).')';
$placeholderList = implode(',', array_fill(0, count($values), $tuplePlaceholders));
$this->whereRaw("({$columns}) {$inOperator} ({$placeholderList})", Arr::flatten($values), $boolean);
// Otherwise use a series of OR/AND clauses
$this->where(function ($query) use ($column, $values) {
foreach ($values as $value) {
$query->orWhere(function ($query) use ($column, $value) {
foreach ($column as $index => $aColumn) {
$query->where($aColumn, $value[$index]);
}
});
}
});

return $this;
}
Expand Down
26 changes: 26 additions & 0 deletions tests/Unit/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class BuilderTest extends TestCase
* @covers \Awobaz\Compoships\Database\Eloquent\Concerns\HasRelationships::sanitizeKey
* @covers \Awobaz\Compoships\Database\Eloquent\Relations\HasOneOrMany::addConstraints
* @covers \Awobaz\Compoships\Database\Eloquent\Relations\HasOneOrMany::getQualifiedParentKeyName
* @covers \Awobaz\Compoships\Database\Query\Builder::whereIn
* @covers \Awobaz\Compoships\Database\Query\Builder::whereColumn
*/
public function test_Illuminate_hasOneOrMany__Builder_whereColumn_on_relation_column()
Expand Down Expand Up @@ -58,4 +59,29 @@ public function test_Illuminate_hasOneOrMany__Builder_whereColumn_on_relation_co
$this->assertCount(1, $allocations);
$this->assertCount(2, $allocations[0]->originalPackages);
}

public function test_use_row_value_expressions_for_eager_loading_when_possible()
{
$allocationId1 = Capsule::table('allocations')->insertGetId([
'user_id' => 1,
'booking_id' => 1,
]);
$allocationId2 = Capsule::table('allocations')->insertGetId([
'user_id' => 2,
'booking_id' => null,
]);
$allocation1 = Allocation::find($allocationId1);
$allocation2 = Allocation::find($allocationId2);

$query1 = Allocation::query()->getRelation('user');
$query1->addEagerConstraints([$allocation1]);
$sql1 = $query1->toRawSql();

$query2 = Allocation::query()->getRelation('user');
$query2->addEagerConstraints([$allocation2]);
$sql2 = $query2->toRawSql();

$this->assertEquals('select * from "users" where (users.id,users.booking_id) IN (VALUES (1,1))', $sql1);
$this->assertEquals('select * from "users" where (("users"."id" = 2 and "users"."booking_id" is null))', $sql2);
}
}
Loading