/** * все сохранение делается внутри транзакции (включая beforeSave и afterSave), если будет исключение - транзакция будет откачена PDO */ public function save() { ActiveRecordHelper::exceptionIfObjectIsIncompatibleWithActiveRecord($this); $obj_class_name = get_class($this); $obj_db_id = $obj_class_name::DB_ID; $transaction_is_my = false; if (!DBWrapper::inTransaction($obj_db_id)) { DBWrapper::beginTransaction($obj_db_id); $transaction_is_my = true; } $this->beforeSave(); \OLOG\Model\ActiveRecordHelper::saveModelObj($this); // не вызываем afterSave если это вызов save для этого объекта изнутри aftersave этого же объекта (для предотвращения бесконечного рекурсивного вызова afterSave) static $__inprogress = []; $inprogress_key = FullObjectId::getFullObjectId($this); if (!array_key_exists($inprogress_key, $__inprogress)) { $__inprogress[$inprogress_key] = 1; $this->afterSave(); unset($__inprogress[$inprogress_key]); } // комитим только если мы же и стартовали транзакцию (на случай вложенных вызовов) if ($transaction_is_my) { DBWrapper::commitTransaction($obj_db_id); } }
/** * находит в указанном контексте (т.е. для набора пар поле - значение) объект с максимальным весом, меньшим чем у текущего, и меняет текущий объект с ним весами * т.е. объект поднимается на одну позицию вверх если сортировать по возрастанию веса * @param array $extra_fields_arr */ public function swapWeights($extra_fields_arr = array()) { $current_class_name = self::getMyClassName(); $current_item_weight = $this->getWeight(); $where_arr = array('weight < ?'); $params_arr = array($current_item_weight); if (!empty($extra_fields_arr)) { foreach ($extra_fields_arr as $extra_field_name => $extra_field_value) { $extra_field_name = preg_replace('|[^a-zA-Z0-9_]|', '', $extra_field_name); if (is_null($extra_field_value)) { $where_arr[] = $extra_field_name . ' is null'; } else { $where_arr[] = $extra_field_name . '=?'; $params_arr[] = $extra_field_value; } } } $sql = 'SELECT id FROM ' . self::DB_TABLE_NAME . ' WHERE ' . implode(' AND ', $where_arr) . ' ORDER BY weight DESC, id DESC LIMIT 1'; $object_to_swap_weights_id = \OLOG\DB\DBWrapper::readField(self::DB_ID, $sql, $params_arr); if (!$object_to_swap_weights_id) { return; } $object_to_swap_weights_obj = $current_class_name::factory($object_to_swap_weights_id); $object_to_swap_weights_weight = $object_to_swap_weights_obj->getWeight(); $this->setWeight($object_to_swap_weights_weight); $this->save(); $object_to_swap_weights_obj->setWeight($current_item_weight); $object_to_swap_weights_obj->save(); }
public static function getIdsArrForVocabularyIdByCreatedAtDesc($value, $offset = 0, $page_size = 30) { if (is_null($value)) { return \OLOG\DB\DBWrapper::readColumn(self::DB_ID, 'select id from ' . self::DB_TABLE_NAME . ' where vocabulary_id is null order by created_at_ts desc limit ' . intval($page_size) . ' offset ' . intval($offset)); } else { return \OLOG\DB\DBWrapper::readColumn(self::DB_ID, 'select id from ' . self::DB_TABLE_NAME . ' where vocabulary_id = ? order by created_at_ts desc limit ' . intval($page_size) . ' offset ' . intval($offset), array($value)); } }
public function testAfterDeleteAndTransaction() { \PHPModelDemo\ModelDemoConfig::init(); // нормальное удаление модели $obj = new \Tests\TestModel(); $obj->setThrowExceptionAfterDelete(true); $obj->save(); $obj_id = $obj->getId(); $this->expectException(\Exception::class); $this->expectExceptionMessage('After delete'); $obj->delete(); $test_model_ids_arr = \OLOG\DB\DBWrapper::readColumn(\Tests\TestModel::DB_ID, 'select id from ' . \Tests\TestModel::DB_TABLE_NAME . ' where id = ?', array($obj_id)); $this->assertEquals(1, count($test_model_ids_arr)); // проверяем что запись в БД осталась, т.е. транзакция с удалением была откачена }
/** * Тест проверяет создание, сохранение, загрузку и удаление объекта через activeRecord и factory */ public function testSaveLoadDelete() { \PHPModelDemo\ModelDemoConfig::init(); $test_title = rand(1, 10000); $new_model = new \Tests\TestModel(); $new_model->setTitle($test_title); $new_model->save(); $test_model_id = $new_model->getId(); $this->assertNotEmpty($test_model_id); // тестирует генерацию непустого идентификатора модели при первом сохранении $loaded_model_obj = \Tests\TestModel::factory($test_model_id); $this->assertEquals($test_title, $loaded_model_obj->getTitle()); // тестируем совпадение заголовков сохраненной и загруженной модели $loaded_model_obj->delete(); $test_model_ids_arr = \OLOG\DB\DBWrapper::readColumn(\Tests\TestModel::DB_ID, 'select id from ' . \Tests\TestModel::DB_TABLE_NAME . ' where id = ?', array($test_model_id)); $this->assertEquals(0, count($test_model_ids_arr)); // проверяем что записей с таким ИД в таблице нет }
public static function getAllIdsArrByCreatedAtDesc() { $ids_arr = \OLOG\DB\DBWrapper::readColumn(self::DB_ID, 'select id from ' . self::DB_TABLE_NAME . ' order by created_at_ts desc'); return $ids_arr; }
public static function getAllIdsArrByCreatedAtDesc($offset = 0, $page_size = 30) { $ids_arr = \OLOG\DB\DBWrapper::readColumn(self::DB_ID, 'select id from ' . self::DB_TABLE_NAME . ' order by created_at_ts desc limit ' . intval($page_size) . ' offset ' . intval($offset)); return $ids_arr; }
public static function process_db($db_id) { // checking DB connectivity $db_obj = null; try { $db_obj = \OLOG\DB\DBFactory::getDB($db_id); } catch (\Exception $e) { echo $e->getMessage() . "\n\n"; } if (!$db_obj) { echo CliUtil::delimiter(); echo "Can't connect to database " . $db_id . "\n"; echo "Probable problems:\n"; echo "- misconfiguration. App config for database:\n"; //echo var_export(DBFactory::getConfigArr($db_id)) . "\n"; // TODO: fix echo "- database server not accessible\n"; echo "- database not created. It must be created manually.\n"; exit; } $executed_queries_sql_arr = []; try { $executed_queries_sql_arr = \OLOG\DB\DBWrapper::readColumn($db_id, 'select sql_query from ' . self::EXECUTED_QUERIES_TABLE_NAME); } catch (\Exception $e) { echo CliUtil::delimiter(); echo "Can not load the executed queries list from " . self::EXECUTED_QUERIES_TABLE_NAME . " table:\n"; echo $e->getMessage() . "\n\n"; echo "Probably the " . self::EXECUTED_QUERIES_TABLE_NAME . " table was not created. Choose:\n"; echo "\tENTER to create table and proceed\n"; // TODO: constants echo "\tany other key to exit\n"; $command_str = CliUtil::readStdinAnswer(); // TODO: switch if ($command_str == '') { // TODO: constants \OLOG\DB\DBWrapper::query($db_id, 'create table ' . self::EXECUTED_QUERIES_TABLE_NAME . ' (id int not null auto_increment primary key, created_at_ts int not null, sql_query text) engine InnoDB default charset utf8'); } else { exit; } } $sql_arr = self::loadSqlArrForDB($db_id); foreach ($sql_arr as $sql) { if (!in_array($sql, $executed_queries_sql_arr)) { echo CliUtil::delimiter(); echo $sql . "\n"; // TODO: constants echo "\n"; echo "\t" . self::COMMAND_SKIP_QUERY . ": skip query now, do not mark as executed\n"; echo "\t" . self::COMMAND_IGNORE_QUERY . ": ignore query - mark as executed, but do not execute (you can execute one manually)\n"; echo "\tENTER execute query\n"; $command_str = CliUtil::readStdinAnswer(); switch ($command_str) { case '': \OLOG\DB\DBWrapper::query($db_id, $sql); \OLOG\DB\DBWrapper::query($db_id, 'insert into ' . self::EXECUTED_QUERIES_TABLE_NAME . ' (created_at_ts, sql_query) values (?, ?)', array(time(), $sql)); echo "Query executed.\n"; break; case self::COMMAND_IGNORE_QUERY: \OLOG\DB\DBWrapper::query($db_id, 'insert into ' . self::EXECUTED_QUERIES_TABLE_NAME . ' (created_at_ts, sql_query) values (?, ?)', array(time(), $sql)); echo "Query marked as executed without execution.\n"; break; case self::COMMAND_SKIP_QUERY: echo "Query skipped.\n"; break; default: //echo "Unknown command.\n"; throw new \Exception('unknown command'); break; // TODO: repeat entry? } } } }
/** * Возвращает одну страницу списка объектов указанного класса. * Сортировка: TODO. * Фильтры: массив $context_arr. * Как определяется страница: см. Pager. * @param $model_class_name Имя класса модели * @param $context_arr array Массив пар "имя поля" - "значение поля" * @return array Массив идентикаторов объектов. */ public static function getObjIdsArrForClassName($table_index_on_page, $model_class_name, $filters_arr, $order_by = '') { \OLOG\CheckClassInterfaces::exceptionIfClassNotImplementsInterface($model_class_name, \OLOG\Model\InterfaceLoad::class); $page_size = Pager::getPageSize($table_index_on_page); $start = Pager::getPageOffset($table_index_on_page); $db_table_name = $model_class_name::DB_TABLE_NAME; $db_id = $model_class_name::DB_ID; $db_id_field_name = CRUDFieldsAccess::getIdFieldName($model_class_name); $query_param_values_arr = array(); $where = ' 1 = 1 '; foreach ($filters_arr as $filter_obj) { /* if ($filter_obj instanceof InterfaceCRUDTableFilter) { $column_name = $filter_obj->getFieldName(); $operation_code = $filter_obj->getOperationCode(); $value = $filter_obj->getValue(); $column_name = preg_replace("/[^a-zA-Z0-9_]+/", "", $column_name); switch ($operation_code) { case CRUDTableFilter::FILTER_EQUAL: $where .= ' and ' . $column_name . ' = ? '; $query_param_values_arr[] = $value; break; case CRUDTableFilter::FILTER_IS_NULL: $where .= ' and ' . $column_name . ' is null '; break; case CRUDTableFilter::FILTER_LIKE: $where .= ' and ' . $column_name . ' like ? '; $query_param_values_arr[] = '%' . $value . '%'; break; case CRUDTableFilter::FILTER_IN: if (count($value)) { $in_placeholders_arr = []; foreach ($value as $in_single_value) { $in_placeholders_arr[] = '?'; $query_param_values_arr[] = $in_single_value; } $where .= ' and ' . $column_name . ' in (' . implode(', ', $in_placeholders_arr) . ') '; } break; default: throw new \Exception('unknown filter code'); } } else */ if ($filter_obj instanceof InterfaceCRUDTableFilter2) { list($filter_sql_condition, $filter_placeholder_values_arr) = $filter_obj->sqlConditionAndPlaceholderValue(); if ($filter_sql_condition != '') { $where .= ' and ' . $filter_sql_condition; } $query_param_values_arr = array_merge($query_param_values_arr, $filter_placeholder_values_arr); } elseif ($filter_obj instanceof InterfaceCRUDTableFilterInvisible) { list($filter_sql_condition, $filter_placeholder_values_arr) = $filter_obj->sqlConditionAndPlaceholderValue(); if ($filter_sql_condition != '') { $where .= ' and ' . $filter_sql_condition; } $query_param_values_arr = array_merge($query_param_values_arr, $filter_placeholder_values_arr); } else { throw new \Exception('filter doesnt implement InterfaceCRUDTableFilter nor InterfaceCRUDTableFilter2'); } } if ($order_by == '') { $order_by = $db_id_field_name; } $obj_ids_arr = \OLOG\DB\DBWrapper::readColumn($db_id, "select " . $db_id_field_name . " from " . $db_table_name . ' where ' . $where . ' order by ' . $order_by . ' limit ' . intval($page_size) . ' offset ' . intval($start), $query_param_values_arr); return $obj_ids_arr; }
/** * @param $login * @param $password_from_form * @return null */ public static function getUserIdByCredentials($login, $password_from_form) { $data = \OLOG\DB\DBWrapper::readObject(\OLOG\Auth\AuthConstants::DB_NAME_PHPAUTH, 'SELECT id, password_hash FROM ' . User::DB_TABLE_NAME . ' WHERE login = ?', array($login)); if ($data === false) { return null; } $password_check_result = password_verify($password_from_form, $data->password_hash); if (!$password_check_result) { return null; } return $data->id; }
public static function getPermissionIdsArrForUserId($value) { return \OLOG\DB\DBWrapper::readColumn(self::DB_ID, 'select permission_id from ' . self::DB_TABLE_NAME . ' where user_id = ?', array($value)); }
public function html($obj) { $field_name = $this->getFieldName(); $referenced_class_name = $this->getReferencedClassName(); $referenced_class_title_field = $this->getReferencedClassTitleField(); $field_value = CRUDFieldsAccess::getObjectFieldValue($obj, $field_name); $options_html_arr = ['<option value=""></option>']; // TODO: check referenced class interfaces $referenced_obj_ids_arr = \OLOG\DB\DBWrapper::readColumn($referenced_class_name::DB_ID, 'select ID from ' . $referenced_class_name::DB_TABLE_NAME . ' order by ID'); $options_arr = []; foreach ($referenced_obj_ids_arr as $id) { $obj = CRUDObjectLoader::createAndLoadObject($referenced_class_name, $id); $options_arr[$id] = CRUDFieldsAccess::getObjectFieldValue($obj, $referenced_class_title_field); } foreach ($options_arr as $value => $title) { $selected_html_attr = ''; if ($field_value == $value) { $selected_html_attr = ' selected'; } $options_html_arr[] = '<option value="' . $value . '"' . $selected_html_attr . '>' . $title . '</option>'; // TODO: sanitize } $html = ''; $select_element_id = 'js_select_' . rand(1, 999999); $html .= '<select id="' . Sanitize::sanitizeAttrValue($select_element_id) . '" name="' . Sanitize::sanitizeAttrValue($field_name) . '" class="form-control">' . implode('', $options_html_arr) . '</select>'; $html .= '<input type="hidden" id="' . Sanitize::sanitizeAttrValue($select_element_id) . '_is_null" name="' . Sanitize::sanitizeAttrValue($field_name) . '___is_null"/>'; ob_start(); ?> <script> var select_element = document.getElementById('<?php echo $select_element_id; ?> '); select_element.addEventListener( 'change', function(){ var select_element_id = document.getElementById('<?php echo $select_element_id; ?> '); var is_null_element = document.getElementById('<?php echo $select_element_id; ?> _is_null'); var value = select_element_id.options[select_element_id.selectedIndex].value; if (value == ''){ is_null_element.value = '1'; } else { is_null_element.value = ''; } } ); select_element.dispatchEvent(new Event('change')); // fire to initialize is_null input on display </script> <?php $html .= ob_get_clean(); return $html; }
/** * Удаление записи * @param $model_obj * @return \PDOStatement */ public static function deleteModelObj($model_obj) { self::exceptionIfObjectIsIncompatibleWithActiveRecord($model_obj); $model_class_name = get_class($model_obj); $db_id = $model_class_name::DB_ID; $db_table_name = $model_class_name::DB_TABLE_NAME; $db_id_field_name = self::getIdFieldName($model_obj); $reflect = new \ReflectionClass($model_obj); $property_obj = $reflect->getProperty($db_id_field_name); $property_obj->setAccessible(true); $model_id_value = $property_obj->getValue($model_obj); if ($model_id_value == '') { throw new \Exception('Deleting not saved object'); } $result = \OLOG\DB\DBWrapper::query($db_id, 'DELETE FROM ' . $db_table_name . ' where ' . $db_id_field_name . ' = ?', array($model_id_value)); //\OLOG\Logger\Logger::logObjectEvent($model_obj, 'DELETE'); return $result; }
<?php require_once '../vendor/autoload.php'; \PHPModelDemo\ModelDemoConfig::init(); echo '<div>MODELS <a href="/?a=add_model">+</a></div>'; // ACTIONS if (isset($_GET['a'])) { if ($_GET['a'] == 'add_model') { $new_model = new \PHPModelDemo\DemoModel(); $new_model->setTitle(rand(1, 1000)); $new_model->save(); } } // DISPLAY $models_ids_arr = \OLOG\DB\DBWrapper::readColumn(\PHPModelDemo\ModelDemoConfig::DB_NAME_PHPMODELDEMO, 'select id from ' . \PHPModelDemo\DemoModel::DB_TABLE_NAME . ' order by id desc'); echo '<ul>'; foreach ($models_ids_arr as $model_id) { $model_obj = \PHPModelDemo\DemoModel::factory($model_id); echo '<div>' . $model_obj->getTitle() . '</div>'; } echo '</ul>'; echo '<div>CONST MODELS <a href="/?a=add_constmodel">+</a></div>'; // ACTIONS if (isset($_GET['a'])) { if ($_GET['a'] == 'add_constmodel') { $new_model = new \PHPModelDemo\ConstTest(); $new_model->setTitle(rand(1, 1000)); $new_model->save(); } } // DISPLAY