Implement and fix CRUD functionality

This commit is contained in:
Dennis Eichhorn 2017-10-12 21:55:46 +02:00
parent 185933a721
commit b2c2f1018f
3 changed files with 194 additions and 58 deletions

View File

@ -855,11 +855,12 @@ class DataMapperAbstract implements DataMapperInterface
*/ */
private static function parseValue(string $type, $value) private static function parseValue(string $type, $value)
{ {
// todo: checking for string === string and is_* is slow. maybe only check type or only string
if (is_null($value)) { if (is_null($value)) {
return null; return null;
} elseif ($type === 'DateTime') { } elseif ($type === 'DateTime' || $value instanceof \DateTime) {
return $value->format('Y-m-d H:i:s'); return $value->format('Y-m-d H:i:s');
} elseif ($type === 'Json' || $type === 'jsonSerializable') { } elseif ($type === 'Json' || $type === 'jsonSerializable' || is_array($value)) {
return json_encode($value); return json_encode($value);
} elseif ($type === 'Serializable') { } elseif ($type === 'Serializable') {
return $value->serialize(); return $value->serialize();
@ -867,13 +868,13 @@ class DataMapperAbstract implements DataMapperInterface
return json_encode($value->jsonSerialize()); return json_encode($value->jsonSerialize());
} elseif (is_object($value) && method_exists($value, 'getId')) { } elseif (is_object($value) && method_exists($value, 'getId')) {
return $value->getId(); return $value->getId();
} elseif ($type === 'int') { } elseif ($type === 'int' || is_int($value)) {
return (int) $value; return (int) $value;
} elseif ($type === 'string') { } elseif ($type === 'string' || is_string($value)) {
return (string) $value; return (string) $value;
} elseif ($type === 'float') { } elseif ($type === 'float' || is_float($value)) {
return (float) $value; return (float) $value;
} elseif ($type === 'bool') { } elseif ($type === 'bool' || is_bool($value)) {
return (bool) $value; return (bool) $value;
} }
@ -895,6 +896,8 @@ class DataMapperAbstract implements DataMapperInterface
*/ */
private static function updateHasMany(\ReflectionClass $reflectionClass, $obj, $objId) /* : void */ private static function updateHasMany(\ReflectionClass $reflectionClass, $obj, $objId) /* : void */
{ {
$objsIds = [];
foreach (static::$hasMany as $propertyName => $rel) { foreach (static::$hasMany as $propertyName => $rel) {
$property = $reflectionClass->getProperty($propertyName); $property = $reflectionClass->getProperty($propertyName);
@ -914,13 +917,13 @@ class DataMapperAbstract implements DataMapperInterface
/** @var string $mapper */ /** @var string $mapper */
$mapper = static::$hasMany[$propertyName]['mapper']; $mapper = static::$hasMany[$propertyName]['mapper'];
$objsIds = [];
$relReflectionClass = null; $relReflectionClass = null;
$objsIds[$propertyName] = [];
foreach ($values as $key => &$value) { foreach ($values as $key => &$value) {
if (!is_object($value)) { if (!is_object($value)) {
// Is scalar => already in database // Is scalar => already in database
$objsIds[$key] = $value; $objsIds[$propertyName][$key] = $value;
continue; continue;
} }
@ -933,7 +936,9 @@ class DataMapperAbstract implements DataMapperInterface
// already in db // already in db
if (!empty($primaryKey)) { if (!empty($primaryKey)) {
$objsIds[$key] = $value; $mapper::update($value);
$objsIds[$propertyName][$key] = $value;
continue; continue;
} }
@ -955,11 +960,11 @@ class DataMapperAbstract implements DataMapperInterface
} }
} }
$objsIds[$key] = $mapper::create($value); $objsIds[$propertyName][$key] = $mapper::create($value);
} }
self::updateRelationTable($propertyName, $objsIds, $objId);
} }
self::updateRelationTable($objsIds, $objId);
} }
/** /**
@ -977,29 +982,20 @@ class DataMapperAbstract implements DataMapperInterface
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private static function updateRelationTable(string $propertyName, array $objsIds, $objId) private static function updateRelationTable(array $objsIds, $objId)
{ {
/** @var string $table */ $many = self::getHasManyRaw($objId);
if (
!empty($objsIds)
&& static::$hasMany[$propertyName]['table'] !== static::$table
&& static::$hasMany[$propertyName]['table'] !== static::$hasMany[$propertyName]['mapper']::$table
) {
$many = self::getHasManyRaw($objId);
foreach(static::$hasMany as $member => $value) { foreach (static::$hasMany as $propertyName => $rel) {
// todo: definately an error here. needs testing $removes = array_diff($many[$propertyName], array_keys($objsIds[$propertyName] ?? []));
throw new \Exception(); $adds = array_diff(array_keys($objsIds[$propertyName] ?? []), $many[$propertyName]);
$removes = array_diff_key($many[$member], $objsIds[$member]);
$adds = array_diff_key($objsIds[$member], $many[$member]);
if(!empty($removes)) { if(!empty($removes)) {
self::deleteRelationTable($propertyName, $removes, $objId); self::deleteRelationTable($propertyName, $removes, $objId);
} }
if(!empty($adds)) { if(!empty($adds)) {
self::createRelationTable($propertyName, $adds, $objId); self::createRelationTable($propertyName, $adds, $objId);
}
} }
} }
} }
@ -1026,11 +1022,10 @@ class DataMapperAbstract implements DataMapperInterface
foreach ($objsIds as $key => $src) { foreach ($objsIds as $key => $src) {
$relQuery = new Builder(self::$db); $relQuery = new Builder(self::$db);
$relQuery->prefix(self::$db->getPrefix()) $relQuery->prefix(self::$db->getPrefix())
->into(static::$hasMany[$propertyName]['table']) ->delete()
->delete(); ->from(static::$hasMany[$propertyName]['table'])
->where(static::$hasMany[$propertyName]['table'] . '.' . static::$hasMany[$propertyName]['src'], '=', $src)
$relQuery->where(static::$hasMany[$propertyName]['src'], '=', $src) ->where(static::$hasMany[$propertyName]['table'] . '.' . static::$hasMany[$propertyName]['dst'], '=', $objId, 'and');
->where(static::$hasMany[$propertyName]['dst'], '=', $objId, 'and');
self::$db->con->prepare($relQuery->toSql())->execute(); self::$db->con->prepare($relQuery->toSql())->execute();
} }
@ -1102,8 +1097,8 @@ class DataMapperAbstract implements DataMapperInterface
{ {
$query = new Builder(self::$db); $query = new Builder(self::$db);
$query->prefix(self::$db->getPrefix()) $query->prefix(self::$db->getPrefix())
->into(static::$table) ->update(static::$table)
->where(static::$primaryField, '=', $objId); ->where(static::$table . '.' . static::$primaryField, '=', $objId);
$properties = $reflectionClass->getProperties(); $properties = $reflectionClass->getProperties();
@ -1126,19 +1121,19 @@ class DataMapperAbstract implements DataMapperInterface
$value = self::parseValue($column['type'], $id); $value = self::parseValue($column['type'], $id);
// todo: should not be done if the id didn't change. but for now don't know if id changed // todo: should not be done if the id didn't change. but for now don't know if id changed
$query->update($column['name'])->value($value, $column['type']); $query->set([static::$table . '.' . $column['name'] => $value], $column['type']);
break; break;
} elseif (isset(static::$belongsTo[$propertyName]) && $column['internal'] === $propertyName) { } elseif (isset(static::$belongsTo[$propertyName]) && $column['internal'] === $propertyName) {
$id = self::updateBelongsTo($propertyName, $property->getValue($obj)); $id = self::updateBelongsTo($propertyName, $property->getValue($obj));
$value = self::parseValue($column['type'], $id); $value = self::parseValue($column['type'], $id);
// todo: should not be done if the id didn't change. but for now don't know if id changed // todo: should not be done if the id didn't change. but for now don't know if id changed
$query->update($column['name'])->value($value, $column['type']); $query->set([static::$table . '.' . $column['name'] => $value], $column['type']);
break; break;
} elseif ($column['internal'] === $propertyName && $column['type'] !== static::$primaryField) { } elseif ($column['internal'] === $propertyName && $column['type'] !== static::$primaryField) {
$value = self::parseValue($column['type'], $property->getValue($obj)); $value = self::parseValue($column['type'], $property->getValue($obj));
$query->update($column['name'])->value($value, $column['type']); $query->set([static::$table . '.' . $column['name'] => $value], $column['type']);
break; break;
} }
} }
@ -1322,8 +1317,8 @@ class DataMapperAbstract implements DataMapperInterface
$query = new Builder(self::$db); $query = new Builder(self::$db);
$query->prefix(self::$db->getPrefix()) $query->prefix(self::$db->getPrefix())
->delete() ->delete()
->into(static::$table) ->from(static::$table)
->where(static::$primaryField, '=', $objId); ->where(static::$table . '.' . static::$primaryField, '=', $objId);
$properties = $reflectionClass->getProperties(); $properties = $reflectionClass->getProperties();
@ -2379,7 +2374,6 @@ class DataMapperAbstract implements DataMapperInterface
->from($value['table']) ->from($value['table'])
->where($value['table'] . '.' . $value['dst'], '=', $primaryKey); ->where($value['table'] . '.' . $value['dst'], '=', $primaryKey);
} elseif ($relations === RelationType::NEWEST) { } elseif ($relations === RelationType::NEWEST) {
/* /*
SELECT c.*, p1.* SELECT c.*, p1.*
FROM customer c FROM customer c

View File

@ -45,6 +45,22 @@ class Builder extends BuilderAbstract
*/ */
public $selects = []; public $selects = [];
/**
* Columns.
*
* @var array
* @since 1.0.0
*/
public $updates = [];
/**
* Stupid work around because value needs to be not null for it to work in Grammar.
*
* @var array
* @since 1.0.0
*/
public $deletes = [1];
/** /**
* Into. * Into.
* *
@ -69,6 +85,14 @@ class Builder extends BuilderAbstract
*/ */
public $values = []; public $values = [];
/**
* Into columns.
*
* @var array
* @since 1.0.0
*/
public $sets = [];
/** /**
* Distinct. * Distinct.
* *
@ -902,6 +926,39 @@ class Builder extends BuilderAbstract
return $this; return $this;
} }
/**
* Values to insert.
*
* @param array $sets Values
*
* @return Builder
*
* @since 1.0.0
*/
public function sets(...$sets) : Builder
{
$this->sets[] = $sets;
return $this;
}
/**
* Values to insert.
*
* @param mixed $set Values
* @param string $type Data type to insert
*
* @return Builder
*
* @since 1.0.0
*/
public function set($set, string $type = 'string') : Builder
{
$this->sets[key($set)] = current($set);
return $this;
}
/** /**
* Update columns. * Update columns.
* *
@ -911,21 +968,28 @@ class Builder extends BuilderAbstract
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function update(...$columns) : Builder public function update(...$tables) : Builder
{ {
if($this->isReadOnly) {
throw new \Exception();
}
$this->type = QueryType::UPDATE; $this->type = QueryType::UPDATE;
foreach ($columns as $key => $column) { foreach ($tables as $key => $table) {
$this->inserts[] = $column; if (is_string($table) || $table instanceof \Closure) {
$this->updates[] = $table;
} else {
throw new \InvalidArgumentException();
}
} }
return $this; return $this;
} }
public function delete() : Builder
{
$this->type = QueryType::DELETE;
return $this;
}
/** /**
* Increment value. * Increment value.
* *

View File

@ -78,6 +78,18 @@ class Grammar extends GrammarAbstract
'wheres', 'wheres',
]; ];
/**
* Update components.
*
* @var string[]
* @since 1.0.0
*/
protected $deleteComponents = [
'deletes',
'from',
'wheres',
];
/** /**
* Random components. * Random components.
* *
@ -111,10 +123,10 @@ class Grammar extends GrammarAbstract
$components = $this->insertComponents; $components = $this->insertComponents;
break; break;
case QueryType::UPDATE: case QueryType::UPDATE:
$components = []; $components = $this->updateComponents;
break; break;
case QueryType::DELETE: case QueryType::DELETE:
$components = []; $components = $this->deleteComponents;
break; break;
case QueryType::RANDOM: case QueryType::RANDOM:
$components = $this->selectComponents; $components = $this->selectComponents;
@ -157,6 +169,42 @@ class Grammar extends GrammarAbstract
return ($query->distinct ? 'SELECT DISTINCT ' : 'SELECT ') . $expression; return ($query->distinct ? 'SELECT DISTINCT ' : 'SELECT ') . $expression;
} }
/**
* Compile select.
*
* @param Builder $query Builder
* @param array $columns Columns
*
* @return string
*
* @since 1.0.0
*/
protected function compileUpdates(Builder $query, array $table) : string
{
$expression = $this->expressionizeTable($table, $query->getPrefix());
if ($expression === '') {
return '';
}
return 'UPDATE ' . $expression;
}
/**
* Compile select.
*
* @param Builder $query Builder
* @param array $columns Columns
*
* @return string
*
* @since 1.0.0
*/
protected function compileDeletes(Builder $query, array $columns) : string
{
return 'DELETE';
}
/** /**
* Compile from. * Compile from.
* *
@ -200,7 +248,7 @@ class Grammar extends GrammarAbstract
} }
} }
if ($expression == '') { if ($expression === '') {
return ''; return '';
} }
@ -287,10 +335,12 @@ class Grammar extends GrammarAbstract
return 'NULL'; return 'NULL';
} elseif (is_bool($value)) { } elseif (is_bool($value)) {
return (string) ((int) $value); return (string) ((int) $value);
} elseif (is_float($value)) {
return (string) $value;
} elseif ($value instanceof Column) { } elseif ($value instanceof Column) {
return $this->compileSystem($value->getColumn(), $prefix); return $this->compileSystem($value->getColumn(), $prefix);
} else { } else {
throw new \InvalidArgumentException(); throw new \InvalidArgumentException(gettype($value));
} }
} }
@ -370,7 +420,7 @@ class Grammar extends GrammarAbstract
$expression .= $this->compileSystem($order['column'], $query->getPrefix()) . ' ' . $order['order'] . ', '; $expression .= $this->compileSystem($order['column'], $query->getPrefix()) . ' ' . $order['order'] . ', ';
} }
if ($expression == '') { if ($expression === '') {
return ''; return '';
} }
@ -422,7 +472,7 @@ class Grammar extends GrammarAbstract
$cols .= $this->compileSystem($column) . ', '; $cols .= $this->compileSystem($column) . ', ';
} }
if ($cols == '') { if ($cols === '') {
return ''; return '';
} }
@ -447,10 +497,38 @@ class Grammar extends GrammarAbstract
$vals .= $this->compileValue($value) . ', '; $vals .= $this->compileValue($value) . ', ';
} }
if ($vals == '') { if ($vals === '') {
return ''; return '';
} }
return 'VALUES ' . rtrim($vals, ', '); return 'VALUES ' . rtrim($vals, ', ');
} }
/**
* Compile insert values.
*
* @param Builder $query Builder
* @param array $values Values
*
* @return string
*
* @since 1.0.0
*/
protected function compileSets(Builder $query, array $values) : string
{
$vals = '';
foreach ($values as $column => $value) {
// todo change expressionizeTableColumn to accept single column and create additionl for Columns
$expression = $this->expressionizeTableColumn([$column], $query->getPrefix());
$vals .= $expression . ' = ' . $this->compileValue($value) . ', ';
}
if ($vals === '') {
return '';
}
return 'SET ' . rtrim($vals, ', ');
}
} }