From 4cb16a69a3c95f00fe279acd40d5b03e08ba3097 Mon Sep 17 00:00:00 2001 From: Aldarien Date: Wed, 22 Jul 2020 14:06:53 -0400 Subject: [PATCH 1/2] Composer: Paris & psr-4 --- composer.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 composer.json diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..14cfc67 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "provm/models", + "description": "Model handling using j4mie/paris", + "type": "library", + "require": { + "j4mie/paris": "^1.5" + }, + "require-dev": { + "phpunit/phpunit": "^9.2", + "kint-php/kint": "^3.3" + }, + "license": "prioprietary", + "authors": [ + { + "name": "Aldarien", + "email": "aldarien85@gmail.com" + } + ], + "autoload": { + "psr-4": { + "ProVM\\Common\\": "common" + } + } +} From f63ec7a482a2fc015bdff5ca6a1cd8f457bebb9c Mon Sep 17 00:00:00 2001 From: Aldarien Date: Wed, 22 Jul 2020 14:07:37 -0400 Subject: [PATCH 2/2] Interface, Abstract, Factory --- common/Alias/Model.php | 14 ++ common/Define/Model.php | 124 +++++++++++++ common/Factory/Model.php | 365 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 503 insertions(+) create mode 100644 common/Alias/Model.php create mode 100644 common/Define/Model.php create mode 100644 common/Factory/Model.php diff --git a/common/Alias/Model.php b/common/Alias/Model.php new file mode 100644 index 0000000..06476af --- /dev/null +++ b/common/Alias/Model.php @@ -0,0 +1,14 @@ +factory = $factory; + return $this; + } + + protected function checkDefinitions(array $definitions, array $required, array $default) { + foreach ($default as $key => $value) { + if (!isset($definitions[$key])) { + $definitions[$key] = $value; + } + } + foreach ($required as $definition) { + if (!isset($definitions[$definition])) { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); + throw new \InvalidArgumentException($definition . ' is required for ' . $trace[1]['function'] . ' in ' . get_called_class()); + } + } + return $definitions; + } + public function parentOf(string $child_model_class, array $relation_definitions): array { + $relation_definitions = $this->checkDefinitions($relation_definitions, [ + Model::SELF_KEY, + Model::CHILD_KEY + ], [ + Model::SELF_KEY => 'id' + ]); + return $this->factory + ->find($child_model_class) + ->where([ + [ + $relation_definitions[Model::CHILD_KEY], + $this->{$relation_definitions[Model::SELF_KEY]} + ] + ]) + ->many(); + } + public function childOf(string $parent_model_class, array $relation_definitions): ModelInterface { + $relation_definitions = $this->checkDefinitions($relation_definitions, [ + Model::SELF_KEY, + Model::PARENT_KEY + ], [ + Model::PARENT_KEY => 'id' + ]); + $parent_table = (new $parent_class())->getTable(); + return $this->factory + ->find($parent_model_class) + ->wherer([ + [ + $relation_definitions[Model::PARENT_KEY], + $this->{$relation_definitions[Model::SELF_KEY]} + ] + ]) + ->one(); + } + public function siblingOf(string $sibling_model_class, string $connecting_table, array $relation_definitions): array { + $relation_definitions = $this->checkDefinitions($relation_definitions, [ + Model::SELF_KEY, + Model::SIBLING_KEY, + Model::SELF_CONNECT_KEY, + Model::SIBLING_CONNECT_KEY + ], [ + Model::SELF_KEY => 'id', + Model::SIBLING_KEY => 'id' + ]); + $sibling_table = (new $sibling_class())->getTable(); + return $this->find($sibling_model_class) + ->select([ + [ + $sibling_table, + '*' + ], + [ + $connecting_table, + '*' + ] + ]) + ->join([ + [ + $connecting_table, + implode('.', [ + $connecting_table, + $relation_definitions[Model::SIBLING_CONNECT_KEY] + ]), + implode('.', [ + $sibling_table, + $relation_definitions[Model::SIBLING_KEY] + ]) + ] + ]) + ->where([ + [ + implode('.', [ + $connecting_table, + $relation_definitions[Model::SELF_CONNECT_KEY] + ]), + $this->{$relation_definitions[Model::SELF_KEY]} + ] + ]) + ->many(); + } + + public function toArray(): array { + return $this->asArray(); + } +} diff --git a/common/Factory/Model.php b/common/Factory/Model.php new file mode 100644 index 0000000..c599d73 --- /dev/null +++ b/common/Factory/Model.php @@ -0,0 +1,365 @@ +{$p} = null; + } + return $this; + } + protected $class; + public function find(string $model_class): Model { + if (!class_exists($model_class)) { + throw new \InvalidArgumentException('El modelo ' . $model_class . ' no existe.'); + } + $this->class = $model_class; + return $this; + } + protected $columns; + public function select($columns): Model { + if ($this->columns === null) { + $this->columns = []; + } + if (!is_array($columns)) { + $columns = [$columns]; + } + foreach ($columns as $c) { + $col = (object) [ + 'table' => '', + 'column' => $c + ]; + if (is_array($c)) { + $col = (object) [ + 'table' => $c['table'] ?? $c[0], + 'column' => $c['column'] ?? $c[1] + ]; + } + $this->columns []= $col; + } + return $this; + } + protected $joins; + public function join(array $joins): Model { + if ($this->joins === null) { + $this->joins = []; + } + foreach ($joins as $j) { + $join = (object) [ + 'table' => $j['table'] ?? $j[0], + 'from' => $j['from'] ?? $j[1], + 'to' => $j['to'] ?? $j[2], + 'sym' => $j['sym'] ?? ($j[3] ?? '='), + 'alias' => $j['alias'] ?? '', + 'type' => strtolower($j['type']) ?? '', + 'params' => $j['params'] ?? '' + ]; + $this->joins []= $join; + } + return $this; + } + protected $conditions; + public function where(array $conditions): Model { + if ($this->conditions === null) { + $this->conditions = []; + } + foreach ($conditions as $c) { + $cond = (object) [ + 'column' => $j['column'] ?? $j[0], + 'value' => $j['value'] ?? $j[1], + 'sym' => strtolower($j['sym'] ?? ($j[2] ?? '=')), + 'type' => strtolower($j['type']) ?? '' + ]; + $this->conditions []= $cond; + } + return $this->conditions; + } + protected $grouping; + public function group($groups): Model { + if ($this->grouping === null) { + $this->grouping = []; + } + if (!is_array($groups)) { + $groups = [$groups]; + } + foreach ($groups as $g) { + $this->grouping []= $g; + } + return $this; + } + protected $ordering; + public function order($orders): Model { + if ($this->ordering === null) { + $this->ordering = []; + } + if (!is_array($orders)) { + $orders = [$orders]; + } + foreach ($orders as $o) { + $order = (object) [ + 'column' => $o, + 'direction' => 'asc' + ]; + if (is_array($o)) { + $order = (object) [ + 'column' => $o['column'] ?? $o[0], + 'direction' => strtolower($o['direction']) ?? $o[1] + ]; + } + $this->ordering []= $order; + } + return $this; + } + protected $limit; + public function limit(int $limit): Model { + $this->limit = $limit; + } + protected $offset; + public function offset(int $offset): Model { + $this->offset = $offset; + } + + protected function parseSelect(ORM $orm): ORM { + if ($this->columns === null) { + return $orm; + } + foreach ($this->columns as $col) { + $orm = $orm->select(trim(implode('.', $col), '.')); + } + return $orm; + } + protected function parseJoin(ORM $orm): ORM { + if ($this->joins === null) { + return $orm; + } + foreach ($this->joins as $join) { + $method = 'join'; + switch ($join->type) { + case 'raw': + $method = 'rawJoin'; + break; + case 'inner': + $method = 'innerJoin'; + break; + case 'left': + case 'left outer': + case 'left_outer': + case 'leftouter': + $method = 'leftOuterJoin'; + break; + case 'right': + case 'right outer': + case 'right_outer': + case 'rightouter': + $method = 'rightOuterJoin'; + break; + case 'full': + case 'full outer': + case 'full_outer': + case 'fullouter': + $method = 'fullOuterJoin'; + break; + } + if ($join->type == 'raw') { + $orm = $orm->{$method}($join->table, [$join->from, $join->symb, $join->to], $join->alias, $join->params); + } elseif ($join->alias === '') { + $orm = $orm->{$method}($join->table, [$join->from, $join->symb, $join->to], $join->alias); + } else { + $orm = $orm->{$method}($join->table, [$join->from, $join->symb, $join->to]); + } + } + return $orm; + } + protected function parseWhere(ORM $orm): ORM { + if ($this->conditions === null) { + return $orm; + } + foreach ($this->conditions as $cond) { + $method = 'where'; + switch ($cond->sym) { + case '<': + $method = 'whereLt'; + break; + case '<=': + $method = 'whereLte'; + break; + case '>': + $method = 'whereGt'; + break; + case '>=': + $method = 'whereGte'; + break; + case '!=': + $method = 'whereNotEqual'; + break; + case 'like': + $method = 'whereLike'; + break; + case 'not like': + case 'not_like': + case 'notlike': + $method = 'whereNotLike'; + break; + } + switch ($cond->type) { + case 'equal': + $method = 'whereEqual'; + break; + case 'not equal': + case 'not_equal': + case 'notequal': + $method = 'whereNotEqual'; + break; + case 'less': + case 'less than': + case 'less_than': + case 'lessthan': + case 'lt': + $method = 'whereLt'; + break; + case 'less equal': + case 'less_equal': + case 'lessequal': + case 'less than equal': + case 'less_than_equal': + case 'lessthanequal': + case 'lte': + $method = 'whereLte'; + break; + case 'greater': + case 'greater than': + case 'greater_than': + case 'greaterthan': + case 'gt': + $method = 'whereGt'; + break; + case 'greater equal': + case 'greater_equal': + case 'greaterequal': + case 'greater than equal': + case 'greater_than_equal': + case 'greaterthanequal': + case 'gte': + $method = 'whereGte'; + break; + case 'like': + $method = 'whereLike'; + break; + case 'not like': + case 'not_like': + case 'notlike': + $method = 'whereNotLike'; + break; + case 'in': + $method = 'whereIn'; + break; + case 'not in': + case 'not_in': + case 'notin': + $method = 'whereNotIn'; + break; + case 'raw': + $method = 'rawWhere'; + break; + } + $orm = $orm->{$method}($cond->column, $cond->value); + } + return $orm; + } + protected function parseGroup(ORM $orm): ORM { + if ($this->grouping === null) { + return $orm; + } + foreach ($this->grouping as $group) { + if (strpos($group, '(') !== false) { + $orm = $orm->groupByExpr($group); + } else { + $orm = $orm->groupBy($group); + } + } + return $orm; + } + protected function parseOrder(ORM $orm): ORM { + if ($this->ordering === null) { + return $orm; + } + foreach ($this->ordering as $order) { + if (strpos($order->column, '(') !== false) { + $orm = $orm->orderByExpr($order->column); + continue; + } + switch ($order->direction) { + case 'asc': + case 'ascending': + $orm = $orm->orderByAsc($order->column); + break; + case 'desc': + case 'descending': + $orm = $orm->orderByDesc($order->column); + break; + } + } + return $orm; + } + protected function parseLimit(ORM $orm): ORM { + if ($this->limit === null) { + return $orm; + } + return $orm->limit($this->limit); + } + protected function parseOffset(ORM $orm): ORM { + if ($this->offset === null) { + return $orm; + } + return $orm->offset($this->offset); + } + + public function one(): ModelInterface { + $result = $this->build()->findOne(); + $result->setFactory($this); + return $result; + } + public function many(): array { + $results = $this->build()->findMany(); + foreach ($results as &$r) { + $r->setFactory($this); + } + return $results; + } + public function array(): array { + $results = $this->build()->findArray(); + return $results; + } + + protected function build(): ORM { + $orm = BaseModel::factory($this->class); + $methods = [ + 'select', + 'join', + 'where', + 'group', + 'order', + 'limit', + 'offset' + ]; + foreach ($methods as $m) { + $method = 'parse' . ucfirst($m); + $orm = $this->{$method}($orm); + } + return $orm; + } +}