diff --git a/src/Annotation/Property/Field/Id.php b/src/Annotation/Property/Field/Id.php index 029277a..931b0ae 100644 --- a/src/Annotation/Property/Field/Id.php +++ b/src/Annotation/Property/Field/Id.php @@ -9,6 +9,7 @@ class Id extends \Ulmus\Annotation\Property\Field { $this->nullable = false; $this->attributes['unsigned'] = true; $this->attributes['primary_key'] = true; + $this->attributes['auto_increment'] = true; parent::__construct('int'); } diff --git a/src/Common/PdoObject.php b/src/Common/PdoObject.php index f564f70..eeb39fd 100644 --- a/src/Common/PdoObject.php +++ b/src/Common/PdoObject.php @@ -2,56 +2,57 @@ namespace Ulmus\Common; -use PDO, PDOStatement; +use PDO, + PDOStatement; class PdoObject extends PDO { - public function select(string $sql, array $parameters = []) : PDOStatement - { -# var_dump($sql, $parameters); - # die(); + public function select(string $sql, array $parameters = []): PDOStatement { try { - if ( false !== ( $statement = $this->prepare($sql) ) ) { + if (false !== ( $statement = $this->prepare($sql) )) { $statement = $this->execute($statement, $parameters, false); $statement->setFetchMode(\PDO::FETCH_ASSOC); - + return $statement; } - } catch (\PDOException $e) { throw $e; } + } catch (\PDOException $e) { + throw $e; + } } - public function runQuery(string $sql, array $parameters = []) : PDOStatement - { + public function runQuery(string $sql, array $parameters = []): PDOStatement { try { - if ( false !== ( $statement = $this->prepare($sql) ) ) { + if (false !== ( $statement = $this->prepare($sql) )) { return $this->execute($statement, $parameters, true); } - } catch (\PDOException $e) { throw $e; } + } catch (\PDOException $e) { + throw $e; + } } - - public function execute(PDOStatement $statement, array $parameters = [], bool $commit = true) : ? PDOStatement - { - try { - if (! $this->inTransaction() ) { - $this->beginTransaction(); - } - - if ( empty($parameters) ? $statement->execute() : $statement->execute($parameters) ) { - if ( $commit ) { - $this->commit(); + + public function execute(PDOStatement $statement, array $parameters = [], bool $commit = true): ?PDOStatement { + try { + if (!$this->inTransaction()) { + $this->beginTransaction(); + } + + if (empty($parameters) ? $statement->execute() : $statement->execute($parameters)) { + $statement->lastInsertId = $this->lastInsertId(); + + if ($commit) { + $this->commit(); } - return $statement; - } - else { - throw new PDOException('Could not begin transaction or given statement is invalid.'); - } - } catch (\PDOException $e) { - $this->rollback(); - throw $e; - } + return $statement; + } else { + throw new PDOException('Could not begin transaction or given statement is invalid.'); + } + } catch (\PDOException $e) { + $this->rollback(); + throw $e; + } - return null; - } + return null; + } } diff --git a/src/EntityTrait.php b/src/EntityTrait.php index 95bb377..c91928f 100644 --- a/src/EntityTrait.php +++ b/src/EntityTrait.php @@ -121,11 +121,21 @@ trait EntityTrait { public function entityGetDataset() : array { - $fields = array_keys($this->resolveEntity()->fieldList()); + $dataset = []; + $entityResolver = $this->resolveEntity(); + + foreach($entityResolver->fieldList() as $key => $field) { + $annotation = $entityResolver->searchFieldAnnotation($key, new Field() ); - return array_combine($fields, array_map(function(string $key) { - return $this->$key; - }, $fields)); + if ( isset($this->$key) ) { + $dataset[ $annotation->name ?? $key ] = $this->$key; + } + elseif ( $field['nullable'] ) { + $dataset[ $annotation->name ?? $key ] = null; + } + } + + return $dataset; } public function toArray() : array diff --git a/src/Exception/EntityRequiredField.php b/src/Exception/EntityRequiredField.php new file mode 100644 index 0000000..bb21b14 --- /dev/null +++ b/src/Exception/EntityRequiredField.php @@ -0,0 +1,5 @@ +table . ( $this->alias ? " {$this->alias}" : "" ); + return $this->table; } } diff --git a/src/Query/Set.php b/src/Query/Set.php new file mode 100644 index 0000000..07e9939 --- /dev/null +++ b/src/Query/Set.php @@ -0,0 +1,45 @@ +queryBuilder = $queryBuilder; + } + + public function set(array $dataset) : self + { + $this->dataset = $dataset; + + return $this; + } + + public function render() : string + { + return $this->renderSegments([ + 'SET', $this->renderParameterPlaceholders(), + ]); + } + + public function renderParameterPlaceholders() : string + { + $keys = []; + + foreach($this->dataset as $key =>$value) { + $this->queryBuilder->addParameter($value, ":v_$key"); + $keys[] = "$key=:v_$key"; + } + + return implode(",", $keys); + } +} \ No newline at end of file diff --git a/src/Query/Update.php b/src/Query/Update.php new file mode 100644 index 0000000..d825a40 --- /dev/null +++ b/src/Query/Update.php @@ -0,0 +1,35 @@ +renderSegments([ + 'UPDATE', + ( $this->priority ?? false ), + ( $this->ignore ? 'IGNORE' : false ), + $this->table, + ]); + } + + protected function renderTable() : string + { + return $this->table; + } +} diff --git a/src/Query/Values.php b/src/Query/Values.php index a0f9663..b7e8416 100644 --- a/src/Query/Values.php +++ b/src/Query/Values.php @@ -2,10 +2,14 @@ namespace Ulmus\Query; +use Ulmus\QueryBuilder; + class Values extends Fragment { public int $order = 0; + public ? int $fieldCount = null; + public array $rows; public QueryBuilder $queryBuilder; @@ -17,36 +21,38 @@ class Values extends Fragment { public function add(array $row) : self { - $this->rows[] = $row; + if ($this->fieldCount === null) { + $this->fieldCount = count($row); + } + + $this->rows[] = array_values($row); return $this; } public function render() : string { - $this->queryBuilder->addParameter($this->flattenRowsArray()); + $this->queryBuilder->addValues($this->flattenRowsArray()); return $this->renderSegments([ - 'VALUES', '(' . $this->renderParameterPlaceholders() . ')', + 'VALUES', $this->renderParameterPlaceholders(), ]); } public function renderParameterPlaceholders() : string { $return = []; - $count = null; foreach($this->rows as $row) { - if ($count === null) { - $count = count($row); + if ($this->fieldCount === null) { } else { - if (count($row) !== $count) { + if (count($row) !== $this->fieldCount) { throw new \Exception("Insert statement must contains the same number of values for each rows."); } } - $return[] = implode(',', array_fill(0, $this->parameterCount, '?')); + $return[] = implode(',', array_fill(0, $this->fieldCount, '?')); } return "(" . implode('),(', $return) . ")"; @@ -54,6 +60,6 @@ class Values extends Fragment { public function flattenRowsArray() : array { - return iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->rows))); + return \iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($this->rows))); } } \ No newline at end of file diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 8b45f5c..94cbc58 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -51,7 +51,7 @@ class QueryBuilder $insert = new Query\Insert(); $this->push($insert); - $insert->fieldist = $fieldlist; + $insert->fieldlist = $fieldlist; $insert->alias = $alias; $insert->table = $table; } @@ -63,7 +63,7 @@ class QueryBuilder { if ( false === ( $values = $this->has(Query\Values::class) ) ) { - $values = new Query\Values(); + $values = new Query\Values($this); $this->push($values); } @@ -72,6 +72,40 @@ class QueryBuilder return $this; } + public function update(string $table, ? string $alias = null, ? string $database = null, ? string $schema = null) : self + { + if ( ! $this->has(Query\Update::class) ) { + if ( $database ) { + $table = "\"$database\".$table"; + } + + if ( $schema ) { + $table = "\"$schema\".$table"; + } + + $update = new Query\Update(); + $this->push($update); + + $update->alias = $alias; + $update->table = $table; + } + + return $this; + } + + public function set(array $dataset) : self + { + + if ( false === ( $values = $this->has(Query\Set::class) ) ) { + $set = new Query\Set($this); + $this->push($set); + } + + $set->set($dataset); + + return $this; + } + public function delete() : self { if ( ! $this->has(Query\Delete::class) ) { @@ -180,12 +214,14 @@ class QueryBuilder } $orderBy->add($field, $direction); + return $this; } public function push(Query\Fragment $queryFragment) : self { $this->queryStack[] = $queryFragment; + return $this; } @@ -226,10 +262,12 @@ class QueryBuilder } $this->parameters[$key] = $value; + return $key; } - public function addValues(array $values) { + public function addValues(array $values) : void + { $this->values = $values; } } diff --git a/src/Repository.php b/src/Repository.php index 5b93626..9004f54 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -68,19 +68,35 @@ class Repository } public function deleteFromPk($value, $primaryKey = "id") - { + { return $this->where($primaryKey, $value)->deleteOne(); } public function save(object $entity) : bool - { + { $dataset = $entity->toArray(); - + $primaryKeyDefinition = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField(); + if ( ! $entity->isLoaded() ) { - $save = $this->insertSqlQuery($dataset)->runQuery(); + $statement = $this->insertSqlQuery($dataset)->runQuery(); + + if ( ( 0 !== $statement->lastInsertId ) && + ( null !== $primaryKeyDefinition )) { + $pkField = key($primaryKeyDefinition); + $entity->$pkField = $statement->lastInsertId; + } } else { - $save = $this->updateSqlQuery($dataset)->runQuery(); + if ($primaryKeyDefinition === null) { + throw new \Exception(sprintf("No primary key found for entity %s", $this->entityClass)); + } + + $pkField = key($primaryKeyDefinition); + $pkFieldName = $primaryKeyDefinition[$pkField]->name ?? $pkField; + $this->where($pkFieldName, $dataset[$pkFieldName]); + + # DATASET MUST BE DIFF OF MODIF ONLY ! + $update = $this->updateSqlQuery($dataset)->runQuery(); } return false; @@ -119,6 +135,20 @@ class Repository return $this; } + public function update(array $fieldlist, string $table, string $alias, ? string $schema) : self + { + $this->queryBuilder->update($table, $alias, $schema); + + return $this; + } + + public function set(array $dataset) : self + { + $this->queryBuilder->set($dataset); + + return $this; + } + public function delete() : self { $this->queryBuilder->delete($this->alias); @@ -176,7 +206,7 @@ class Repository return $this; } - public function notWhere(array $condition) : self + public function notWhere($field, $value, string $operator = Query\Where::OPERATOR_NOT_EQUAL) : self { $this->queryBuilder->where($field, $value, $operator, Query\Where::CONDITION_AND, true); @@ -269,18 +299,21 @@ class Repository public function orderBy(string $field, ? string $direction = null) : self { $this->queryBuilder->orderBy($field, $direction); + return $this; } public function limit(int $value) : self { $this->queryBuilder->limit($value); + return $this; } public function offset(int $value) : self { $this->queryBuilder->offset($value); + return $this; } @@ -314,7 +347,7 @@ class Repository return $entityCollection; } - public function runQuery() + public function runQuery() : \PDOStatement { return Ulmus::runQuery($this->queryBuilder); } @@ -322,10 +355,10 @@ class Repository protected function insertSqlQuery(array $dataset) : self { if ( ! $this->queryBuilder->has(Query\Insert::class) ) { - $this->insert(array_keys($dataset)); + $this->insert(array_keys($dataset), $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName()); } - $this->values(count($dataset)); + $this->values($dataset); return $this; } @@ -333,8 +366,10 @@ class Repository protected function updateSqlQuery(array $dataset) : self { if ( ! $this->queryBuilder->has(Query\Update::class) ) { - # $this->insert(); + $this->update($dataset, $this->entityResolver->tableName(), $this->alias, $this->entityResolver->schemaName()); } + + $this->set($dataset); return $this; } diff --git a/src/Ulmus.php b/src/Ulmus.php index 3ca3ca3..2132cae 100644 --- a/src/Ulmus.php +++ b/src/Ulmus.php @@ -32,9 +32,14 @@ abstract class Ulmus ]; } + public static function pdo(?ConnectionAdapter $adapter = null) : Common\PdoObject + { + return ( $adapter ?: static::$defaultAdapter )->pdo; + } + public static function runQuery(QueryBuilder $queryBuilder, ?ConnectionAdapter $adapter = null) { - return ( $adapter ?: static::$defaultAdapter )->pdo->runQuery($queryBuilder->render(), $queryBuilder->parameters ?? []); + return static::pdo($adapter)->runQuery($queryBuilder->render(), array_merge($queryBuilder->values ?? [], $queryBuilder->parameters ?? [])); } public static function resolveEntity(string $entityClass) : Common\EntityResolver