Skip to content
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

Impl. Model::getIdField() method #1163

Merged
merged 15 commits into from
Feb 19, 2024
27 changes: 17 additions & 10 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class Field implements Expressionable
setOwner as private _setOwner;
}

private static Persistence $genericPersistence;

// {{{ Core functionality

/**
Expand Down Expand Up @@ -101,9 +103,7 @@ private function normalizeUsingTypecast($value)
{
$persistence = $this->issetOwner() && $this->getOwner()->issetPersistence()
? $this->getOwner()->getPersistence()
: new class() extends Persistence {
public function __construct() {}
};
: $this->getGenericPersistence();

$persistenceSetSkipNormalizeFx = \Closure::bind(static function (bool $value) use ($persistence) {
$persistence->typecastSaveSkipNormalize = $value;
Expand Down Expand Up @@ -231,15 +231,15 @@ public function normalize($value)
throw $e;
}

if ($e->getPrevious() !== null && $e instanceof Exception && $e->getMessage() === 'Typecast save error') {
$e = $e->getPrevious();
}

$messages = [];
do {
$messages[] = $e->getMessage();
} while ($e = $e->getPrevious());

if (count($messages) >= 2 && $messages[0] === 'Typecast save error') {
array_shift($messages);
}

throw (new ValidationException([$this->shortName => implode(': ', $messages)], $this->issetOwner() ? $this->getOwner() : null))
->addMoreInfo('field', $this);
}
Expand Down Expand Up @@ -283,6 +283,15 @@ final public function setNull(Model $entity): self
return $this;
}

private function getGenericPersistence(): Persistence
{
if ((self::$genericPersistence ?? null) === null) {
self::$genericPersistence = new class() extends Persistence {};
}

return self::$genericPersistence;
}

/**
* @param mixed $value
*
Expand All @@ -291,9 +300,7 @@ final public function setNull(Model $entity): self
private function typecastSaveField($value, bool $allowGenericPersistence = false)
{
if (!$this->getOwner()->issetPersistence() && $allowGenericPersistence) {
$persistence = new class() extends Persistence {
public function __construct() {}
};
$persistence = $this->getGenericPersistence();
} else {
$this->getOwner()->assertHasPersistence();
$persistence = $this->getOwner()->getPersistence();
Expand Down
8 changes: 8 additions & 0 deletions src/Field/SqlExpressionField.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,12 @@ public function getDsqlExpression(Expression $expression): Expression

return $expr;
}

#[\Override]
public function __debugInfo(): array
{
return array_merge(parent::__debugInfo(), [
'expr' => $this->expr,
]);
}
}
45 changes: 32 additions & 13 deletions src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class Model implements \IteratorAggregate
*
* @var array<string, mixed>
*/
private array $data = [];
private array $data;

/**
* After loading an entity the data will be stored in
Expand All @@ -157,7 +157,7 @@ class Model implements \IteratorAggregate
*
* @var array<string, mixed>
*/
private array $dirty = [];
private array $dirty;

/**
* Setting model as readOnly will protect you from accidentally
Expand Down Expand Up @@ -235,11 +235,6 @@ class Model implements \IteratorAggregate
* $m = new Model();
* $m->setPersistence($db);
*
* The second use actually calls add() but is preferred usage because:
* - it's shorter
* - type hinting will work;
* - you can specify string for a table
*
* @param array<string, mixed> $defaults
*/
public function __construct(Persistence $persistence = null, array $defaults = [])
Expand Down Expand Up @@ -374,6 +369,9 @@ public function createEntity(): self
unset($entity->{$name});
}

$entity->data = [];
$entity->dirty = [];

return $entity;
}

Expand Down Expand Up @@ -607,6 +605,21 @@ public function getField(string $name): Field
}
}

public function getIdField(): Field
{
if ($this->isEntity()) {
return $this->getModel()->getIdField();
}

try {
return $this->getField($this->idField);
} catch (\Throwable $e) {
$this->assertHasIdField();

throw $e;
}
}

/**
* Sets which fields we will select.
*
Expand Down Expand Up @@ -863,7 +876,7 @@ public function getModelCaption(): string
}

/**
* Return value of $model->get($model->titleField). If not set, returns id value.
* Return value of $model->get($model->titleField). If not set, returns ID value.
*
* @return mixed
*/
Expand All @@ -885,7 +898,9 @@ public function getTitles(): array
{
$this->assertIsModel();

$field = $this->titleField && $this->hasField($this->titleField) ? $this->titleField : $this->idField;
$field = $this->titleField && $this->hasField($this->titleField)
? $this->titleField
: $this->idField;

return array_map(static function (array $row) use ($field) {
return $row[$field];
Expand Down Expand Up @@ -1656,7 +1671,9 @@ public function insert(array $row)
});
}

return $this->idField ? $entity->getId() : null;
return $this->idField
? $entity->getId()
: null;
}

/**
Expand Down Expand Up @@ -1686,7 +1703,7 @@ public function import(array $rows)
* @param string $keyField Optional name of field which value we will use as array key
* @param bool $typecast Should we typecast exported data
*
* @return ($keyField is string ? array<mixed, array<string, mixed>> : array<int, array<string, mixed>>)
* @return ($keyField is string ? array<mixed, array<string, mixed>> : list<array<string, mixed>>)
*/
public function export(array $fields = null, string $keyField = null, bool $typecast = true): array
{
Expand Down Expand Up @@ -2050,10 +2067,12 @@ public function __debugInfo(): array
{
if ($this->isEntity()) {
return [
'model' => $this->getModel()->__debugInfo(),
'entityId' => $this->idField && $this->hasField($this->idField)
? ($this->_entityId !== null ? $this->_entityId . ($this->getId() !== null ? '' : ' (unloaded)') : 'null')
? ($this->_entityId === null || $this->getId() !== null
? $this->getId()
: 'unloaded (' . (is_object($this->_entityId) ? get_class($this->_entityId) : $this->_entityId) . ')')
: 'no id field',
'model' => $this->getModel()->__debugInfo(),
];
}

Expand Down
5 changes: 2 additions & 3 deletions src/Model/AggregateModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
* 'salary' => ['expr' => 'sum([])', 'type' => 'atk4_money'],
* ];
*
* your resulting model will have 3 fields: first, last, salary
*
* but when querying it will use the original model to calculate the query, then add grouping and aggregates.
* Your resulting model will have 3 fields: first, last, salary, but when querying
* it will use the original model to calculate the query, then add grouping and aggregates.
*
* If you wish you can add more fields, which will be passed through:
* $aggregate->addField('middle');
Expand Down
22 changes: 13 additions & 9 deletions src/Persistence.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ public function insert(Model $model, array $data)
return false;
}

$idField = $model->getField($model->idField);
$idField = $model->getIdField();
$insertId = $this->typecastLoadField(
$idField,
$idField->getPersistenceName() === $model->table->idField
? $this->typecastSaveField($model->table->getField($model->table->idField), $innerInsertId)
? $this->typecastSaveField($model->table->getIdField(), $innerInsertId)
: $dataRaw[$idField->getPersistenceName()]
);

Expand All @@ -192,7 +192,7 @@ public function insert(Model $model, array $data)
return false;
}

$id = $this->typecastLoadField($model->getField($model->idField), $idRaw);
$id = $this->typecastLoadField($model->getIdField(), $idRaw);

return $id;
}
Expand All @@ -217,7 +217,9 @@ public function update(Model $model, $id, array $data): void
{
$model->assertIsModel();

$idRaw = $model->idField ? $this->typecastSaveField($model->getField($model->idField), $id) : null;
$idRaw = $model->idField
? $this->typecastSaveField($model->getIdField(), $id)
: null;
unset($id);
if ($idRaw === null || (array_key_exists($model->idField, $data) && $data[$model->idField] === null)) {
throw new Exception('Unable to update record: Model idField is not set');
Expand All @@ -231,7 +233,7 @@ public function update(Model $model, $id, array $data): void
}

if (is_object($model->table)) {
$idPersistenceName = $model->getField($model->idField)->getPersistenceName();
$idPersistenceName = $model->getIdField()->getPersistenceName();
$innerId = $this->typecastLoadField($model->table->getField($idPersistenceName), $idRaw);
$innerEntity = $model->table->loadBy($idPersistenceName, $innerId);

Expand Down Expand Up @@ -261,14 +263,16 @@ public function delete(Model $model, $id): void
{
$model->assertIsModel();

$idRaw = $model->idField ? $this->typecastSaveField($model->getField($model->idField), $id) : null;
$idRaw = $model->idField
? $this->typecastSaveField($model->getIdField(), $id)
: null;
unset($id);
if ($idRaw === null) {
throw new Exception('Unable to delete record: Model idField is not set');
}

if (is_object($model->table)) {
$idPersistenceName = $model->getField($model->idField)->getPersistenceName();
$idPersistenceName = $model->getIdField()->getPersistenceName();
$innerId = $this->typecastLoadField($model->table->getField($idPersistenceName), $idRaw);
$innerEntity = $model->table->loadBy($idPersistenceName, $innerId);

Expand Down Expand Up @@ -434,7 +438,7 @@ public function typecastSaveField(Field $field, $value)
}

throw (new Exception('Typecast save error', 0, $e))
->addMoreInfo('field', $field->shortName);
->addMoreInfo('field', $field);
}
}

Expand Down Expand Up @@ -462,7 +466,7 @@ public function typecastLoadField(Field $field, $value)
}

throw (new Exception('Typecast parse error', 0, $e))
->addMoreInfo('field', $field->shortName);
->addMoreInfo('field', $field);
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/Persistence/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public function getRawDataByTable(Model $model, string $table): array
*/
private function assertNoIdMismatch(Model $model, $idFromRow, $id): void
{
if ($idFromRow !== null && !$model->getField($model->idField)->compare($idFromRow, $id)) {
if ($idFromRow !== null && !$model->getIdField()->compare($idFromRow, $id)) {
throw (new Exception('Row contains ID column, but it does not match the row ID'))
->addMoreInfo('idFromKey', $id)
->addMoreInfo('idFromData', $idFromRow);
Expand All @@ -136,7 +136,7 @@ private function assertNoIdMismatch(Model $model, $idFromRow, $id): void
private function saveRow(Model $model, array $rowData, $id): void
{
if ($model->idField) {
$idField = $model->getField($model->idField);
$idField = $model->getIdField();
$id = $idField->normalize($id);
$idColumnName = $idField->getPersistenceName();
if (array_key_exists($idColumnName, $rowData)) {
Expand Down Expand Up @@ -249,7 +249,7 @@ 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->getField($model->idField);
$condition->key = $model->getIdField();
$condition->setOwner($model->createEntity()); // TODO needed for typecasting to apply
$action->filter($condition);

Expand Down Expand Up @@ -308,7 +308,9 @@ public function generateNewId(Model $model)
{
$this->seedData($model);

$type = $model->idField ? $model->getField($model->idField)->type : 'integer';
$type = $model->idField
? $model->getIdField()->type
: 'integer';

switch ($type) {
case 'integer':
Expand Down Expand Up @@ -389,7 +391,7 @@ public function initAction(Model $model, array $fields = null): Action

$rows = [];
foreach ($table->getRows() as $row) {
$rows[$row->getValue($model->getField($model->idField)->getPersistenceName())] = $row->getData();
$rows[$row->getValue($model->getIdField()->getPersistenceName())] = $row->getData();
}
}

Expand Down Expand Up @@ -432,7 +434,7 @@ protected function applyScope(Model $model, Action $action): void
// add entity ID to scope to allow easy traversal
if ($model->isEntity() && $model->idField && $model->getId() !== null) {
$scope = new Model\Scope([$scope]);
$scope->addCondition($model->getField($model->idField), $model->getId());
$scope->addCondition($model->getIdField(), $model->getId());
}

$action->filter($scope);
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 @@ -190,7 +190,7 @@ protected function beforeValuesSet(Row $childRow, $newRowData): void
public function getRowById(Model $model, $idRaw): ?Row
{
foreach ($this->getRows() as $row) {
if ($row->getValue($model->getField($model->idField)->getPersistenceName()) === $idRaw) {
if ($row->getValue($model->getIdField()->getPersistenceName()) === $idRaw) {
return $row;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/Persistence/Csv.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,9 @@ protected function insertRaw(Model $model, array $dataRaw)

$this->putLine($line);

return $model->idField ? $dataRaw[$model->idField] : null;
return $model->idField
? $dataRaw[$model->idField]
: null;
}

/**
Expand Down
Loading
Loading