public function afterDelete(Model &$model) { $class = get_class($model); $relations = $class::getRelations(); foreach ($relations as $name => $relation) { switch ($relation['type']) { case Model::MANY_TO_MANY: $linkTable = $class::getRelationLinkName($relation); $query = new QueryBuilder(); $query->delete()->table($linkTable)->where($class::getManyToManyThisLinkColumnName() . '=:id'); $query->params([':id' => $model->getPk()]); $class::getDbConnection()->execute($query); } } return true; }
/** * @param string|\T4\Dbal\QueryBuilder $query * @param array $params * @return \T4\Dbal\Statement */ public function query($query, array $params = []) { if ($query instanceof QueryBuilder) { $params = array_merge($params, $query->getParams()); $query = $query->getQuery($this->getDriver()); } $statement = $this->pdo->prepare($query); $statement->execute($params); return $statement; }
public function actionSetupBlock($sectionId, $blockPath) { $query = new QueryBuilder(); $query->select('MAX(' . Block::getDbDriver()->quoteName('order') . ')')->from(Block::getTableName())->where('section=:section')->params([':section' => $sectionId]); $maxOrder = (int) $this->app->db->default->query($query)->fetchScalar(); $block = new Block(); $block->section = $sectionId; $block->path = $blockPath; $block->template = ''; $params = []; foreach ($this->app->config->blocks->{$blockPath}->options as $optionName => $options) { $params[$optionName] = $options->default; } $block->options = json_encode($params); $block->order = $maxOrder + 10; if (false !== $block->save()) { $this->data->id = $block->getPK(); $this->data->result = true; } else { $this->data->result = false; } }
/** * Создает пустое место в дереве заданной ширины * Место создается ВНУТРИ элемента с заданным rgt как ПОСЛЕДНИЙ его потомок * @param \T4\Dbal\Connection $connection * @param string $table * @param int $rgt * @param int $width */ protected function expandTreeBeforeRgt(Connection $connection, $table, $rgt, $width) { $query = new QueryBuilder(); $query->update()->table($table)->values(['__lft' => 'CASE WHEN __lft>=:rgt THEN __lft + (:width + 1) ELSE __lft END', '__rgt' => 'CASE WHEN __rgt>=:rgt THEN __rgt + (:width + 1) ELSE __rgt END'])->where('__lft>=:rgt OR __rgt>=:rgt')->order('__lft DESC'); $query->params([':rgt' => $rgt, ':width' => $width]); $connection->execute($query); }
public function countAllByColumn($class, $column, $value, $options = []) { $query = new QueryBuilder(); $query->select('COUNT(*)')->from($class::getTableName())->where('`' . $column . '`=:value')->params([':value' => $value]); return $class::getDbConnection()->query($query->getQuery($this), $query->getParams())->fetchScalar(); }
protected function delete(Migration $migration) { $query = new QueryBuilder(); if ($this->app->db->default->getDriverName() == 'mysql') { $column = '`time`'; } else { $column = '"time"'; } $query->delete(self::TABLE_NAME)->where($column . '=:time')->params([':time' => $migration->getTimestamp()]); $this->app->db->default->execute($query); }
/** * "Ленивое" получение данных связи для моделей * @param string $key * @param array $options * @return mixed * @throws Exception */ public function getRelationLazy($key, $options = []) { $class = get_class($this); $relations = $class::getRelations(); if (empty($relations[$key])) { throw new Exception('No such column or relation: ' . $key . ' in model of ' . $class . ' class'); } $relation = $relations[$key]; switch ($relation['type']) { case $class::BELONGS_TO: $relationClass = $relation['model']; $link = $class::getRelationLinkName($relation); $subModel = $relationClass::findByPK($this->{$link}); if (empty($subModel)) { return null; } else { return $relationClass::findByPK($this->{$link}); } break; case $class::HAS_ONE: $relationClass = $relation['model']; $link = $class::getRelationLinkName($relation); return $relationClass::findByColumn($link, $this->getPk(), $options); break; case $class::HAS_MANY: $relationClass = $relation['model']; $link = $class::getRelationLinkName($relation); return $relationClass::findAllByColumn($link, $this->getPk(), $options); break; case $class::MANY_TO_MANY: $relationClass = $relation['model']; $linkTable = $class::getRelationLinkName($relation); $pivots = $relationClass::getPivots($class, $key); if (!empty($pivots)) { $pivotColumnsSql = ', ' . implode(', ', array_map(function ($x) { return 'j1.' . $x; }, array_keys($pivots))); } else { $pivotColumnsSql = ''; } $query = new QueryBuilder(); $query->select('t1.*' . $pivotColumnsSql)->from($relationClass::getTableName())->join($linkTable, 't1.' . $relationClass::PK . '=j1.' . static::getManyToManyThatLinkColumnName($relation), 'right')->where('(j1.' . static::getManyToManyThisLinkColumnName() . '=:id)' . (isset($options['where']) ? ' AND (' . $options['where'] . ')' : '')); if (isset($options['order'])) { $query->order($options['order']); } $query->params([':id' => $this->getPk()]); $result = $relationClass::getDbConnection()->query($query)->fetchAll(\PDO::FETCH_CLASS, $relationClass); if (!empty($result)) { $ret = new Collection($result); $ret->setNew(false); return $ret; } else { return new Collection(); } } }
/** * TODO: много лишних isset, которые всегда true по определению * Сохранение полей модели без учета связей, требующих ID модели * @param Model $model * @return Model */ protected function saveColumns(Model $model) { $class = get_class($model); $columns = $class::getColumns(); $relations = $class::getRelations(); $cols = []; $sets = []; $data = []; foreach ($columns as $column => $def) { if (isset($model->{$column}) && !is_null($model->{$column})) { $cols[] = $column; $sets[$column] = ':' . $column; $data[':' . $column] = $model->{$column}; } elseif (isset($def['default'])) { $sets[$column] = ':' . $column; $data[':' . $column] = $def['default']; } } foreach ($relations as $rel => $def) { switch ($def['type']) { case $class::BELONGS_TO: $column = $class::getRelationLinkName($def); if (!in_array($column, $cols)) { if (isset($model->{$column}) && !is_null($model->{$column})) { $sets[$column] = ':' . $column; $data[':' . $column] = $model->{$column}; } elseif (isset($model->{$rel}) && $model->{$rel} instanceof Model) { $sets[$column] = ':' . $column; $data[':' . $column] = $model->{$rel}->getPk(); } } break; } } $connection = $class::getDbConnection(); if ($model->isNew()) { $sql = new QueryBuilder(); $sql->insert($class::getTableName())->values($sets); $connection->execute($sql, $data); $model->{$class::PK} = $connection->lastInsertId(); } else { $sql = new QueryBuilder(); $sql->update($class::getTableName())->values($sets)->where($class::PK . '=' . $model->getPk()); $connection->execute($sql, $data); } return $model; }
public function __call($method, $argv) { /** @var \T4\Orm\Model $model */ $model = $argv[0]; array_shift($argv); /* @var \T4\Orm\Model $class */ $class = get_class($model); $tableName = $class::getTableName(); /** @var \T4\Dbal\Connection $connection */ $connection = $class::getDbConnection(); switch ($method) { case 'refreshTreeColumns': $sql = new QueryBuilder(); $sql->select(['__lft', '__rgt', '__lvl', '__prt'])->from($tableName)->where('t1.' . $class::PK . '=:id'); $columns = $connection->query($sql, [':id' => $model->getPk()])->fetch(); $model->merge($columns); return $model; case 'getTreeWidth': if ($model->isNew()) { return 1; } else { return $model->__rgt - $model->__lft; } case 'findAllParents': $query = new QueryBuilder(); $query->select('*')->from($class::getTableName())->where('__lft<:lft AND __rgt>:rgt')->order('__lft')->params([':lft' => $model->__lft, ':rgt' => $model->__rgt]); return $class::findAllByQuery($query); case 'findAllChildren': $query = new QueryBuilder(); $query->select('*')->from($class::getTableName())->where('__lft>:lft AND __rgt<:rgt')->order('__lft')->params([':lft' => $model->__lft, ':rgt' => $model->__rgt]); return $class::findAllByQuery($query); case 'hasChildren': $query = new QueryBuilder(); $query->select('COUNT(*)')->from($class::getTableName())->where('__lft>:lft AND __rgt<:rgt')->order('__lft')->params([':lft' => $model->__lft, ':rgt' => $model->__rgt]); return 0 != $connection->query($query)->fetchScalar(); case 'findSubTree': $query = new QueryBuilder(); $query->select('*')->from($class::getTableName())->where('__lft>=:lft AND __rgt<=:rgt')->order('__lft')->params([':lft' => $model->__lft, ':rgt' => $model->__rgt]); return $class::findAllByQuery($query); case 'hasPrevSibling': $query = new QueryBuilder(); $query->select('COUNT(*)')->from($class::getTableName())->where('__rgt<:lft AND __prt=:prt')->params([':lft' => $model->__lft, ':prt' => $model->__prt]); return 0 != $connection->query($query)->fetchScalar(); case 'getPrevSibling': $query = new QueryBuilder(); $query->select('*')->from($class::getTableName())->where('__rgt<:lft AND __prt=:prt')->order('__lft DESC')->limit(1)->params([':lft' => $model->__lft, ':prt' => $model->__prt]); return $class::findByQuery($query); case 'hasNextSibling': $query = new QueryBuilder(); $query->select('COUNT(*)')->from($class::getTableName())->where('__lft>:rgt AND __prt=:prt')->params([':rgt' => $model->__rgt, ':prt' => $model->__prt]); return 0 != $connection->query($query)->fetchScalar(); case 'getNextSibling': $query = new QueryBuilder(); $query->select('*')->from($class::getTableName())->where('__lft>:rgt AND __prt=:prt')->order('__lft')->limit(1)->params([':rgt' => $model->__rgt, ':prt' => $model->__prt]); return $class::findByQuery($query); case 'insertBefore': $element = $argv[0]; $this->insertModelBeforeElement($model, $element); return $model; break; case 'insertAfter': $element = $argv[0]; $this->insertModelAfterElement($model, $element); return $model; break; case 'moveToFirstPosition': $parent = $model->parent; if (empty($parent)) { $this->insertModelAsFirstRoot($model); } else { $this->insertModelAsFirstChildOf($model, $parent); } return $model; case 'moveToLastPosition': $parent = $model->parent; if (empty($parent)) { $this->insertModelAsLastRoot($model); } else { $this->insertModelAsLastChildOf($model, $parent); } return $model; } }
/** * Merges queries * * @param array|QueryBuilder|\T4\Core\IArrayable $prototype * @param string $operator AND, OR * @return self * @throws \InvalidArgumentException * @throws \DomainException */ public function merge($prototype, $operator = 'and') { if ($prototype instanceof QueryBuilder) { $params = $prototype->getParams(); $prototype = $prototype->toArray(); $prototype['params'] = $params; } elseif ($prototype instanceof \T4\Core\IArrayable) { $prototype = $prototype->toArray(); } if (!is_array($prototype) && !$prototype instanceof \ArrayAccess) { throw new \InvalidArgumentException('Invalid builder type!'); } if (!empty($this->mode) && !empty($prototype['mode']) && $this->mode !== $prototype['mode']) { throw new \DomainException('Query mode is not much!'); } if (!empty($prototype['select'])) { $this->select($prototype['select']); } if (!empty($prototype['from'])) { $this->from($prototype['from']); } if (!empty($prototype['where'])) { $this->where = empty($this->where) ? $prototype['where'] : "({$this->where}) {$operator} ({$prototype['where']})"; } foreach (['group', 'order', 'limit', 'offset'] as $property) { if (!empty($prototype[$property])) { $this->{$property} = $prototype[$property]; } } foreach (['joins', 'params', 'insertTables', 'updateTables', 'deleteTables', 'values'] as $arrayProperty) { if (!empty($prototype[$arrayProperty])) { $this->{$arrayProperty} = array_merge($this->{$arrayProperty} ?? [], $prototype[$arrayProperty]); } } return $this; }
/** * Удаление узла дерева * В данном методе удаляются все его подузлы * @param \T4\Orm\Model $model * @return bool */ public function afterDelete(Model &$model) { /** @var \T4\Orm\Model $class */ $class = get_class($model); $tableName = $class::getTableName(); /** @var \T4\Dbal\Connection $connection */ $connection = $class::getDbConnection(); $query = new QueryBuilder(); $query->delete()->table($tableName)->where('__lft>:lft AND __rgt<:rgt'); $query->params([':lft' => $model->__lft, ':rgt' => $model->__rgt]); $connection->execute($query); $this->removeFromTreeByLftRgt($connection, $tableName, $model->__lft, $model->__rgt); $model->__lft = 0; $model->__rgt = 0; $model->__lvl = 0; $model->__prt = 0; }