SQL-запрос на PHP (Версия 0.2)

image

Внес изменения в свою реализацию класса для генерации SQL запросов по сравнению с прошлой версией. Однако прежде чем писать о них, хотелось сразу прояснить некоторые вопросы которые в первом посте остались, на мой взгляд, не раскрыты:

  1. Это не очередной QueryBuilder. Хотя синтаксис и выглядит похоже, но по сути работает это по-другому. QueryBuilder обычно выполняется в Runtime. Мой же код предварительно нужно компилировать и на выполнение запускать уже откомпилированную версию. В частности код с картинки будет скомпилирован в следующий код PHP:

    if (!isset($__query58)) {
        $__query58 = new \ML\SQL\Query(
             '0cfd84f430c39bb567ef7cd28bf36054', 
             array(
                      'server' => '***', 
                      'database' => '***', 
                      'user' => '***', 
                      'pass' => '***', 
                      'prefix' => 'ms-', 
                      'codepage' => 'utf8'
              )
        );
    }
    $__query58->sql = ' SELECT `profile`.`iduser` AS `profile.iduser`,`user`.`name` AS `user.name` FROM `ms-Users-Profile` AS `profile`LEFT OUTER JOIN `ms-Users-User` AS `user` ON `user`.`id`=`profile`.`iduser` WHERE (`profile`.`net`=\'VK\' OR `profile`.`net`=\'OK\') AND `user`.`sex`=\'M\'';
    $rows = $__query58->rows();

    При использовании переменных в качестве имен полей или значений они будут вставлены в запрос через вызовы специальных функций, которые исключат SQL-инъекцию.

  2. Для чего это нужно: я планирую сделать свой ORM (само собой с блекджеком и шлюхами) и PHPSQL будет являться абстрактным слоем для работы с БД. Планируется писать запросы в виде:

    select(id)->from(Users\User)->where(sex == 'M');

    Который затем будет компилировать в PHP код с промежуточной компиляцией в PHPSQL.

  3. Нет, такой подход с компиляцией файлов не будет тормозить работу. Достаточно сделать это один раз при первом обращении к файлу, а затем закешировать скомпилированный вариант и в дальнейшем работать уже с ним. Также это можно делать один раз при установке/обновлении движка через консоль. В этом случае нам вообще неважно сколько времени займет компиляция, главное чтобы итоговый файл работал быстро.

  4. Да, такой подход действительно может создавать проблемы в отладке. Потому что из-за сгенерированного кода при ошибке в коде номер строки ошибки будет соответствовать новому скомпилированному файлу, а не тому, в котором вы этот код набирали.На текущий момент я пока не придумал как это обойти. Если есть какие-то идеи — озвучьте их.

  5. Да, я в курсе что «вместо изобретения своего велосипеда лучше бы использовал что-то крутое, типа Doctrine|Eloquent|другой вариант». Но я пошел своим путем потому что 1) мне это интересно 2) мне это нравится.

  6. Почему не выложил на гибхаб: потому что данный класс использует функции из других моих библиотек, которые в свою очередь требуют еще библиотек и в общем это все занимает 17 мб исходных текстов, по которым нет справки. При этом из некоторых библиотек используется одна/две простенькие функции. На текущий момент пока не вижу смысла делать рефакторинг чтобы вынести этот функционал в отдельную небольшую папку для общего доступа. Будет интерес — выложу.

Ну а теперь перейдем к изменениям:

  • Самое основное нововведение — в новой редакции все константные строки интерпретируются как имена полей. Т.е. теперь вместо field(net) == 'VK' можно писать net == 'VK'. Для имен полей с символом тире в имени можно использовать оператор исполнения в обратных ковычках: `net-soc`== 'VK'. Функция field осталась, её можно использовать если имя поля, к примеру, находится в переменной: field($fieldname).

  • Исчезли функции вида _And, _Or. Теперь в условии нужно использовать вместо них операторы && и ||. Т.е. раньше было _Or(field(net)==value('VK'),field(net)==value('OK')), в новой редакции net=='VK' || net=='OK'

  • Добавилась поддержка JOIN запросов (на стартовой картинке как раз пример с ним).

  • Добавлен низкоуровневый метод Query, который позволяет создавать любые запросы,
    которые нельзя сделать с помощью специализированных функций. По сути это просто обычное формирование запроса с помощью конкатенации строк. Однако в методе можно использовать специальные функции, которые позволяют исключить SQL-инъекции.

  • Расширилась функция field. Теперь можно написать в виде field([<имя псевдонима таблицы>,]<имя поля>)[->as(<имя псевдонима поля>)]

  • Поддержка сортировки, группировки, условий агрегации

Для тех, кому интересно, тестовый полигон для компиляции кода находится по тому же адресу. Если найдете какую-то ошибку, пишите в комментариях. Пока это версия 0.2 так что ошибки весьма вероятны.

UPD: Судя по всему 90% комментаторов осилили только заголовок и картинку под ним и тут же начали писать комментарий в стиле «ваш велосипед гавно, лучше пишите на xxx/yyy/zzz». Хотя специально для таких был написан пункт 5.

UPD2: Добавил команды INSERT, UPDATE, DELETE. Также в команду SELECT добавил offset() и limit(). Так что теперь можно свою ORM делать.

UPD2.1: Добавил метод COUNT в SELECT. Если в нем указать переменную, то в неё вернется общее количество записей без учета команды LIMIT. Сам запрос тоже выполнится. Это для пагинаторов сделано.
Share post

Comments 58

    +4
    Как-то сложновато все, вы не находите?
    Получается мало понимать как написать запрос в консоли любимой СУБД, нужно еще и понять как его интерпретировать используя ваш подход.

    Нет проблем, если запрос простой.
    Но если он с кучей джоинов, подзапросов и т.д., то это уже будет куда сложнее.
    Для MySQL тут проще взять safemysql и передать все нужное через плейсхолдеры.

    Подождем более опытных коллег, возможно я не прав.
      0

      Очень часто использование QueryBuilder-a уместно и оправдано. Вопрос в том, насколько хорошо продумано его API, чтобы он покрывал максимальное количество возможных кейсов.


      Наиболее популярный случай, когда запрос дополняется джоинами и условиями в зависимости от внешних факторов. И, например для пагинатора, на основе одной сборки нужно построить два запроса: текущая страница, и общее кол-во страниц. Тут QueryBuilder серьезно упрощает код.


      Отличный QueryBuilder у Yii2.
      http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html
      Единственный минус — неотделим от самого фреймворка. Но в 2.1 планируют вынести в отдельный, не связанный с ядром Yii2, компонент.

        +1

        Не планируем, а рассматриваем возможность. Пока не ясно, будет это в 2.1 или позже.

      +4
      Как по мне, так велосипед с первой статьи не изменился. Единственный «плюс», который я вижу: переменные биндятся сразу на месте, а не в конце списком параметров. Даже если опустить удобство биндинга в конце, ваш вариант проигрывает тупо из-за неудобства написания SQL в вашем синтаксисе.

      Если уж так хочется достичь того же самого, сделайте проще:
      ML(«SELECT id, name FROM table WHERE name = {$name}»);

      Парсите такие строки и так же автоматически подменяйте переменные на безопасный биндинг параметров. Таким образом вы убьете всех зайцев разом: стандартный синтаксис, нормальная подсветка синтаксиса, IDE ни на что не ругается и вы получаете свой профит в виде биндига на месте.
        –3
        Самый первый вариант таким и был. В этом случае гораздо сложнее парсинг выражения. И каждый JOIN усложняет этот парсинг на порядок.
          +1

          На самом деле, нет. При правильном построении грамматики запроса последовательный разбор становится достаточно несложной задачей.

            0
            К примеру такой простой запрос:
            ML(«SELECT id, name FROM table WHERE {$name}={$fieldname}»);
            каким образом понять где у нас имя поля, а где имя значения? Т.е. уже нужны какие-то модификаторы для обозначения этого. Добавить к этому куски запроса, которые вставляются по запросу — уже получается не так просто.
            В теории — да, действительно просто. Поэтому и делал так. Как говорится, дьявол, он в деталях. И в данном случае этих деталей много. В результате требуется полноценный разборщик строки на лексемы и гораздо проще использовать парсер PHP.
              –1
              каким образом понять где у нас имя поля, а где имя значения?

              Да, придется разбирать SQL выражение. Но по-моему это был бы гораздо более интересный и полезный проект, т.к. то что вы предлагаете, как уже замечено — уже 1000 раз реализовано.
            +1
            Ну а как вы хотели?

            Сейчас вы пошли легким для себя, но бессмысленным для окружающих путем. Для «домашнего» использования это может и прокатит, а вот для массового — увы, нет.
          +6
          Оба поста выглядят как
          «Я вот придумал штуку, которую уже много раз делали, но я вам свой вариант все-равно не покажу.»
          • UFO just landed and posted this here
              +2
              Yii2 ActiveQuery — Лучше инструмента для этих целей не видел
                0

                И эти люди запрещают использовать хранимые процедуры в качестве application server. Глядя на картинки становится невыносимо печально, а ведь это простейший запрос. Чуть сложнее логика, не дай бог с временными таблицами в запросе и всё, в этой каше разобраться будет нереально. Попробуйте хранимые процедуры, вам понравится.

                  0

                  У автора просто QueryBuilder не очень хороший, да и не совсем Builder. С нормальным подходом будет как-то так:


                  $rows = Profile::find()
                      ->joinWith('user')
                      ->select(['profile.user_id', 'user.name'])
                      ->where(['user.sex' => 'M'])
                      ->andWhere(['profile.net' => ['VK', 'OK']])
                      ->asArray()
                      ->all();

                  Вместо небольших временных таблиц в языке есть массивы, да и работа с большими не сильно отличается от обычных.

                    0

                    Я немного про другое. Вместо универсального построителя любых запросов, использовать процедуры под конкретную задачу. Процедура называется, например, userprofile_get, и на вход ждёт два параметра _sex и _net. Тогда вызов будет что-то типа:


                    call userprofile_get (_sex='M', _net='VK;OK');

                    а внутри этой процедуры может быть какой угодно сложный запрос, который оставит код на PHP/Ruby/c++ лёгким.

                      +2

                      Проблема в том, что его надо поддерживать. А поддерживать код на обычном языке программирования гораздо проще.

                        0

                        Плюс, отсутствие версионного контроля и авторства изменений. Ну и контроль за бизнес логикой выходит за пределы приложения

                          0

                          Или версионный контроль есть, но сложные процедуры синхронизации репзитория типа git и самих БД в различных средах.

                    +2

                    Кто запрещает то? Если хранимая процедура дает серьезный выигрыш по совокупности факторов производительности и сопровождаемости, то почему бы и нет.


                    Просто их сложнее поддерживать в актуальном состоянии. Я про условия командной разработки, где актуальность кода гарантирует VCS, а структуры БД — миграции. И тут получается, что хранимая процедура — не рыба не мясо, не код и не структура. Изменения хранимок в миграциях держать неудобно, потому что это код, и неотъемлемая часть общей логики, и было бы неплохо иметь возможность с помощью Git-а посмотреть историю изменений в этом самом коде. Но и в sql-файлах их держать тоже не так удобно, потому что нет гарантий, что хранимка скомпилится, если миграциями не обновили структуру до совместимого с ней состояния.


                    Но, если в команде выработаны общие подходы по работе с хранимками, то нет проблем.

                    –1
                    Все писатели подобных QueryBuilder-ов упорно не понимают простую истину: чем сложнее запрос, тем проще написать его на SQL, без всяких QueryBuilder-ов.

                    Делайте проще — и в этом будет смысл. Постараетесь учесть все нюансы SQL — и вы изобретете заново SQL, но с дурными синтаксисом и кучей ограничений, запутаетесь и сделаете бессмысленный комбайн, который всё равно работает от силы в половине случаев.
                      +2

                      Спорное утверждение. Все сильно зависит от ситуации. Если ваш запрос сложный но монолитный, то да, проще на чистом SQL. А если наличие каких-то джоинов и условий зависит от внешних факторов, то без QueryBuilder-а сборка запроса превращается в дикую кашу из конкатенаций, где без пол литры и дебаггера не разобраться.

                        0
                        То, что вы написали — это очень простой запрос.
                        Говоря «сложный» я имею в виду, например, вложенные подзапросы, рекурсию или использование оконных функций. Пока что ни один QueryBuilder с таким не справляется — и не нужно!

                        Имхо, задача QueryBuilder-а это абстрагировать простые запросы от БД.
                        Пишем, например, $query->select('*')->from(table)->limit($n)->offset($m) — получаем для каждого драйвера конкретной базы корректно сформированный запрос с учетом диалекта (экранирование и прочее).

                        Других хороших применений не вижу.

                        P.S. Ваш пример идеально ложится сюда же.
                          0
                          Кстати, на тему абстрагирования от конкретной БД.
                          Теоретически конечно прикольно, но нужно ли всегда на практике?
                          Одно дело пилим продукт с возможностью инсталлирования третьими лицами, другое дело — свой сервис. А часто ли приходится менять в проекте БД?
                          По-моему в большинстве случаев использование этого слоя абстракции не оправдано.
                            0
                            Это имеет место если: Вы делаете сервис для себя например на MSSQL, и потом бац решили перейти на MySQL или Postgresql, да бывает такое, вот тут как раз универсальность и спасет (видел один такой проект).
                              0
                              Нужно.
                              Пример: система миграций в фреймворке. Она должна быть абстрагирована от БД.
                              Или метод findById() в какой-нибудь ORM библиотеке. Его нельзя не сделать абстрагированным.

                              Да и переход с одной БД на другую посередине жизненного цикла проекта мне доводилось проводить, и не раз.
                                +1

                                Даже если не пришлось менять, то порог вхождения в проект нового разработчика понижается. Вот у нас в продакшене несколько проектов на MySQL, PostgreSQL и MS SQL на одной ORM, но их хорошее знание будет лишь плюсом, необходимо знание одной Doctrine.

                                0

                                https://habrahabr.ru/post/327160/#comment_10188340


                                Говоря «сложный» я имею в виду, например, вложенные подзапросы, рекурсию или использование оконных функций.

                                Во многих случаях это решается кодом. Либо кодом с временными таблицами.


                                Других хороших применений не вижу.

                                Добавление частей запроса по условиям. Когда в зависимости от входных параметров надо сделать или не сделать join с таблицей и where по полю в этой таблице и возможно еще добавить поле в select. И передача этого результата в другую функцию, которая например добавит пагинацию. С конкатенацией в строку будет много дублирующихся проверок и разных поисков в этой строке. Еще полезен не сам QueryBuilder, а его сочетание с ORM, например, когда поля для join автоматически определяются.

                                  0

                                  С вложенными подзапросами типа WHERE id IN (SELECT ...) вполне справляются рекурсивно.


                                  Как вы предлагаете писать SQL запросы с поддержкой хотя бы синтаксической проверки SQL в IDE, если вид запроса зависит от каких-то параметров, например значений полей для фильтрации, выбранных пользователем?

                                  0

                                  а чем лучше будет с queryBuilder-oм? тоже самое придется написать


                                  Единственный плюс с queryBuilder-ом порядок условий не важен, но это тоже реализуемо, без нового заморского синтаксиса.


                                  Проблема всех query билдеров в том что надо учить новый синтаксис, вопрос зачем? я вначале представляю какой SQL запрос надо сделать, потом пытаюсь этот SQL преобразовать с помощью доки к библиотеке, зачем мне делать последний шаг и тратить время


                                  Простейшая обертка вроде:


                                  $user = $db->selectOneSQL('SELECT * FROM users WHERE id = :id', ['id' => 123]);
                                  var_dump($user); // структура в виде массива
                                  
                                  $users = $db->selectSQL('SELECT * FROM users'); // возвращаем statment с итератором
                                  foreach ($users as $user) {
                                     var_dump($user);
                                  }
                                  
                                  $user = $db->selectOne('users', ['id' => 123]);  // тот же самый результат что и первый запрос selectOneSQL
                                  
                                  $db->select('users'); // Тот же самый результат что у второго запроса selectSQL
                                  
                                  $db->query('UPDATE users SET email = :newValue WHERE id = :id', [
                                        'newValue' => 'test@mail.ru'
                                     ], [
                                        'id' => 123
                                     ]
                                  );
                                  
                                  // простейшая обертка над update делает тоже самое
                                  $db->update('users', ['email' => 'test@mail.ru'], ['id' => 123]);

                                  Чем такой подход хуже ?


                                  только я могу копировать запросы и отлаживать их при желание или делать explain или другие вещи ?


                                  При этом я знаю о ORM и даже иногда использую их только чем ваша библиотека лучше doctrine/dbal ?

                                    0
                                    Ничем не хуже. Приличная ORM библиотека должна позволять вам делать что-то вроде:

                                    $users = User::findAllByQuery(new Query('SELECT * FROM users WHERE ...'));
                                    и вернуть типизированную коллекцию объектов класса User

                                    И действительно в большинстве случаев этого более, чем достаточно.
                                  0

                                  Посмотрите как работает Linq в C# — там сложные запросы именно что пишутся на linq на порядок проще чем на голом SQL. Но это языковая фича, для PHP такого сделать в рамках библиотеки нельзя.

                                    0
                                    Писать запрос на SQL, это еще тот маразм, особенно в крупных проектах с большой командой.
                                    Программисты общаются на языке проекта, объектами оперировать куда как приятнее и понятнее.
                                    Все необходимое для понимания, читаем в маппинге и валидации. В будущем объекты тоже сыграют положительную роль.

                                    И не обязательно использовать QB, есть др. методологии.

                                    Встретился я тут год назад с проектом, где код на 800мб и SQL запросы зашиты в нем.
                                    Найти и проанализировать нужные запросы, оч. тяжело и иногда не реально.
                                    0
                                    del
                                      0
                                      Doctrine в ключе DDD/CQRS
                                      Частные случаи в Repository на QB.

                                      А тут как то все сложно и не приятно читать.
                                        0

                                        Стоило бы упомянуть ещё и про спецификации. С ними репозиторий будет чист как слеза младенца

                                        0

                                        Чтобы не создавалось проблем при отладке, надо вставлять весь код одной строчкой, а потом добивать пустыми строками до прежнего количества строк.

                                          0
                                          Я конечно сварщик ненастоящий, но даже вот этот ActiveRecord, хоть мёртвый уже несколько лет как, лучше чем ваше поделие…
                                            0
                                            del
                                              –3
                                              Автора уже дважды за такие посты закидывали минусами…
                                              При этом ему по второму кругу задают одни и те же вопросы…
                                              Мне же понятно что ему просто интересно собрать свой (хоть и сомнительный) велосипед…
                                              На самом деле я тоже люблю велосипеды, но не готов их публиковать на хабре.
                                              Как пример приведу один php файл который использую для sql запросов (тут не весь функционал а лишь тот который мне нужен для определенного проекта):
                                              200 строк кода на php (не для слабонервных)
                                              <?php
                                              class SQLCONFIG {
                                              	private $config = [
                                              		["host" => "localhost", "username" => "root", "password" => "24022017", "database" => "bd1", "charset" => "utf8"],
                                              		["host" => "localhost", "username" => "root", "password" => "24022017", "database" => "bd2", "charset" => "utf8"]
                                              	];
                                              	function getConfig($def = 0){
                                              		if (isset($this->config[$def])) return (object)$this->config[$def];
                                              	}
                                              }
                                              class SQL extends SQLCONFIG{
                                              	private $columns, $rows, $table, $output, $operation, $where, $group, $order, $limit, $join = '', $query;
                                              	public $sql;
                                              
                                              	function __construct($def = 0){
                                              		$config = $this->getConfig($def);
                                              		if (empty($config)) {
                                              			$this->error("Отсутствуют настройки для подключения.");
                                              		}else{
                                              			$this->sql = mysqli_connect($config->host, $config->username, $config->password) OR $this->error();
                                              			mysqli_select_db($this->sql, $config->database) OR $this->error();
                                              			mysqli_set_charset ($this->sql, $config->charset) OR $this->error();
                                              		}
                                              	}
                                              
                                              	function __destruct(){
                                              		@mysqli_close($this->sql);
                                              	}
                                              
                                              	public function close(){
                                              		$this->clear();
                                              		mysqli_close($this->sql);
                                              		return;
                                              	}
                                              
                                              	public function select($table, $columns = "*", $output = "arr"){
                                              		$this->operation = __FUNCTION__;
                                              		$this->table = $this->columns = ""; $this->output = $output;
                                              		$this->table = "`{$table}`";
                                              		if (!is_array($columns)) $columns = explode(",", $columns);
                                              		foreach ($columns as $key => $value) $this->columns .= "{$this->table}.`".str_replace(".", "`.`", $this->check($value))."`,";
                                              		$this->columns = rtrim($this->columns, ",");
                                              		return $this;
                                              	}
                                              
                                              	public function where($where = ""){
                                              		$this->where = (!empty($where)) ? " WHERE {$where}": "";
                                              		return $this;
                                              	}
                                              
                                              	public function group($group = ""){
                                              		$this->group = "";
                                              		if (!is_array($group)) $group = explode(",", $group);
                                              		foreach ($group as $key => $value) $this->group .= "`".str_replace(".", "`.`", $this->check($value))."`,";
                                              		$this->group = rtrim($this->group, ",");
                                              		$this->group = (!empty($group)) ? " GROUP BY {$this->group}" : "";
                                              		return $this;
                                              	}
                                              
                                              	public function order($order = "", $sort = "DESC"){
                                              		$this->order = "";
                                              		$sort = strtoupper($sort);
                                              		$sort = ($sort == "DESC" || $sort == "ASC" || $sort == "RAND()") ? $sort : "";
                                              		if (!is_array($order)) $order = explode(",", $order);
                                              		foreach ($order as $key => $value) $this->order .= "`".str_replace(".", "`.`", $this->check($value))."`,";
                                              		$this->order = rtrim($this->order, ",");
                                              		$this->order = (!empty($order)) ? " ORDER BY {$this->order} {$sort}" : "";
                                              		return $this;
                                              	}
                                              
                                              	public function limit($start = 0, $count = 0){
                                              		$this->limit = ($count > 0) ? " LIMIT {$start},{$count}" : "LIMIT {$start}";
                                              		return $this;
                                              	}
                                              
                                              	public function join($table, $columns, $on, $prefix = ""){
                                              		if (!is_array($columns)) $columns = explode(",", $columns);
                                              		foreach ($columns as $key => $value){ 
                                              			$dop = (!empty($prefix)) ? " as {$prefix}_{$value}" : "";
                                              			$this->columns .= ",`".$this->check($table)."`.`".str_replace(".", "`.`", $this->check($value))."`{$dop}";
                                              		}
                                              		$this->join .= " INNER JOIN `".$this->check($table)."` ON {$on}";
                                              		return $this;
                                              	}
                                              
                                              	public function insert($table, $data = array()){
                                              		$this->operation = __FUNCTION__;
                                              		$this->columns = $this->rows = ""; $this->table = "`{$table}`";
                                              		if (isset($data[0])) {
                                              			foreach ($data[0] as $key => $value) $this->columns .= "`".$this->check($key)."`,";
                                              			foreach ($data as $key => $value) {
                                              				$this->rows .= "(";
                                              				foreach ($value as $values) $this->rows .= "'".$this->check($values)."',";
                                              				$this->rows = rtrim($this->rows, ",")."),";
                                              			}
                                              		}else{
                                              			foreach ($data as $key => $value) {
                                              				$this->columns .= "`".$this->check($key)."`,";
                                              				$this->rows .= "'".$this->check($value)."',";
                                              			}
                                              			$this->rows = "(".rtrim($this->rows, ",")."),";
                                              		}
                                              		$this->columns = rtrim($this->columns, ",");
                                              		$this->rows = rtrim($this->rows, ",");
                                              		return $this;
                                              	}
                                              
                                              	public function update($table, $data = array()){
                                              		$this->operation = __FUNCTION__;
                                              		$this->rows = ""; $this->table = "`{$table}`";
                                              		foreach ($data as $key => $value) $this->rows .= "`".$this->check($key)."` = '".$this->check($value)."',";
                                              		$this->rows = rtrim($this->rows, ",");
                                              		return $this;
                                              	}
                                              
                                              	public function delete($table){
                                              		$this->operation = __FUNCTION__;
                                              		$this->table = $this->table = "`{$table}`";
                                              		return $this;
                                              	}
                                              
                                              	public function count($table, $columns="*"){
                                              		$this->precounter($table, $columns, __FUNCTION__); return $this;
                                              	}
                                              	public function sum($table, $columns){
                                              		$this->precounter($table, $columns, __FUNCTION__); return $this;
                                              	}
                                              	public function min($table, $columns){
                                              		$this->precounter($table, $columns, __FUNCTION__); return $this;
                                              	}
                                              	public function max($table, $columns){
                                              		$this->precounter($table, $columns, __FUNCTION__); return $this;
                                              	}
                                              	public function avg($table, $columns){
                                              		$this->precounter($table, $columns, __FUNCTION__); return $this;
                                              	}
                                              	private function precounter($table, $columns, $operation = "count"){
                                              		$this->operation = $operation; $this->table = "`{$table}`";
                                              		$this->columns = ($columns != "*") ? strtoupper($operation)."(`".$this->check($columns)."`) as ".strtolower($operation) : strtoupper($operation)."(*) as ".strtolower($operation);
                                              		return;
                                              	}
                                              
                                              	public function query(){
                                              		if ($this->operation == "select") {
                                              			if ($this->output == "only") $this->limit = " LIMIT 0,1";
                                              			$query = "SELECT {$this->columns} FROM {$this->table}{$this->join}{$this->where}{$this->group}{$this->order}{$this->limit};";
                                              		}
                                              		if ($this->operation == "insert") {
                                              			$query = "INSERT INTO {$this->table} ({$this->columns}) VALUES {$this->rows};";
                                              		}
                                              		if ($this->operation == "update") {
                                              			$query = "UPDATE {$this->table} SET {$this->rows}{$this->where}{$this->group}{$this->order}{$this->limit};";
                                              		}
                                              		if ($this->operation == "delete") {
                                              			$query = "DELETE FROM {$this->table}{$this->where}{$this->group}{$this->order}{$this->limit};";
                                              		}
                                              		if ($this->operation == "count" || $this->operation == "sum" || $this->operation == "min" || $this->operation == "max" || $this->operation == "avg") {
                                              			$this->rows = (!empty($this->group)) ? ",".str_replace(" GROUP BY ", "", $this->group) : "";
                                              			$query = "SELECT {$this->columns}{$this->rows} FROM {$this->table}{$this->join}{$this->where}{$this->group}{$this->order}{$this->limit};";
                                              		}
                                              		return $query;
                                              	}
                                              
                                              	public function send($query = ""){
                                              		$this->query = (empty($query)) ? $this->query() : $query;
                                              		$res = mysqli_query($this->sql, $this->query) OR $this->error();
                                              		if ($this->operation == "select" || $this->operation == "count" || $this->operation == "sum" || $this->operation == "min" || $this->operation == "max" || $this->operation == "avg") {
                                              			$result = array();
                                              			if ($this->output == "arr") while ($row = mysqli_fetch_assoc($res)) $result[] = $row;
                                              			elseif ($this->output == "full") while ($row = mysqli_fetch_array($res)) $result[] = $row;
                                              			elseif ($this->output == "only") $result = mysqli_fetch_assoc($res);
                                              			elseif ($this->output == "object") while ($row = mysqli_fetch_object($res)) $result[] = $row;
                                              			else{
                                              				if ($res->field_count == 1 && $res->num_rows == 1)	{ 
                                              					$result = mysqli_fetch_assoc($res);
                                              					if (isset($result[$this->operation])) $result = (float)$result[$this->operation];
                                              				}
                                              			}
                                              		}else if($this->operation == "insert"){
                                              			$result = (int)mysqli_insert_id($this->sql);
                                              		}else if($this->operation == "update"){
                                              			$result = $res OR $this->error();
                                              		}else if($this->operation == "delete"){
                                              			$result = $res OR $this->error();
                                              		}else{
                                              			$result = true;
                                              		}
                                              		$this->clear();
                                              		return $result;
                                              	}
                                              
                                              	public function operation($operation){
                                              		$this->operation = $operation;
                                              		return $this;
                                              	}
                                              	public function output($output){
                                              		$this->output = $output;
                                              		return $this;
                                              	}
                                              
                                              	private function error($error = ""){
                                              		$error = (empty($error)) ? mysqli_error($this->sql) : $error;
                                              		die("<div style='background-color:#e95656;color:#fff;font-size:16px;padding:20px;margin:20px;font-family:Consolas,monaco,monospace;'>{$error}</div>");
                                              	}
                                              
                                              	private function check($value){
                                              		$value = mysqli_real_escape_string($this->sql, $value);
                                              		return $value;
                                              	}
                                              
                                              	function clear(){
                                              		$this->where = $this->group = $this->order = $this->limit = $this->join = "";
                                              		$this->columns = $this->rows = $this->table = $this->output = $this->operation = $this->query = "";
                                              	}
                                              
                                              }
                                              
                                              ?>
                                              


                                              И пару примеров:

                                              <?php
                                              
                                              include "sql.php";
                                              $sql = new SQL(0); // 0 - первый конфиг подключения.
                                              print_r($sql->select("temp")->send());
                                              // Вернет все строки из таблицы temp
                                              print_r($sql->select("temp", "id,name")->where("`id` > 0")->order("date")->limit(0,100)->send());
                                              
                                              // а так же всякие там $sql->insert("temp", ["name" => "test"])->send();
                                              // $sql->update("temp", ["name" => "test1"])->send();
                                              // ещё join можно использовать... есть count, SUM, MIN, MAX, AVG...
                                              ?>
                                              


                                                –1
                                                Что вас заставляет писать код, не соответствующий PSR-2 (и даже PSR-0, судя по всему)?

                                                Как вообще можно достичь такого состояния сознания, что вам пофигу на стандарты?

                                                А
                                                else if
                                                вообще рвотный рефлекс вызывает, если честно… Ну как же себя можно не уважать, чтобы писать так?
                                                  –1
                                                  Не всем PSR-2 по душе. Не равняйте по себе.
                                                  Я, например предпочитаю писать так:
                                                  if (1!=1) {
                                                  ...
                                                  }
                                                  else
                                                  if (1=1) {
                                                  ...
                                                  }
                                                  

                                                  Удобно бывает копипастить по строчкам, не задумываясь о съехавших блоках.
                                                    –1
                                                    Не всем PSR-2 по душе

                                                    Если вам не по душе общепринятые стандарты — что вы делаете в этой профессии?
                                                      +2
                                                      Надеюсь, вам не нужно напоминать, что PSR — это рекомендации. И появились они позже языка.
                                                      И я искренне надеюсь, что вы не из тех, кто лишь по предпочтениям в форматировании кода измеряет его качество.
                                                      Стандарты кодирования безусловно нужны, и в рабочих группах желательно их соблюдение. Но они вовсе не обязаны соответствовать документу PSR-2, если на то пошло.
                                                        0
                                                        Я из тех. Вы зря надеялись.

                                                        Несоблюдение общепринятых стандартов — это шаг к Битриксу, если позволите. Это неуважение к сообществу, которое положило многие человеко-годы на разработку этих стандартов, на то, чтобы PHP стал хотя бы немного похож на современный язык программирования, а не на набор скриптов для домашних страничек.

                                                        И для меня это, разумеется, неприемлемо.
                                                          0
                                                          Вы предлагаете переписать весь код, написанный до 2012 года (принятия PSR-2) и никому его не показывать? А если публиковать, то обязательно приводить к стандарту?
                                                          Кроме того указанное мной выше предпочтение не простая прихоть и имеет основание. И не важно, что по этому поводу пишут в стандартах. Мне так удобно, и эстетически кажется красивее (это моё субъективное мнение, которое я не навязываю). И такой код удобно поддерживать.
                                                          Даже популярные IDE имеют гибкие настройки по изменению предпочтений в форматировании кода. Казалось бы, зачем это, если придумали PSR-2? Вероятно не всем и не во всех ситуациях годятся эти рекомендации.
                                                          Некоторые до сих пор предпочитают Vim, Notepad++ или например FAR в качестве редактора кода. Что не может быть поводом для пинка из профессии.
                                                            0
                                                            А если публиковать, то обязательно приводить к стандарту?

                                                            Именно так.
                                                            Публикуете код — соблюдайте общепринятые стандарты.

                                                            это моё субъективное мнение

                                                            С чего вы взяли, что вы имеете право на субъективное мнение в данном вопросе?
                                                              –1
                                                              С чего вы взяли, что вы имеете право на субъективное мнение в данном вопросе?
                                                              Пожалуй вы правы, вы умнее и лучше меня и остальных.
                                                                –1
                                                                Я — нет, ни в коем случае. Сообщество в целом — да. В PHP-FIG состоят люди, которых я готов признать гораздо умнее и себя и вас вместе взятых.

                                                                Искренне желаю и вам и себе однажды тоже дорасти до такого же уровня.
                                                                  +2

                                                                  Да, там и правда люди, которые гораздо умнее вас.


                                                                  Например, они не стали принудительно вводить проверку соблюдения этого стайл-гайда интерпретатором, оставив возможность программисту самостоятельно принимать решение о том, будет ли он этих правил придерживаться.

                                                        0

                                                        Вы так пишите как будто в общепринятых стандартах не может быть написана фигня

                                                          0
                                                          Если считаете, что там написана фигня — вносите свои предложения, проходите экспертизу и голосование. В чём проблема-то?
                                                      0
                                                      Ну вот, судя по комментариям мне просто необходимо изредка вываливать свой «неформат» ибо про PSR-2 не слышал, пойду почитаю. А на счёт else if — так короче, строк меньше, больше строк вмещается на экран — мне удобнее читать (только мне, ибо в команде не работаю, кодом обычно не делюсь)

                                                      И не надо ругаться что не слышал про это соглашение, вроде мониторю хабр регулярно, про php читаю всё…
                                                    0
                                                    За новые идеи могу похвалить, но минусы в первую очередь за то сначала опробовать всё что есть и удостовериться что идея стоит того.

                                                    Из явных недостатков подхода отмечу то, что нельзя сделать динамически изменяемый запрос.
                                                      0
                                                      Можно. Забыл в справку добавить. Функция _if(<условие>,...,...) поддерживается в SELECT, OrderBy, GroupBy, Where. В WHERE если без ELSE, то условие заменяется на 1=1 чтобы не ломать логику AND/OR блоков.
                                                        0

                                                        Я бы сказал что проблема в другом. Конструктор запросов означает что запрос может быть составлен не линейно и части запроса применятся в отдельных условиях или даже функциях и файлах.


                                                        Простейший пример это вывод списка сущностей с погинацией. Запрос по сути один, но первый будет с SELECT COUNT(*), а второй с LIMIT и OFFSET. В вашем случае похоже нужно писать запрос дважды.


                                                        Еще рекомендую почитать про спецификации из DDD или посмотреть пример реализации для Doctrine.

                                                          –1
                                                          Такие варианты также предусмотрены. Текст запроса содержится в $__query->sql. Один из вариантов функций возврата результата будет возвращать объект $__query, с которым можно делать что угодно. Можно будет определить количество строк, потом модифицировать запрос и определить текущие строки.

                                                          p.s.А вот про функции LIMIT и OFFSET в select я забыл. Спасибо что напомнили :)
                                                            0

                                                            А модификация запроса будет выполнятся через интерфейс QueryBuilder или как строки запроса?
                                                            В первом случае теряется смысл от кешированя, во втором теряется смысл от самой либы.

                                                              –2
                                                              Скорее всего для пагинации будет сделан отдельный метод, который уже будет внутри себя делать что нужно в рамках библиотеки и отдавать требуемый результат.

                                                    Only users with full accounts can post comments. Log in, please.