121 lines
3.8 KiB
PHP
121 lines
3.8 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Nest\Model;
|
||
|
|
||
|
use Nest\Database\Query\DeleteQuery;
|
||
|
use Nest\Database\Query\InsertQuery;
|
||
|
use Nest\Database\Query\Raw;
|
||
|
use Nest\Database\Query\SelectQuery;
|
||
|
use Nest\Database\Exceptions\NotCurrentTransactionException;
|
||
|
use Nest\Database\Exceptions\Query\MissingConditionValueException;
|
||
|
use Nest\Database\Exceptions\UnknownDatabaseException;
|
||
|
use Nest\Model\Query\EntityQuery;
|
||
|
use function Nest\Utils\array_first_or_val;
|
||
|
use function Nest\Utils\str_snake_singularize;
|
||
|
|
||
|
class ArrayInstance extends ReadableArrayBlueprint
|
||
|
{
|
||
|
/**
|
||
|
* Create an array field instance from an array field definition.
|
||
|
* @param ArrayBlueprint $array
|
||
|
*/
|
||
|
public function __construct(ArrayBlueprint $array)
|
||
|
{
|
||
|
parent::__construct($array->entityClass, $array->name);
|
||
|
|
||
|
// Copy all fields.
|
||
|
$this->type = $array->type;
|
||
|
$this->table = $array->table;
|
||
|
$this->foreignKeyName = $array->foreignKeyName;
|
||
|
$this->foreignValueName = $array->foreignValueName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get current table / foreign key / foreign value state.
|
||
|
* @param string $fromTable Table from which to get values.
|
||
|
* @return array{string, string, string} Table, Foreign key, Foreign value.
|
||
|
*/
|
||
|
public function getState(string $fromTable): array
|
||
|
{
|
||
|
return [
|
||
|
$table = $this->getTable() ?? "{$fromTable}_{$this->name}",
|
||
|
$foreignKey = $this->getForeignKeyName() ?? "$table.".str_snake_singularize($fromTable)."_id",
|
||
|
$foreignValue = $this->getForeignValueName() ?? "$table.".str_snake_singularize($this->name),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extract a column name from a fully qualified name.
|
||
|
* @return string The column name.
|
||
|
*/
|
||
|
private function getColumnName(string $fullyQualifiedName): string
|
||
|
{
|
||
|
return substr($fullyQualifiedName, strrpos($fullyQualifiedName, ".") + 1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Setup inline loading for the current array field in an entity query.
|
||
|
* @param EntityQuery $baseQuery The entity query to alter.
|
||
|
* @param string $prefix Prefix to add to the name of the properties.
|
||
|
* @return Raw Generated select subquery.
|
||
|
* @throws MissingConditionValueException
|
||
|
*/
|
||
|
public function genSelect(EntityQuery $baseQuery, string $prefix = ""): Raw
|
||
|
{
|
||
|
// Get all auto values for undefined tables or keys.
|
||
|
[$table, $foreignKey, $foreignValue] = $this->getState($baseQuery->getTableName());
|
||
|
|
||
|
// Build subquery SELECT SQL.
|
||
|
$sql = ((new SelectQuery($baseQuery->getDatabase(), $table))
|
||
|
->select($foreignValue)
|
||
|
->whereColumn("{$baseQuery->getTableName()}.".$baseQuery->getPrimaryKeyName(), "=", $foreignKey))->build();
|
||
|
|
||
|
// Return the raw SELECT of a JSON array.
|
||
|
return new Raw(
|
||
|
"ARRAY_TO_JSON(ARRAY($sql->sql)) AS $prefix$this->name",
|
||
|
$sql->bindings,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Entity $entity Entity for which to save.
|
||
|
* @param array $value Array to save.
|
||
|
* @return void
|
||
|
* @throws MissingConditionValueException
|
||
|
* @throws UnknownDatabaseException
|
||
|
* @throws NotCurrentTransactionException
|
||
|
*/
|
||
|
public function save(Entity $entity, array $value): void
|
||
|
{
|
||
|
// Start a transaction for the whole save.
|
||
|
$transaction = $entity->getDatabase()->startTransaction();
|
||
|
|
||
|
// Get all auto values for undefined tables or keys.
|
||
|
[$table, $foreignKey, $foreignValue] = $this->getState($entity->getTableName());
|
||
|
|
||
|
// Get entity primary key value.
|
||
|
$entityKey = $entity->{array_first_or_val($entity->getPrimaryFields())};
|
||
|
|
||
|
// Delete existing values.
|
||
|
((new DeleteQuery($entity->getDatabase(), $table))
|
||
|
->where($foreignKey, "=", $entityKey))
|
||
|
->execute();
|
||
|
|
||
|
// Get actual columns names.
|
||
|
$foreignKey = $this->getColumnName($foreignKey);
|
||
|
$foreignValue = $this->getColumnName($foreignValue);
|
||
|
|
||
|
// Insert new ones.
|
||
|
((new InsertQuery($entity->getDatabase(), $table)))
|
||
|
// Insert all values of the array.
|
||
|
->values(...array_map(fn (mixed $val) => ([
|
||
|
$foreignKey => $entityKey,
|
||
|
$foreignValue => $val,
|
||
|
]), $value))
|
||
|
->execute();
|
||
|
|
||
|
// Commit transaction.
|
||
|
$transaction->commit();
|
||
|
}
|
||
|
}
|