Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
- Enh #991: Improve types in `ConnectionInterface::transaction()` (@kikara)
- Chg #998: Add `yiisoft/db-implementation` virtual package as dependency (@vjik)
- Chg #999: Remove `requireTransaction()` method and `$isolationLevel` property from `AbstractCommand` (@vjik)
- Enh #1000: Prepare values in `Columns` (@vjik)
- Chg #1000, #1007: Add `Equals` condition. Remove `Hash` condition in favor `Equals` and `In` usage (@vjik)
- Chg #1001: Remove `ParamInterface` (@vjik)
- Chg #1001: Add public properties `$type` and `$value` to `Param` class instead of `getType()` and `getValue()` methods that were removed (@vjik)
- Chg #1002: Remove specific condition interfaces (@vjik)
Expand Down
1 change: 1 addition & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,4 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace
- Remove `AbstractConjunctionCondition` and `AbstractOverlapsConditionBuilder`;
- Change namespace of condition and condition builder classes;
- Remove `AbstractDsn` and `AbstractDsnSocket` classes and `DsnInterface` interface;
- Remove `Hash` condition;
23 changes: 16 additions & 7 deletions src/QueryBuilder/AbstractDQLQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use Yiisoft\Db\Expression\CaseExpressionBuilder;
use Yiisoft\Db\Expression\StructuredExpression;
use Yiisoft\Db\Expression\StructuredExpressionBuilder;
use Yiisoft\Db\QueryBuilder\Condition\Columns;
use Yiisoft\Db\QueryBuilder\Condition\ConditionInterface;
use Yiisoft\Db\QueryBuilder\Condition\Simple;
use Yiisoft\Db\Query\Query;
Expand All @@ -33,6 +32,7 @@
use function array_filter;
use function array_merge;
use function array_shift;
use function count;
use function implode;
use function is_array;
use function is_bool;
Expand Down Expand Up @@ -449,11 +449,19 @@ public function createConditionFromArray(array $condition): ConditionInterface
return $className::fromArrayDefinition($operator, $condition);
}

/**
* Key-value format: 'column1' => 'value1', 'column2' => 'value2', ...
* @psalm-var array<string, mixed> $condition
*/
return new Columns($condition);
$conditions = [];
foreach ($condition as $column => $value) {
if (!is_string($column)) {
throw new InvalidArgumentException('Condition array must have string keys.');
}
if (is_iterable($value) || $value instanceof QueryInterface) {
$conditions[] = new Condition\In($column, 'IN', $value);
continue;
}
$conditions[] = new Condition\Equals($column, $value);
}

return count($conditions) === 1 ? $conditions[0] : new Condition\AndX($conditions);
}

public function getExpressionBuilder(ExpressionInterface $expression): object
Expand Down Expand Up @@ -509,6 +517,7 @@ protected function defaultConditionClasses(): array
'NOT' => Condition\Not::class,
'AND' => Condition\AndX::class,
'OR' => Condition\OrX::class,
'=' => Condition\Equals::class,
'BETWEEN' => Condition\Between::class,
'NOT BETWEEN' => Condition\Between::class,
'IN' => Condition\In::class,
Expand Down Expand Up @@ -545,9 +554,9 @@ protected function defaultExpressionBuilders(): array
Condition\Between::class => Condition\Builder\BetweenBuilder::class,
Condition\In::class => Condition\Builder\InBuilder::class,
Condition\Like::class => Condition\Builder\LikeBuilder::class,
Condition\Equals::class => Condition\Builder\EqualsBuilder::class,
Condition\Exists::class => Condition\Builder\ExistsBuilder::class,
Simple::class => Condition\Builder\SimpleBuilder::class,
Columns::class => Condition\Builder\ColumnsBuilder::class,
Condition\BetweenColumns::class => Condition\Builder\BetweenColumnsBuilder::class,
JsonExpression::class => JsonExpressionBuilder::class,
ArrayExpression::class => ArrayExpressionBuilder::class,
Expand Down
62 changes: 0 additions & 62 deletions src/QueryBuilder/Condition/Builder/ColumnsBuilder.php

This file was deleted.

68 changes: 68 additions & 0 deletions src/QueryBuilder/Condition/Builder/EqualsBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\QueryBuilder\Condition\Builder;

use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
use Yiisoft\Db\Expression\ExpressionInterface;
use Yiisoft\Db\QueryBuilder\Condition\Equals;
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;

/**
* Build an object of {@see Equals} into SQL expressions.
*
* @implements ExpressionBuilderInterface<Equals>
*/
class EqualsBuilder implements ExpressionBuilderInterface
{
public function __construct(
private readonly QueryBuilderInterface $queryBuilder,
) {
}

/**
* Build SQL for {@see Equals}.
*
* @param Equals $expression
*
* @throws Exception
* @throws InvalidConfigException
* @throws NotSupportedException
*/
public function build(ExpressionInterface $expression, array &$params = []): string
{
$column = $this->prepareColumn($expression->column, $params);
$value = $this->prepareValue($expression->value, $params);

return $value === null
? "$column IS NULL"
: "$column = $value";
}

/**
* @throws InvalidConfigException
* @throws NotSupportedException
* @throws Exception
*/
private function prepareColumn(string|ExpressionInterface $column, array &$params): string
{
if ($column instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($column, $params);
}

return $this->queryBuilder->getQuoter()->quoteColumnName($column);
}

private function prepareValue(mixed $value, array &$params): string|null
{
if ($value === null) {
return null;
}

return $this->queryBuilder->buildValue($value, $params);
}
}
30 changes: 0 additions & 30 deletions src/QueryBuilder/Condition/Columns.php

This file was deleted.

50 changes: 50 additions & 0 deletions src/QueryBuilder/Condition/Equals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\QueryBuilder\Condition;

use InvalidArgumentException;
use Yiisoft\Db\Expression\ExpressionInterface;

use function array_key_exists;
use function is_string;

/**
* Represents an equality condition like `"column" = value`.
*/
final class Equals implements ConditionInterface
{
/**
* @param ExpressionInterface|string $column The column name or an expression.
* @param mixed $value The value to compare with.
*/
public function __construct(
public readonly string|ExpressionInterface $column,
public readonly mixed $value
) {
}

/**
* Creates a condition based on the given operator and operands.
*
* @throws InvalidArgumentException
*/
public static function fromArrayDefinition(string $operator, array $operands): self
{
if (!array_key_exists(0, $operands)) {
throw new InvalidArgumentException("Operator '$operator' requires first operand as column.");
}
if (!array_key_exists(1, $operands)) {
throw new InvalidArgumentException("Operator '$operator' requires second operand as value.");
}

[$column, $value] = $operands;

if (!is_string($column) && !$column instanceof ExpressionInterface) {
throw new InvalidArgumentException("Operator '$operator' requires column to be string or ExpressionInterface.");
}

return new self($column, $value);
}
}
21 changes: 14 additions & 7 deletions tests/AbstractQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public function testBuildHaving(): void
$this->assertSame(
DbHelper::replaceQuotes(
<<<SQL
HAVING [[id]]=1
HAVING [[id]] = 1
SQL,
$db->getDriverName(),
),
Expand Down Expand Up @@ -1520,7 +1520,7 @@ public function testBuildWithWhereExistsArrayParameters(): void
$this->assertSame(
DbHelper::replaceQuotes(
<<<SQL
SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]] WHERE (w.id = t.website_id) AND (([[w]].[[merchant_id]]=6) AND ([[w]].[[user_id]]=210)))) AND ([[t]].[[some_column]]=:qp0)
SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]] WHERE (w.id = t.website_id) AND (([[w]].[[merchant_id]] = 6) AND ([[w]].[[user_id]] = 210)))) AND ([[t]].[[some_column]] = :qp0)
SQL,
$db->getDriverName(),
),
Expand Down Expand Up @@ -1648,15 +1648,22 @@ public function testCreateOverlapsConditionFromArrayWithInvalidColumn(): void

public function testCreateOverlapsConditionFromArrayWithInvalidValues(): void
{
$db = $this->getConnection();
$qb = $db->getQueryBuilder();
$qb = $this->getConnection()->getQueryBuilder();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Operator "JSON OVERLAPS" requires values to be iterable or ExpressionInterface.');

$qb->createConditionFromArray(['json overlaps', 'column', 1]);
}

public function testCreateConditionFromArrayWithIntegerKeys(): void
{
$qb = $this->getConnection()->getQueryBuilder();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Condition array must have string keys.');
$qb->createConditionFromArray(['id' => 45, 9 => 'hello']);
}

/**
* @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::createIndex
*/
Expand Down Expand Up @@ -2416,7 +2423,7 @@ public function testOverrideParameters1(): void
$this->assertEquals([':id', ':qp2', ':qp2_0',], array_keys($command->getParams()));
$this->assertEquals(
DbHelper::replaceQuotes(
'SELECT * FROM [[animal]] WHERE (id = 1 AND type = \'test\') AND ([[type]]=\'test1\')',
'SELECT * FROM [[animal]] WHERE (id = 1 AND type = \'test\') AND ([[type]] = \'test1\')',
$db->getDriverName()
),
$command->getRawSql()
Expand All @@ -2442,7 +2449,7 @@ public function testOverrideParameters2(): void
$this->assertEquals([':qp1', ':qp1_0',], array_keys($command->getParams()));
$this->assertEquals(
DbHelper::replaceQuotes(
'SELECT * FROM [[animal]] WHERE (id = 1) AND ([[type]]=\'test2\')',
'SELECT * FROM [[animal]] WHERE (id = 1) AND ([[type]] = \'test2\')',
$db->getDriverName()
),
$command->getRawSql()
Expand Down
4 changes: 2 additions & 2 deletions tests/Db/Command/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ public function testDelete(): void
$this->assertSame(
DbHelper::replaceQuotes(
<<<SQL
DELETE FROM [[table]] WHERE [[column]]=:qp0
DELETE FROM [[table]] WHERE [[column]] = :qp0
SQL,
$db->getDriverName(),
),
Expand Down Expand Up @@ -720,7 +720,7 @@ public function testUpdate(): void
$command = $db->createCommand();
$command->update('{{table}}', ['name' => 'John'], ['id' => 1]);

$this->assertSame('UPDATE [table] SET [name]=:qp0 WHERE [id]=1', $command->getSql());
$this->assertSame('UPDATE [table] SET [name]=:qp0 WHERE [id] = 1', $command->getSql());
$this->assertSame([':qp0' => 'John'], $command->getParams());
}

Expand Down
Loading