Друзья Помню тема уже подымалась эта, но до конца раскрыта не была. В framework Типа YII2 или LARAVEL Есть такая реализация при исполнении запросов к БД PHP: DB::table('tableName')->where('id','=', 'id')->get(); Такой вид запросов реализован в LARAVEL, примерно знаю что в yii2 Тоже самое... Так как реализовать подобные запроса. Так скажем объектного вида... PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 04.06.2016 * Time: 18:04 */ namespace App\Models; use System\Http\Model\Model; class Main extends Model { public $mass; public function mainModel () { $this->mass = $this->Select('tableName')->get(); // Не знаю что сделать чтобы можно было делать так... } } А теперь сама Model над которой я проделываю без результатные опыты PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 04.06.2016 * Time: 18:03 */ namespace System\Http\Model; use System\Db\Db; class Model { protected $tableName; public $Select; public $get; public function __construct ($tableName) { $this->tableName = $tableName; $this->Select = $this->Select(); } public function Select () { $this->get = $this->get(); } public function get () { $db = Db::getConnection(); $result = $db->prepare('SELECT * FROM tablename=:tablename'); $result->bindParam(':tablename',$this->tableName); $result->execute(); $result = $result->fetch(); return $result; } public function table ($tableName) { } }
Тут и пример на PHP есть, будут вопросы задавай. https://en.wikipedia.org/wiki/Method_chaining ты кстати get правильно сделал, а вот остальные методы нет. помни что конструктор не может возвращать данные, это на всякий случай, а в методе Select() последней строчкой надо вызывать return $this; тогда и будет возможность вызывать цепочку $this->Select('tableName')->get(); последний метод get() правильно делает возврат результата, то есть в этой цепочки все методы делают return $this и только последний возвращает результат.
Внутри там примерно такое: http://kramerblog.net/dinamicheskoe-formirovanie-where-dlya-sozdaniya-fil-trov/ (здесь без ООП, но суть идеи та же - накопить все условия в массив, потом объединить). Ну про цепочку написали уже.
Вот я реализовал как у себя, ORM оч просто конечно, но для старта пойдёт. PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 04.06.2016 * Time: 18:04 */ namespace App\Models\Main; use System\Http\Model\Model; class Main extends Model { public function mainModel ($arr) { $this->table('Users')->Insert(['user_name' => 'Андрей', 'email' => 'zakoncv@bk.ru']); //Пример добавление данных в БД $result = $this->table('Users')->where(['id = 1', 'или' , 'groups = 0'])->get($arr); // Пример получения с помощью отбора данных из таблицы // Существует два варианта обновления строки в бд первый вы используете метод ->where ниже пример // И затем вы используете метод ->Up Для того чтобы передать ключ как имя столбца в таблицы и значение как то что вы хотите туда написать. $this->table('Users')->where(['id > 10'])->Update(['groups' => 1]); // И второй вариант без использования метода ->where напрямую. $this->table('Users')->Update( ['user_name' => 'Леонид'], // Здесь пишите Имя таблицы => И то чем заполняете . Как и в первом варианте ['id < 10'] // А тут начинается самое интересное, здесь пишите условие по которому оно выполняется. ); // Ну и на функции обновления всё Просто и со вкусом :D // Так ну и с функцией удаление всё тоже просто и красиво // В первом варианте мы будем использовать метод->table('Ваша таблица')->where и после метод удаления ->del $this->table('Users')->where(['id < 10'])->del(); // И во втором варианте мы удаляем с помощью одного метода ->table('Ваша таблица')->del() $this->table('Users')->del(['id < 90']); return $result; } } Это конечно уже готовые методы...
Только это не ORM, а DAO (Data Access Objects). ORM - это когда строки таблицы отображаются на объекты классов тем или иным образом.
ну твоя get() возвращает просто массив, а ORM это когда будет возвращён класс и там много чего можно нафаршировать, посмотри doctrine orm, ну и тот же Yii2 там есть пример DAO и ORM (Active Record)
Ага посмотрел почитал понял... Засёк... Интересно как это фича реализована... Это нужно за счёт наверно когда таблицу создаёшь происходит запись автоматическое создание объектов, со всеми данными о таблице что позволяет потом через методы обращаться на прямую и изменять данные?
Создание объектов происходит также при запросе. Посмотрите, как реализовано. Если схематично: PHP: class AR { protected $values; public function __construct($values) { $this->values = $values; } public static function tableName() { return ""; } static function find() { $rows = DB::select(static::tableName()); $result = []; foreach ($rows as $r) { $result[] = new static($r); } return $result; } public function __get($name) { return $this->values[$name]; } public function __set($name, $value) { $this->values[$name] = $value; } public function save() { /* код вставки или обновления записи в БД */ } /* Другие вкусности, типа методов для запроса по связанным таблицам */ } class Task extends AR { public static function tableName() { return "tasks"; } } $tasks = Task::find(); $task[0]->name = "Вася"; $task[0]->save(); Это очень упрощённо подход, используемый в yii2. Я не показал DB::select, но предполагается, что оно возвращает результат обычного select * from table; --- Добавлено --- А вообще, есть разные паттерны для отображения строк таблицы на объекты. Это - Active Record, он простой, но не без недостатков. Есть ещё Data Mapper, погуглите названия
PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 04.06.2016 * Time: 18:03 */ namespace System\Http\Model; use System\Db\Db; class Model { protected $tableName; protected $Select = 'SELECT '; protected $Insert = 'INSERT INTO '; protected $Update = 'UPDATE '; protected $Delete = 'DELETE FROM '; protected $where = ''; //Функция выбора Таблицы из БД. public function table($tableName) { $this->tableName = $tableName; return $this; } //Функция добавления данных в таблицу БД public function Insert($arr) { $db = Db::getConnection(); $stringIns = ''; foreach ($arr as $key => $value) { $stringIns = $stringIns.', '.$key.'="'.$value.'"'; }; $stringIns = trim($stringIns, ', '); $result = $db->query($this->Insert . $this->tableName . ' SET ' . $stringIns); } //Функция обновления данных в таблице БД public function Update($arr, $where = NULL) { $db = Db::getConnection(); $stringUp = ''; foreach ($arr as $key => $value) { $stringUp = $stringUp.', '.$key.'="'.$value.'"'; }; $stringUp = trim($stringUp, ', '); if ($where != Null) { $this->where = $this->whereIs($where); } $db->query($this->Update . $this->tableName . ' SET ' . $stringUp . $this->where); } //Функция Удаления данных из таблицы в БД public function del($where = NULL) { $db = Db::getConnection(); if ($where != Null) { $this->where = $this->whereIs($where); } $db->query($this->Delete . $this->tableName . $this->where); } //Общая функция условия при выполнении запроса к таблицы БД public function whereIs ($arr) { $stringVal = ''; foreach ($arr as $key => $value) { if ($value == 'и') { $value = '&&'; }; if ($value == 'или') { $value = '||'; }; $stringVal = $stringVal . ' ' . $value ; } $stringVal = trim($stringVal, ' ,'); $this->where = ' WHERE '. $stringVal; return $this->where; } //Функция условия при выполнении запроса к таблицы БД по средством цепочки public function where($arr) { $this->where = $this->whereIs($arr); return $this; } //Функция получения строк из таблицы public function get($arr) { $db = Db::getConnection(); $stringVal = ''; foreach ($arr as $key => $value) { $stringVal = $stringVal . ' ,'. $value ; }; $stringVal = trim($stringVal, ' ,'); $result = $db->prepare($this->Select . $stringVal . ' FROM ' . $this->tableName . $this->where); $result->execute(); $base = $result->fetchAll(); return $base; } } Вот мой обработчик к базе с запросом, через методы. Думаешь такой вариант подхода плох? Скажите если вдруг по коду есть замечания. С удовольствием улучшу его. --- Добавлено --- PHP: namespace App\Database; use System\Db\createdDB; class User { public function createUser () { $newTable = new createdDB(__CLASS__); // То есть вот так норм передаю а как мне сделать вот так new createdDB() И чтобы у меня // в другом классе появился тек класс где создаю экземпляр класса.... Не могу понять там он вызывыет класс того пространства :( $newTable->int('id', '123'); $newTable->varchar('name', '150'); $newTable->varchar('email', '150'); $newTable->save(); } } ... Я чё то уже со всем поехал.. Ваще врубится не могу... Как мне тек класс передать в другой класс... Не вызывая его при этом в тек классе. PHP: public function __construct($tableName) { $className = $tableName; $className = explode('\\', $className); $className = array_pop($className); $this->tablename = $className; } Вот в принципе где я принимаю тот класс Я просто не знаю как вообще можно ли так сделать... --- Добавлено --- Решил данную задачу таким путём PHP: public function __construct() { $className = get_class($this); $className = explode('\\', $className); $className = array_pop($className); $this->tablename = $className.' ('; } --- Добавлено --- Теперь я могу создавать таблицы таким путём ! PHP: namespace App\Database; use System\Db\createdDB; class User extends createdDB { public function __construct() { parent::__construct(); } public function createUser () { $this->int('id', '123'); $this->varchar('name', '150'); $this->varchar('email', '150'); $this->save(); } } А имя таблицы будет имя класса в котором вы создаёте таблицу --- Добавлено --- Я жажду критикичё все молчат то... Что настолько всё плохо что даже писать никто не хочет?)))
Да особо плохового ничего нету. Только это не ORM и не ActiveRecord. Только уязвимости - нигде ничего не экранируешь.
Это же я не запускаю как движок... Это просто система заноса в бд... А дальше уже с этими методами будут работать я и другие разрабы... Там уже где не посредственно будут внос делать переменных... Которые несут пользовательские значения, там пусть и занимаются защитой... Уберают лишние символы всякие вредные и тд... --- Добавлено --- А что это ? --- Добавлено --- Таким же образом в yii добавляются новые таблицы... --- Добавлено --- Только у себя я ограничил, здесь таблицы будут добавляться только в дочерних классах имеющих наследовниe от createdDB --- Добавлено --- Я вроде делал следуя паттерну ActiveRecord.... Или я просто напрочь его понял не правильно... --- Добавлено --- Я могу переделать это на статические функции и реализация будет ваще 1 к 1 как в YII2 или LARAVEL 5 Только мне кажется я сделал круче не надо ни каких артисанов... Блин и писать чёто в консоле типа php artisan там и тд... Я сделал так что просто, создаёшь класс который наследуется, вписываешь параметры таблицы, и всё. Дальше просто в браузере вкладку надо будет открыть сайт слешь /installDB и всё все таблицы созданы которые прописали...
Паттерн ActiveRecord - это когда строка таблицы преобразуется в экземпляр класса. А у тебя постоянно туда-сюда гоняются обычные массивы. Получилось DAO, я уже писал. Плохая идея. Во-первых, никаких "вредных символов" из запроса убирать не надо, а во-вторых экранировать надо как можно ближе к запросу, чтобы точно всё было экранировано. Или использовать параметрические запросы.
Мне кажется наоборот, если хакер пытается взломать и попытается, положить базу вводом, то нужно экранировать на уровне ввода... И не пропускать в код символы. Не нужные... --- Добавлено --- У меня проблема появилась в чём причина не знаю... Если есть где грубая ошибка подскажи плиз... PHP: public function __construct() { $className = get_class($this); $className = explode('\\', $className); $className = array_pop($className); echo $className.'<br>'; $this->tablename = $className.' ('; } public function save () { $db = Db::getConnection(); //echo $this->sql.$this->primary.' )'; $db->query($this->sql.$this->primary.' )'); echo 'Успех <br>'; } Дальше сейчас покажу как вызываю методы... PHP: $Users = new User(); $Users->createUser(); $demoTest = new demoTable(); $demoTest->create(); Вот класс User PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 07.06.2016 * Time: 14:33 */ namespace App\Database; use System\Db\createdDB; class User extends createdDB { public function __construct() { parent::__construct(); } public function createUser () { $this->int('id', '123'); $this->varchar('name', '150'); $this->varchar('email', '150'); $this->save(); } } Вот класс demoTable PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 07.06.2016 * Time: 18:02 */ namespace App\Database; use System\Db\createdDB; class demoTable extends createdDB { public function __construct() { parent::__construct(); } public function create () { $this->int('id','11'); $this->varchar('var_name', '255'); $this->integer('number_group', '11', '0'); $this->integer('number_test', '11'); $this->timestamp('time_Desteny'); $this->save(); } } Так на экране видно так: User Успех demoTable Успех А добавляется всё тупо в таблицу User.... За место создания... Другой таблицы
Основы вообще не знаешь. Нафига свою ORM пытаться нарисовать. Если все данные экранируются, то добавлять в базу данных можно любую информацию. Никаких опасных символов тогда не существует. К тому же, если не экранировать (или не использовать подготовленные запросы), то базу можно взломать вполне безобидными вещами, типа в текст ввести "I don't speak English", и апостроф от don't поломает запрос. Короче, экранировать правильно близко к запросам. Иначе грош цена библиотеки. Я в yii вообще про экранирование не думаю - просто знаю, что если я запросы строю Query Builder'ом, то Yii2 сам обеспечит безопасность. Перед вставкой в базу htmlspecialchars не делается, это неграмотный совет, который можно найти в нете. Вызов этой функции - только при выводе данных, которые не должны содержать html
Я знаю что нужно экранировать. Но до этого ещё пока далеко. Как до луны... У меня адекватно в базу не влетают таблицы... Я выше написал что у меня не так получается... А вообще я могу получить и на каждый экземпляр по строке... Но а смысл... Когда передоваться во view будет массив, данных, я лучше сразу и передам уже готовый массив чтобы во view его отправить. Про экронизацию знаю, я сделаю все проверки на спец символы, но сначала нужно сделать нормальное создание таблиц, и экранирование нужно делать, только на insert и update... И всё... --- Добавлено --- Я принял твоё замечание спасибо! Я сделаю экранизацию... Можешь направить на путь правильный про экранирование, как это сделать лучше... Заменить спец символы, с помощью preg_replace на пустые строки ? Или же есть специальный функции в php для экранирования строк запросов перед отправкой ? Я использовал query, для создания таблиц, и для получения данных из db, а для запросов типа UPDATE и INSERT я использую prepare --- Добавлено --- И только их в принципе и буду экранировать... Не вижу смысла экранировать данные при создании таблиц, ибо не думаю, что разработчик сам себе враг. --- Добавлено --- Как получить массив методов, только указанного класса без наследуемых методов? А то функция PHP: get_class_methods('путь к классу') Она возвращает тупо все методы и наследуемые тоже --- Добавлено --- Уже всё сделал подругому...
А говоришь, что знаешь, как экранировать... Ты не показал, что у тебя под капотом, а так: https://secure.php.net/manual/ru/mysqli.real-escape-string.php https://secure.php.net/manual/ru/pdo.quote.php Ничего ниоткуда не вырезается. Эти функции перед символами добавляют слеши, чтоб показать mysql, что символы - часть строки, и не надо их воспринимать в их исходном значении А также подготовленные запросы: https://secure.php.net/manual/ru/pdo.prepare.php https://secure.php.net/manual/ru/mysqli.prepare.php --- Добавлено --- По твоей проблеме: кода мало. Что там у тебя за createdDB? --- Добавлено --- Смысл - потом реализовать автоматический запрос по связанным таблицам, как http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#relational-data. Это раз. А два, там могут быть другие методы в классах.
Как получить массив методов, только указанного класса без наследуемых методов? А то функция Поправочка. Я знаю что нужно экранировать ! --- Добавлено --- Но ведь также часто требуется и получать просто массив с данными, а не одну строчку с бд... --- Добавлено --- Пока не доделал его, покажу чуть позже...
Ну так посмотри мой пример кода: там вернётся массив объектов класса. Или доки по yii (или Laravel, там тот же Active Record вроде реализован)
Да я и старался сделать похожим на лару... --- Добавлено --- Ну я тебя понял . Я собрал DAO... Теперь надо собрать ORM --- Добавлено --- Лови CreatedDB PHP: <?php /** * Created by PhpStorm. * User: askanim * Date: 07.06.2016 * Time: 10:37 */ namespace System\Db; use System\Db\Db; class createdDB { protected $tablename; // Название таблицы // protected $integer; // Просто числовое значение protected $integerCount; protected $int; // Диапазон от -2 147 483 648 до 2 147 483 647 Если используете автоикнкремент protected $intCount; // Кол-во символов в int // protected $float; //Число с плавающей точкой двойной точности. protected $double; //Дробное число, хранящееся в виде строки. protected $boolen; // protected $date; // Дата в формате ГГГГ-ММ-ДД protected $timestamp; // Дата и время в формате timestamp. Однако при получении значения // поля оно отображается не в формате timestamp, // а в виде ГГГГММДДЧЧММСС, что сильно умаляет преимущества его использования в PHP // protected $datetime; // Дата и время в формате ГГГГ-ММ-ДД ЧЧ:ММ:СС protected $time; // Время в формате ЧЧ:ММ:СС // // protected $decimal; // Дробное число, хранящееся в виде строки. protected $char; // // Тип данных TEXT protected $varchar; // Может хранить не более 255 символов. protected $varCount; // Кол - во символов в VARCHAR // protected $tinytext; // Может хранить не более 255 символов. protected $text; // Может хранить не более 65 535 символов. protected $mediumtext; // Может хранить не более 16 777 215 символов. protected $longtext; // Может хранить не более 4 294 967 295 символов. // // Бинарные данные; // это почти то же самое, что и данные в формате TEXT, но только при поиске в них учитывается регистр символов. protected $blob; // Может хранить не более 65 535 символов. protected $mediumblob; // Может хранить не более 16 777 215 символов. protected $tinyblob; // Может хранить не более 255 символов. protected $longblob; // Может хранить не более 4 294 967 295 символов. // protected $primary = ''; // Указывает на уникальный ключ protected $null; // BLOD-данные не перекодируются автоматически, // если при работе с установленным соединением включена возможность перекодирования текста "на лету". protected $sql = 'CREATE TABLE '; // Функция сохранения запроса. public function __construct() { $className = get_class($this); $className = explode('\\', $className); $className = array_pop($className); $this->tablename = $className.' ('; $this->sql = $this->sql.$this->tablename; } public function save () { $db = Db::getConnection(); $db->query($this->sql.$this->primary.' )'); } public function int($int, $count = '11') { $this->intCount = $count; $this->int = $int; $this->primary = $this->primary($int); $this->sql = $this->sql.' '.$this->int.' INT('.$this->intCount.') NOT NULL AUTO_INCREMENT, '; } public function integer ($integer, $count = '11', $default = Null, $null = 'not null') { $this->integerCount = $count; $this->integer = $integer; if ($null == 'null') { $this->null = $this->null(FALSE); } elseif ($null == 'not null') { $this->null = $this->null(TRUE); } if ($default != Null) { $default = ' DEFAULT '.$default; } $this->sql = $this->sql.' '.$this->integer.' INT('.$this->integerCount.') '.$this->null.$default.', '; } public function varchar($name, $count = '255', $null = 'null') { $this->varchar = $name; $this->varCount = $count; if ($null == 'null') { $this->null = $this->null(FALSE); } elseif ($null == 'not null') { $this->null = $this->null(TRUE); } $this->sql = $this->sql.$this->varchar.' VARCHAR('.$this->varCount.') '.$this->null.', '; } public function text($name, $null = 'not null') { $this->text = $name; if ($null == 'null') { $this->null = $this->null(FALSE); } elseif ($null == 'not null') { $this->null = $this->null(TRUE); } $this->sql = $this->sql.$this->text.' TEXT '.$this->null.', '; } public function timestamp($name, $null = 'not null' ,$default = ' DEFAULT CURRENT_TIMESTAMP') { if ($null == 'null') { $this->null = $this->null(FALSE); } elseif ($null == 'not null') { $this->null = $this->null(TRUE); } $this->timestamp = $name; $this->sql = $this->sql.$this->timestamp.' TIMESTAMP '.$this->null.$default.', '; } public function primary ($val) { $this->primary = $val; return 'PRIMARY KEY('.$val.')'; } public function null ($try) { if($try === TRUE) { $this->null = 'NOT NULL'; return $this->null; } elseif ($try === FALSE) { $this->null = 'NULL'; return $this->null; } } }