Skip to content

Rename Condition::$key property to Condition::$field #1174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 27, 2024
7 changes: 3 additions & 4 deletions src/Model/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ class Scope extends AbstractScope
{
use ContainerTrait;

// junction definitions
public const OR = 'OR';
public const AND = 'AND';

/** @var self::AND|self::OR Junction to use in case more than one element. */
protected $junction = self::AND;
protected string $junction = self::AND;

/**
* Create a Scope from array of condition objects or condition arrays.
Expand All @@ -33,7 +32,7 @@ class Scope extends AbstractScope
public function __construct(array $conditions = [], string $junction = self::AND)
{
if (!in_array($junction, [self::OR, self::AND], true)) {
throw (new Exception('Using invalid CompondCondition junction'))
throw (new Exception('Unsupported compound condition junction'))
->addMoreInfo('junction', $junction);
}

Expand Down Expand Up @@ -94,7 +93,7 @@ public function addCondition($field, $operator = null, $value = null)
*
* @return array<AbstractScope>
*/
public function getNestedConditions()
public function getNestedConditions(): array
{
return $this->elements;
}
Expand Down
19 changes: 15 additions & 4 deletions src/Model/Scope/AbstractScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,30 @@ abstract class AbstractScope
use InitializerTrait {
init as private _init;
}
use TrackableTrait;
use TrackableTrait {
setOwner as private _setOwner;
}
use WarnDynamicPropertyTrait;

/**
* Method is executed when the scope is added to parent scope using Scope::add().
* @param Model\Scope $owner
*
* @return $this
*/
protected function init(): void
public function setOwner(object $owner)
{
$owner = $this->getOwner();
if (!$owner instanceof self) { // @phpstan-ignore-line
throw new Exception('Scope can only be added as element to scope');
}

return $this->_setOwner($owner);
}

/**
* Method is executed when the scope is added to parent scope using Scope::add().
*/
protected function init(): void
{
$this->_init();

$this->onChangeModel();
Expand Down
77 changes: 40 additions & 37 deletions src/Model/Scope/Condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ class Condition extends AbstractScope
use ReadableCaptionTrait;

/** @var string|Field|Expressionable */
public $key;
public $field;

/** @var string|null */
public $operator;
public ?string $operator = null;

/** @var mixed */
public $value;
Expand All @@ -40,7 +39,7 @@ class Condition extends AbstractScope
public const OPERATOR_NOT_REGEXP = 'NOT REGEXP';

/** @var array<string, array<string, string>> */
protected static $operators = [
protected static array $operators = [
self::OPERATOR_EQUALS => [
'negate' => self::OPERATOR_DOESNOT_EQUAL,
'label' => 'is equal to',
Expand Down Expand Up @@ -92,17 +91,17 @@ class Condition extends AbstractScope
];

/**
* @param string|Expressionable $key
* @param string|Expressionable $field
* @param ($value is null ? mixed : string) $operator
* @param ($operator is string ? mixed : never) $value
*/
public function __construct($key, $operator = null, $value = null)
public function __construct($field, $operator = null, $value = null)
{
if ($key instanceof AbstractScope) {
if ($field instanceof AbstractScope) {
throw new Exception('Only Scope can contain another conditions');
} elseif ($key instanceof Field) { // for BC
$key = $key->shortName;
} elseif (!is_string($key) && !$key instanceof Expressionable) { // @phpstan-ignore-line
} elseif ($field instanceof Field) { // for BC
$field = $field->shortName;
} elseif (!is_string($field) && !$field instanceof Expressionable) { // @phpstan-ignore-line
throw new Exception('Field must be a string or an instance of Expressionable');
}

Expand All @@ -111,12 +110,12 @@ public function __construct($key, $operator = null, $value = null)
$operator = self::OPERATOR_EQUALS;
}

$this->key = $key;
$this->field = $field;
$this->value = $value;

if ($operator === null) {
// at least MSSQL database always requires an operator
if (!$key instanceof Expressionable) {
if (!$field instanceof Expressionable || $value !== null) {
throw new Exception('Operator must be specified');
}
} else {
Expand Down Expand Up @@ -161,8 +160,8 @@ protected function onChangeModel(): void
&& !$this->value instanceof Expressionable
&& !$this->value instanceof Persistence\Array_\Action // needed to pass hintable tests
) {
// key containing '/' means chained references and it is handled in toQueryArguments method
$field = $this->key;
// field containing '/' means chained references and it is handled in toQueryArguments method
$field = $this->field;
if (is_string($field) && !str_contains($field, '/')) {
$field = $model->getField($field);
}
Expand All @@ -185,11 +184,7 @@ protected function onChangeModel(): void
*/
public function toQueryArguments(): array
{
if ($this->isEmpty()) {
return []; // @phpstan-ignore-line
}

$field = $this->key;
$field = $this->field;
$operator = $this->operator;
$value = $this->value;

Expand Down Expand Up @@ -258,22 +253,22 @@ public function toQueryArguments(): array
return [$field, $operator, $value];
}

/**
* @return false
*/
#[\Override]
public function isEmpty(): bool
{
return $this->key === null
&& $this->operator === null
&& $this->value === null;
return false;
}

/**
* @return never
*/
#[\Override]
public function clear(): self
{
$this->key = null; // @phpstan-ignore-line
$this->operator = null;
$this->value = null;

return $this;
throw new Exception('Condition does not support clear operation');
}

#[\Override]
Expand All @@ -283,7 +278,7 @@ public function negate(): self
$this->operator = self::$operators[$this->operator]['negate'];
} else {
throw (new Exception('Negation of condition is not supported for this operator'))
->addMoreInfo('operator', $this->operator ?? 'no operator');
->addMoreInfo('operator', $this->operator);
}

return $this;
Expand All @@ -300,18 +295,18 @@ public function toWords(Model $model = null): string
throw new Exception('Condition must be associated with Model to convert to words');
}

$key = $this->keyToWords($model);
$field = $this->fieldToWords($model);
$operator = $this->operatorToWords();
$value = $this->valueToWords($model, $this->value);

return trim($key . ' ' . $operator . ' ' . $value);
return trim($field . ' ' . $operator . ' ' . $value);
}

protected function keyToWords(Model $model): string
protected function fieldToWords(Model $model): string
{
$words = [];

$field = $this->key;
$field = $this->field;
if (is_string($field)) {
if (str_contains($field, '/')) {
$references = explode('/', $field);
Expand All @@ -329,7 +324,9 @@ protected function keyToWords(Model $model): string
$words[] = 'where';

if ($field === '#') {
$words[] = $this->operator ? 'number of records' : 'any referenced record exists';
$words[] = $this->operator
? 'number of records'
: 'any referenced record exists';
}
}

Expand All @@ -349,7 +346,9 @@ protected function keyToWords(Model $model): string

protected function operatorToWords(): string
{
return $this->operator ? self::$operators[$this->operator]['label'] : '';
return $this->operator
? self::$operators[$this->operator]['label']
: '';
}

/**
Expand All @@ -358,7 +357,9 @@ protected function operatorToWords(): string
protected function valueToWords(Model $model, $value): string
{
if ($value === null) {
return $this->operator ? 'empty' : '';
return $this->operator
? 'empty'
: '';
}

if (is_array($value)) {
Expand All @@ -383,7 +384,7 @@ protected function valueToWords(Model $model, $value): string
}

// handling of scope on references
$field = $this->key;
$field = $this->field;
if (is_string($field)) {
if (str_contains($field, '/')) {
$references = explode('/', $field);
Expand All @@ -404,7 +405,9 @@ protected function valueToWords(Model $model, $value): string
$title = null;
if ($field instanceof Field && $field->hasReference()) {
// make sure we set the value in the Model
$entity = $model->isEntity() ? clone $model : $model->createEntity();
$entity = $model->isEntity()
? clone $model
: $model->createEntity();
$entity->set($field->shortName, $value);

// then take the title
Expand Down
7 changes: 3 additions & 4 deletions src/Model/Scope/RootScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
*/
class RootScope extends Model\Scope
{
/** @var Model */
protected $model;
protected Model $model;

protected function __construct(array $conditions = [])
{
Expand All @@ -29,7 +28,7 @@ public function setModel(Model $model)
{
$model->assertIsModel();

if ($this->model !== $model) {
if (($this->model ?? null) !== $model) {
$this->model = $model;

$this->onChangeModel();
Expand All @@ -47,7 +46,7 @@ public function getModel(): Model
#[\Override]
public function negate(): self
{
throw new Exception('Model scope cannot be negated');
throw new Exception('Model root scope cannot be negated');
}

/**
Expand Down
9 changes: 4 additions & 5 deletions src/Persistence/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,8 @@ public function tryLoad(Model $model, $id): ?array

if (is_object($model->table)) {
$action = $this->action($model, 'select');
$condition = new Model\Scope\Condition('', $id);
$condition->key = $model->getIdField();
$condition->setOwner($model->createEntity()); // TODO needed for typecasting to apply
$condition = new Model\Scope\Condition($model->getIdField(), $id);
$condition->setOwner($model->scope()); // needed for typecasting to apply
$action->filter($condition);

$rowData = $action->getRow();
Expand Down Expand Up @@ -300,7 +299,7 @@ protected function deleteRaw(Model $model, $idRaw): void
/**
* Generates new record ID.
*
* @return string
* @return int|string
*/
public function generateNewId(Model $model)
{
Expand Down Expand Up @@ -334,7 +333,7 @@ public function generateNewId(Model $model)
/**
* Last ID inserted.
*
* @return mixed
* @return int|string
*/
public function lastInsertId(Model $model = null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/Array_/Db/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ protected function beforeValuesSet(Row $childRow, $newRowData): void
/**
* TODO rewrite with hash index support.
*
* @param mixed $idRaw
* @param scalar $idRaw
*/
public function getRowById(Model $model, $idRaw): ?Row
{
Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/Sql/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ public function groupConcat($field, string $separator = ',')
/**
* Returns Query object of [case] expression.
*
* @param mixed $operand optional operand for case expression
* @param string|Expressionable|null $operand optional operand for case expression
*
* @return self
*/
Expand Down
11 changes: 4 additions & 7 deletions tests/Persistence/ArrayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -770,22 +770,19 @@ public function testUnsupportedAggregate(): void
$m->action('fx', ['UNSUPPORTED', 'name']);
}

public function testUnsupportedCondition1(): void
public function testConditionNoOperatorException(): void
{
$p = new Persistence\Array_([1 => ['name' => 'John']]);
$m = new Model($p);
$m = new Model();
$m->addField('name');

$this->expectException(Exception::class);
$this->expectExceptionMessage('Operator must be specified');
$m->addCondition('name');
}

public function testUnsupportedCondition2(): void
public function testConditionUnsupportedFieldException(): void
{
$p = new Persistence\Array_([1 => ['name' => 'John']]);
$m = new Model($p);
$m->addField('name');
$m = new Model();

$this->expectException(Exception::class);
$this->expectExceptionMessage('Field must be a string or an instance of Expressionable');
Expand Down
Loading