sqlInterface()[$action])) ? $this->sqlInterface()[$action] : null; } /** * Перегружаєм метод щоб дати доступ до синонімів атрибутів * @return array */ public function attributes() { return array_merge(parent::attributes(), $this->internalServiceAttributes()); } /** * перегрузка гетера властивостей для підтримки мапінгу * @param string $name * @throws \Exception * @return mixed */ public function __get($name) { $value = null; try { $value = parent::__get($name); } catch(\Exception $e) { $mapping = $this->propertyMapping(); if(!$value && isset($mapping[$name])) { if (is_string($mapping[$name])) { $value = parent::__get($mapping[$name]); } else if (is_array($mapping[$name])) { $success = false; foreach ($mapping[$name] as $mappingItem) { if (parent::__isset($mappingItem)) { $value = parent::__get($mappingItem); $success = true; break; } } if(!$success) throw $e; } else { throw $e; } } } return $value; } public function exists($name) { try { $v = $this[$name]; return true; } catch(\Exception $e) { return false; } } /** * метод getAttribute($name) також повиннен підтримувати мапінг * @param string $name * @return mixed|null */ public function getAttribute($name) { $value = null; try { $value = $this->__get($name); } catch (\Exception $e) { ;; } return $value; } /** * перевіряє чи необхідно скидати поле в БД і записує результат в атрибут моделі для подальшого передавання у виклик SQL функції * @param string $resetName * @param string $name * @param bool $skipLastValue * @return bool */ public function checkAttributeForReset($resetName, $name, $skipLastValue = false) { $ret = false; if ('y' !== $this->getAttribute($resetName) || $skipLastValue) { $this->setAttribute($resetName, (isset($this[$name]) && '' !== $this[$name]) ? 'n' : 'y'); $ret = true; } return $ret; } /** * вертає об’єкт що представляє SQL команду виклику функції * @param string $sqlFunctionName * @param array $args * @return Command */ public function getExecFunctionQuery($sqlFunctionName, Array $args = null) { $functionSchema = new SqlFunctionSchema($sqlFunctionName); $argsQuerySection = $functionSchema->bindArgs( (is_array($args)) ? $args : $this); if ($functionSchema->hasError()) { foreach($functionSchema->errors() as $attribute => $error) { $this->addError($attribute, $error); } } if (false === $argsQuerySection) { return false; } $command = \Yii::$app->db->createCommand( (($functionSchema->isFunction()) ? 'select ' : 'call ') . $functionSchema->getFunctionName() . '(' . $argsQuerySection . ')'); return $command; } /** * Зручний метод виклику SQL функції * @param $sqlFunctionName * @param array $args * @return mixed */ public function execFunction($sqlFunctionName, Array $args = null) { $result = false; $command = $this->getExecFunctionQuery($sqlFunctionName, $args); if ($command) { $result = $command->execute(); } return $result; } /** * УВАГА!!!, по дефолту в Active Record параметр $attributes в методах update() та insert() * вказує які атрибути моделі мають зберегтись, всі інші ігноруються. Дана реалізація не підтримує цієї фічі. * @param bool $runValidation * @param array $attributes * @return bool|int|mixed * @throws \yii\base\InvalidParamException */ public function update($runValidation = true, $attributes = null) { $crudSqlFunctionName = $this->getSqlActionFunction('update'); if (!$crudSqlFunctionName) return parent::update($runValidation, $attributes); if ($attributes) throw new InvalidParamException(\Yii::t('api','ActiveSqlViewModel isn\'t support feature to save custom attributes')); if ($runValidation && !$this->validate($attributes)) { \Yii::info('Model not updated due to validation error.', __METHOD__); return false; } if (!$this->beforeSave(false)) { return false; } $ret = $this->execFunction($crudSqlFunctionName); $this->afterSave(false); return $ret; } /** * @param bool $runValidation * @param null $attributes * @return bool|int * @throws \yii\base\InvalidParamException */ public function insert($runValidation = true, $attributes = null) { $crudSqlFunctionName = $this->getSqlActionFunction('insert'); if (!$crudSqlFunctionName) return parent::insert($runValidation, $attributes); if ($attributes) throw new InvalidParamException(\Yii::t('api','ActiveSqlViewModel isn\'t support feature to save custom attributes')); if ($runValidation && !$this->validate($attributes)) { \Yii::info('Model not inserted due to validation error.', __METHOD__); return false; } if (!$this->beforeSave(false)) { return false; } $command = $this->getExecFunctionQuery($crudSqlFunctionName); $ret = false; if (!$this->hasErrors()) { $ret = $command->execute(); // todo первинний ключ може мати декілька колонок, тут це не враховано $statement = $command->pdoStatement->fetchAll(); $this->setAttribute($this->primaryKey()[0], $statement[0][0]); $this->setOldAttributes($this->getAttributes()); } $this->afterSave(false); return $ret; } /** * @return bool|int|mixed * @throws StaleObjectException */ public function delete() { $crudSqlFunction = $this->getSqlActionFunction('delete'); if (!$crudSqlFunction) return parent::delete(); $result = false; if ($this->beforeDelete()) { // we do not check the return value of deleteAll() because it's possible // the record is already deleted in the database and thus the method will return 0 $condition = $this->getOldPrimaryKey(true); $lock = $this->optimisticLock(); if ($lock !== null) { $condition[$lock] = $this->$lock; } $pkNames = $this->primaryKey(); $currentPrimaryKey = []; foreach ($pkNames as $pkName) { $currentPrimaryKey[$pkName] = $this[$pkName]; $this[$pkName] = $condition[$pkName]; } $result = $this->execFunction($crudSqlFunction); if ($lock !== null && !$result) { throw new StaleObjectException('The object being deleted is outdated.'); } foreach ($pkNames as $pkName) { $this[$pkName] = $currentPrimaryKey[$pkName]; } $this->setOldAttributes(null); $this->afterDelete(); } return $result; } }