• Главная
  • Контакты
Подписаться:
  • Twitter
  • Facebook
  • RSS
  • VK
  • PushAll

logo

  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • За год
    • Положительные
    • Отрицательные
  • Сортировка
    • По дате (возр)
    • По дате (убыв)
    • По рейтингу (возр)
    • По рейтингу (убыв)
    • По комментам (возр)
    • По комментам (убыв)
    • По просмотрам (возр)
    • По просмотрам (убыв)
Главная
  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • Главная
  • SQL-запрос на PHP (Версия 0.2)

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

24.04.2017 05:39
shasoft 58 3300 Источник
Разработка веб-сайтов*, SQL*, PHP*
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. Сам запрос тоже выполнится. Это для пагинаторов сделано.
Поделиться с друзьями
-->

Комментарии (58)


  1. FessAectan
    24.04.2017 09:17
    #10188032
    +4

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

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

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


    1. Zhuravljov
      24.04.2017 11:52
      #10188294

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


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


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


      1. SamDark
        25.04.2017 15:35
        #10190472
        +1

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


  1. Akuma
    24.04.2017 09:29
    #10188062
    +4

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

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

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


    1. shasoft
      24.04.2017 10:08
      #10188112
      -3

      Самый первый вариант таким и был. В этом случае гораздо сложнее парсинг выражения. И каждый JOIN усложняет этот парсинг на порядок.


      1. keksmen
        24.04.2017 10:32
        #10188144
        +1

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


        1. shasoft
          24.04.2017 21:22
          #10189246

          К примеру такой простой запрос:

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


          1. YemSalat
            25.04.2017 11:48
            #10189980
            -1

            каким образом понять где у нас имя поля, а где имя значения?

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


      1. Akuma
        24.04.2017 11:04
        #10188210
        +1

        Ну а как вы хотели?

        Сейчас вы пошли легким для себя, но бессмысленным для окружающих путем. Для «домашнего» использования это может и прокатит, а вот для массового — увы, нет.


  1. AterCattus
    24.04.2017 10:36
    #10188150
    +6

    Оба поста выглядят как
    «Я вот придумал штуку, которую уже много раз делали, но я вам свой вариант все-равно не покажу.»


  1. basili4
    24.04.2017 10:46
    #10188166
    -1

    Мой же код предварительно нужно компилировать и на выполнение запускать уже откомпилированную версию

    Я вот чего не пойму как будет предварительно компилироваться запрос select * from pages p where p.page_id=(номер страницы переданный в запросе к странице);?


    И какой смысл в предворительной компиляции запроса в строку, если сам запрос и его план можно кешировать на стороне сервера?


  1. ablai
    24.04.2017 11:04
    #10188208
    +2

    Yii2 ActiveQuery — Лучше инструмента для этих целей не видел


  1. QuickJoey
    24.04.2017 11:08
    #10188212

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


    1. michael_vostrikov
      24.04.2017 12:16
      #10188320

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


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

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


      1. QuickJoey
        24.04.2017 12:31
        #10188348

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


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

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


        1. michael_vostrikov
          24.04.2017 12:48
          #10188380
          +2

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


          1. ghost404
            29.04.2017 00:26
            #10197000

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


            1. VolCh
              03.05.2017 15:26
              #10201626

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


    1. Zhuravljov
      24.04.2017 12:28
      #10188344
      +2

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


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


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


  1. AlexLeonov
    24.04.2017 11:09
    #10188218
    -1

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

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


    1. Zhuravljov
      24.04.2017 11:23
      #10188238
      +2

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


      1. AlexLeonov
        24.04.2017 12:27
        #10188340

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

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

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

        P.S. Ваш пример идеально ложится сюда же.


        1. akubintsev
          24.04.2017 12:50
          #10188388

          Кстати, на тему абстрагирования от конкретной БД.
          Теоретически конечно прикольно, но нужно ли всегда на практике?
          Одно дело пилим продукт с возможностью инсталлирования третьими лицами, другое дело — свой сервис. А часто ли приходится менять в проекте БД?
          По-моему в большинстве случаев использование этого слоя абстракции не оправдано.


          1. wispoz
            24.04.2017 13:23
            #10188466

            Это имеет место если: Вы делаете сервис для себя например на MSSQL, и потом бац решили перейти на MySQL или Postgresql, да бывает такое, вот тут как раз универсальность и спасет (видел один такой проект).


          1. AlexLeonov
            24.04.2017 19:12
            #10189074

            Нужно.
            Пример: система миграций в фреймворке. Она должна быть абстрагирована от БД.
            Или метод findById() в какой-нибудь ORM библиотеке. Его нельзя не сделать абстрагированным.

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


          1. VolCh
            25.04.2017 10:21
            #10189768
            +1

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


        1. michael_vostrikov
          24.04.2017 13:05
          #10188424

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


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

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


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

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


        1. VolCh
          25.04.2017 10:18
          #10189750

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


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


      1. smple
        24.04.2017 17:11
        #10188926

        а чем лучше будет с 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 ?


        1. AlexLeonov
          24.04.2017 19:14
          #10189076

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

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

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


    1. mayorovp
      24.04.2017 12:58
      #10188412

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


    1. sayber
      24.04.2017 13:10
      #10188434

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

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

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


  1. akubintsev
    24.04.2017 12:45
    #10188368

    del


  1. sayber
    24.04.2017 13:04
    #10188420

    Doctrine в ключе DDD/CQRS
    Частные случаи в Repository на QB.

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


    1. ghost404
      29.04.2017 00:44
      #10197032

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


  1. mayorovp
    24.04.2017 13:07
    #10188432

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


  1. zzzmmtt
    24.04.2017 16:48
    #10188876

    Я конечно сварщик ненастоящий, но даже вот этот ActiveRecord, хоть мёртвый уже несколько лет как, лучше чем ваше поделие…


  1. shasoft
    24.04.2017 18:45
    #10189040

    del


  1. funnybanana
    24.04.2017 22:43
    #10189372
    -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. AlexLeonov
      24.04.2017 23:10
      #10189396
      -1

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

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

      А

      else if
      вообще рвотный рефлекс вызывает, если честно… Ну как же себя можно не уважать, чтобы писать так?


      1. pewpew
        25.04.2017 00:17
        #10189460
        -1

        Не всем PSR-2 по душе. Не равняйте по себе.
        Я, например предпочитаю писать так:

        if (1!=1) {
        ...
        }
        else
        if (1=1) {
        ...
        }
        

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


        1. AlexLeonov
          25.04.2017 11:06
          #10189876
          -1

          Не всем PSR-2 по душе

          Если вам не по душе общепринятые стандарты — что вы делаете в этой профессии?


          1. pewpew
            25.04.2017 13:38
            #10190200
            +2

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


            1. AlexLeonov
              25.04.2017 13:43
              #10190214

              Я из тех. Вы зря надеялись.

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

              И для меня это, разумеется, неприемлемо.


              1. pewpew
                25.04.2017 14:00
                #10190244

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


                1. AlexLeonov
                  25.04.2017 14:21
                  #10190296

                  А если публиковать, то обязательно приводить к стандарту?

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

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

                  С чего вы взяли, что вы имеете право на субъективное мнение в данном вопросе?


                  1. pewpew
                    25.04.2017 14:28
                    #10190314
                    -1

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


                    1. AlexLeonov
                      25.04.2017 14:32
                      #10190330
                      -1

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

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


                      1. mayorovp
                        25.04.2017 14:41
                        #10190354
                        +2

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


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


          1. mayorovp
            25.04.2017 14:33
            #10190336

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


            1. AlexLeonov
              25.04.2017 14:34
              #10190338

              Если считаете, что там написана фигня — вносите свои предложения, проходите экспертизу и голосование. В чём проблема-то?


      1. funnybanana
        26.04.2017 02:48
        #10191226

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

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


  1. cvlab
    24.04.2017 23:33
    #10189412

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

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


    1. shasoft
      25.04.2017 09:09
      #10189670

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


      1. ghost404
        29.04.2017 01:01
        #10197044

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


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


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


        1. shasoft
          29.04.2017 13:01
          #10197326
          -1

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

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


          1. ghost404
            30.04.2017 20:54
            #10198410

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


            1. shasoft
              30.04.2017 22:06
              #10198448
              -2

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

МЕТКИ

  • Хабы
  • Теги

Разработка веб-сайтов

SQL

PHP

php

SQL

web

СЕРВИСЫ
  • logo

    CloudLogs.ru - Облачное логирование

    • Храните логи вашего сервиса или приложения в облаке. Удобно просматривайте и анализируйте их.
Все публикации автора
  • SQL-запрос на PHP (Версия 0.2) -7

    • 24.04.2017 05:39

    SQL-запрос на PHP -7

    • 30.01.2017 06:39

    «Дьявольский» ACL — мой вариант проверки прав +2

    • 10.02.2016 06:52

    Класс для работы с MySQL, компилирующий запрос для выполнения в чистый PHP код -30

    • 06.08.2015 14:52

Подписка


ЛУЧШЕЕ

  • Сегодня
  • Вчера
  • Позавчера
08:01

Трамплин в интернет: как мы ускорили запуск Яндекс Браузера +23

05:16

Дело о Транзитроне — или Ламповый тьюториал для любопытных +21

03:22

Как за один вечер создать репутацию вашего стартапа в поисковой выдаче: 20 бесплатных площадок для быстрого буста +20

10:09

Как настраивать сети: готовые решения Selectel для максимальной отказоустойчивости +19

10:09

Последний звонок: прощаемся со Skype +17

08:57

Разбор полётов: что умеет отечественная СХД Аэродиск AQ440 +17

07:00

DevOps без боли: 8 инструментов для мониторинга, автоматизации и стабильной работы команд +17

09:01

Почему из технологий делают культы +15

06:53

Важное обновление BatteryTest 2 +15

04:53

Баффет наконец накопил достаточно для выхода на пенсию, а в OpenAI выкатили новый хитрый план +14

11:34

Как избавиться от зацикливания обработчиков событий в Битриксе +13

07:43

Больше нет входа в IT. Только выход +13

08:59

Автоматизированное E2E-тестирование App.Farm: от хаоса к системе +11

09:31

Как ESLint помогает управлять архитектурой проекта +9

07:59

Стелс-изобретения, возможно, сделают невидимыми людей, самолёты и даже города +9

08:35

Распределённый инференс и шардирование LLM. Часть 1: настройка GPU, проброс в Proxmox и настройка Kubernetes +7

07:05

Делать фичи или техдолг: как мы решали проблему разработки с помощью мыслительных инструментов теории ограничений +7

06:16

Отправка label в систему логирования и мониторинга из метаданных GitLab Runner (job_id, pipeline_id) +7

04:03

Должен ли быть бизнес справедливым? Часть 2 +7

12:00

Как довести фичу до продакшена без боли: пошаговый гайд от команды RuStore. Часть 2 +5

19:36

ООП не мертво. Вы просто пользуетесь им как молотком по клавиатуре +113

08:00

Электробус из 1907 года от мошенников-стартаперов. И да, он возил пассажиров +43

13:01

Как serverless-архитектура влияет на модернизацию инфраструктуры +41

09:01

Google Maps не знают, как работает адресация улиц +34

06:30

Дискретные тригонометрические функции, машинный эпсилон и автоматическое дифференцирование +31

10:05

Разговоры с мамой, остросюжетный роман и дофаминовые ловушки. Что и зачем читать продакту в 2025 году +29

09:15

Wizordum — пример правильной ностальгии +29

16:08

Как отключить слежку на умном ТВ +25

10:00

Multi-GPU Rendering для игр жив? +25

15:16

Заговор разработчиков против корпораций: работа с командой +19

07:00

От песка в глазах к панораме: как 49-дюймовый монитор заменил два 27-дюймовых и сделал жизнь и работу комфортнее +18

05:47

Убить героя: почему героизм — это выбор легкого пути, который вредит не только вам, но и окружающим +18

12:28

Как устроен arXiv — самая преобразующая платформа во всей науке +16

17:21

CTO: рынок, стратегия и инженерная культура +14

17:01

PTTJS — формат текстового хранения комплексных таблиц +14

14:46

Что открыть в 2025, если ты не Илон Маск и не хочешь продавать курсы? +14

16:24

Корутины в C++20: архитектура и практическое применение +12

14:00

Корпоративное ЕГЭ или как обучение сотрудников НЕ влияет на рост и эффективность +12

18:20

Подмена E-EDID на Windows +10

18:33

Пишем (и используем) ИИ-агент на Gradle и Ollama +8

19:18

Конституция против цензуры: история одного иска в суд +152

14:05

Пишем один «exe», который работает на 3-х разных ОС без перекомпиляции +136

09:01

Спидометр для электромопеда на микроконтроллере PIC16F628A +61

08:00

Архитекторы чипов: как Китай строит инфраструктуру по производству современных процессоров +60

14:12

Оцифровка показаний стрелочного манометра в Home Assistant +53

11:02

Низкоуровневое программирование под 8086 для любопытных, часть 2 +52

13:01

Про человека и свободу — реально главный вопрос жизни, вселенной и всего такого +41

16:46

Творческая утилизация клавиатур +37

09:49

Не смотрите наверх +28

13:01

Промпт-инжиниринг на основе здравого смысла: как понимать LLM и получать от них предсказуемый результат +24

06:47

Размышления об интервью +24

11:52

Python, Java, C++ и Go — как появились популярные языки программирования +23

08:02

Задачи на собеседованиях. Денежные переводы в SQL. Обновление счетов и уровни изоляций +21

19:18

Проводим слепой тест переводов прямо на Хабре +20

09:00

Фронтенд — новый легаси: Как мы проспали event-driven революцию +20

17:24

Инженерия — не наука +18

08:24

Huawei и HarmonyOS PC: китайский десктоп с собственной ОС появится уже в этом месяце. Что это будет? +18

15:14

Почему нужно знать историю фронтенда, даже если просто пишешь на React +14

13:28

Немое кино: как инженеры превратили тени в магию экрана: «бегущие» картинки, фоторужья и 24 кадра в секунду +10

14:22

Есть ли смысл применять SOLID в React? +9

ОБСУЖДАЕМОЕ

  • Конституция против цензуры: история одного иска в суд +152

    • 258   11000

    От песка в глазах к панораме: как 49-дюймовый монитор заменил два 27-дюймовых и сделал жизнь и работу комфортнее +18

    • 256   39000

    ООП не мертво. Вы просто пользуетесь им как молотком по клавиатуре +114

    • 175   25000

    Больше нет входа в IT. Только выход +13

    • 111   19000

    Задачи на собеседованиях. Денежные переводы в SQL. Обновление счетов и уровни изоляций +21

    • 98   17000

    Аркадий Стругацкий против Deepseek и ChatGPT: как ИИ повлияет на художественный перевод +9

    • 88   6900

    Проводим слепой тест переводов прямо на Хабре +20

    • 83   2800

    «Накопитель риска» в команде: как одиночные эксперты тормозят развитие +3

    • 64   6100

    Инженерия — не наука +18

    • 62   3300

    Фронтенд — новый легаси: Как мы проспали event-driven революцию +20

    • 62   26000

    «640 кбайт хватит для всего» +7

    • 59   17000

    Google Maps не знают, как работает адресация улиц +34

    • 53   7000

    Не смотрите наверх +28

    • 53   14000

    Пишем один «exe», который работает на 3-х разных ОС без перекомпиляции +136

    • 41   12000

    Оцифровка показаний стрелочного манометра в Home Assistant +53

    • 37   7500
  • Главная
  • Контакты
© 2025. Все публикации принадлежат авторам.