О проекте

GreenPig (далее GP) – это небольшая DAO библиотека для работы с базами данных Oracle или mySql.

Как и любой инструмент GP заточен под решение определенных задач. Он будет вам полезен, если вы предпочитаете писать запросы к БД на чистом sql и не используете Active record и прочие подобные технологии.

GP позволяет решать следующие задачи:

  1. Получение от БД древовидного, вложенного массива, вместо стандартного плоского. Эта вложенная структура отлично работает с пагинацией и сортировкой, за все это отвечает библиотека.
  2. бла бла ………………………………………..

Простой старт

Установка

Вы можете установить GreenPig (далее именуемый GP) двумя способами:

  1. Используя Composer:

    composer require falbin/green-pig

  2. Скачав архив на GitHub (не рекомендуется).

Управление соединениями

Сначала необходимо настроить параметры соединения с БД в «единой точке входа» приложения (в принципе можно настроить в любом месте). Настраивается с помощью функции GP::config($settings). Если у вас одно соединение с БД, то достаточно просто описать настройки подключений в массиве настроек:

                    GP::config([
                        // настройки подключения
                    ]);
                

Данный набор настроек автоматически будет назван default и получить объект для работы с GP можно так: GP::instance() (полный синтаксис: GP::instance('default')). Вся работа с БД осуществляется через объект, полученный с помощью функции GP::instance().

Можно настроить несколько подключений к разным БД. В таком случае необходимо написать ассоциативный массив настроек, где ключами будут названия соединений. В таком случае необходимо явно указать ключ default, т.к. дефолтное соединения должно быть обязательно.

                    GP::config([
                        'default' => [ /* настройки подключения */],
                        'oracle' => [ /* настройки подключения */],
                        'mysql' => [ /* настройки подключения */],
                        ........
                    ]);
                    $gp = GP::instance();
                    $gpOracle = GP::instance('oracle');
                    $gpMysql  = GP::instance('mysql');
                
Настройки Oracle
                    GP::config([
                        'RDBMS' => 'Oracle',
                        'db' => [
                            'username' => 'my_app',
                            'password' => 'secret',
                            'connection_string' => '8.8.8.8:3306/XE',
                            'character_set' => 'AL32UTF8',
                            'session_mode' => null,
                        ],
                        'date' => [
                            'php' => 'd.m.Y H:i:s',
                            'sql' => 'dd.mm.yyyy hh24:mi:ss'
                        ],
                        'debugQuery' => 100
                    ]);
                
  • RDBMS - название БД (может быть либо Oracle, либо MySQL).
  • db - настройки подключения к БД. Для подключения к БД Oracle используется oci_connect() и все значения настройки, это параметры данной функции.
    • username - Имя пользователя Oracle
    • password - Пароль username
    • connection_string - Содержит экземпляр Oracle для подключения.
    • character_set - Определяет кодировку, используемую клиентскими библиотеками Oracle.
    • session_mode - Данный параметр можно не писать, он служит для настройки привилегированного соединения
  • date - Настройка формата даты для Where части запросов. Данная настройка не обязательна, по умолчанию для Oracle значения выставляются так:
                                'date' => [
                                    'php' => 'd.m.Y H:i:s',
                                    'sql' => 'dd.mm.yyyy hh24:mi:ss'
                                ]
                            
    • php - формат даты для экземпляров рнр класса DateTime. Т.е. нам достаточно передать в where условие переменную, которая является экземпляром класса DateTime без дополнительных действий.
    • sql - формат для преобразования в дату с помощью sql функции Oracle TO_DATE().
                                        ['date', '>', new DateTime()]
                                        // Преобразует в:
                                        // sql: date > TO_DATE(:greenpig_alias_where_1, 'dd.mm.yyyy hh24:mi:ss')
                                        // bind: ['greenpig_alias_where_1' => '31.05.2021 14:35:32']
                            
    Подробнее в разделе Where.
  • debugQuery - Настройка для дебага. Советуем не указывать эту настройку, оставить значения по умолчанию.
    По умолчанию 100, это значит что будет сохраняться текст последних 100 запросов, и значений биндов к ним пока существует экземпляр класса (переменная). Подробнее в описании к функции debugInfo().
    Чтобы отключить сбор информации для отладки, необходимо выставить значение 0.
    Предупреждение! Если вы выставите значение, скажем 1000000 и будете в цикле инсертить в таблицу сотни тысяч записей, у вас в рнр произойдет переполнение памяти.
Настройки MySQL
                    GP::config([
                        'RDBMS' => 'MySql',
                        'db' => [
                            'dsn' => 'mysql:host=localhost;dbname=my_app;charset=utf8',
                            'username' => 'my_app',
                            'password' => 'secret',
                            'options' => null
                        ],
                        'date' => [
                            'php' => 'd.m.Y H:i:s',
                            'sql' => '%d.%m.%Y %H:%i:%s'
                        ],
                        'debugQuery' => 100
                    ]);
                
  • RDBMS - название БД (может быть либо Oracle, либо MySQL).
  • db - настройки подключения к БД. Для подключения к БД MySQL используется PDO::__construct() и все значения настройки, это параметры данного конструктора.
    • dsn - Имя источника данных или DSN
    • username - Имя пользователя для строки DSN
    • password - Пароль для строки DSN
    • options - Данный параметр можно не писать, он служит для специфичных настроек подключения.
  • date - Настройка формата даты для Where части запросов. Данная настройка не обязательна, по умолчанию для mySql значения выставляются так:
                                'date' => [
                                    'php' => 'd.m.Y H:i:s',
                                    'sql' => '%d.%m.%Y %H:%i:%s'
                                ]
                            
    • php - формат даты для экземпляров рнр класса DateTime. Т.е. нам достаточно передать в where условие переменную, которая является экземпляром класса DateTime без дополнительных действий.
    • sql - формат для преобразования в дату с помощью sql функции mySql STR_TO_DATE().
                                        ['date', '>', new DateTime()]
                                        // Преобразует в:
                                        // sql: date > STR_TO_DATE(:greenpig_alias_where_1, '%d.%m.%Y %H:%i:%s')
                                        // bind: ['greenpig_alias_where_1' => '31.05.2021 14:35:32']
                            
    Подробнее в разделе Where.
  • debugQuery - Настройка для дебага. Советуем не указывать эту настройку, оставить значения по умолчанию.
    По умолчанию 100, это значит что будет сохраняться текст последних 100 запросов, и значений биндов к ним пока существует экземпляр класса (переменная). Подробнее в описании к функции debugInfo().
    Чтобы отключить сбор информации для отладки, необходимо выставить значение 0.
    Предупреждение! Если вы выставите значение, скажем 1000000 и будете в цикле инсертить в таблицу сотни тысяч записей, у вас в рнр произойдет переполнение памяти.
Пример нескольких подключений
                    GP::config([
                        'default' => [
                            'RDBMS' => 'MySql',
                            'db' => [
                                'dsn' => 'mysql:host=localhost;dbname=my_app;charset=utf8',
                                'username' => 'my_app',
                                'password' => 'secret',
                                'options' => null
                            ]
                        ],
                        'oracle' => [
                            'RDBMS' => 'Oracle',
                            'db' => [
                                'username' => 'my_app',
                                'password' => 'secret',
                                'connection_string' => '8.8.8.8:3306/XE',
                                'character_set' => 'AL32UTF8',
                                'session_mode' => null,
                            ]
                        ]
                    ]);
                    $gp = GP::instance();
                    $gpOracle = GP::instance('oracle');
                
или
                    GP::config([
                        'RDBMS' => 'MySql',
                        'db' => [
                            'dsn' => 'mysql:host=localhost;dbname=my_app;charset=utf8',
                            'username' => 'my_app',
                            'password' => 'secret',
                            'options' => null
                        ]
                    ]);
                    GP::config([
                        'oracle' => [
                            'RDBMS' => 'Oracle',
                            'db' => [
                                'username' => 'my_app',
                                'password' => 'secret',
                                'connection_string' => '8.8.8.8:3306/XE',
                                'character_set' => 'AL32UTF8',
                                'session_mode' => null,
                            ]
                        ]
                    ]);
                    $gp = GP::instance();
                    $gpOracle = GP::instance('oracle');
                
Дополнительные функции для работы с соединениями:
  • GP::getNamesActiveInstances() - возвращает массив имен всех активных подключений к БД.
  • GP::disconnect($nameConnection) - закрывает $nameConnection соединение с БД. Возвращает true, если соединение с БД было открыто и оно успешно закрылось, во всех остальных случаях - false. При этом настройки данного соединения остануться и можно будет заново открыть соединение с помошью функции GP::instance($nameConnection). Под копотом используется синглтон, так что с БД всегда будет только одно соединение для конкретного $nameConnection.
  • GP::disconnectAll() - закрывает все активные соединение с БД. Возвращает количество закрытых соединений.
  • GP::clearConfig() - закрывает все активные соединение с БД. Возвращает количество закрытых соединений. При этом весь массив настроек так же очищается и теперь невозможно снова открыть соединение с БД, необходимо будет сначала передать параметры подключения с помощью функции GP::config($settings).
  • GP::configDate($formatDatePhp, $formatDateSql, $nameConnection = 'default') - меняет формат преобразования даты в конфиге для конкретного $nameConnection.
  • GP::configDebugQuery($numberQuery, $nameConnection = 'default') - указываем для скольких запросов храним отладочную информацию (sql текст, бинды и время выполнения) для конкретного $nameConnection. Чтобы отключить сбор информации для отладки, необходимо выставить значение 0.

Функции для работы с БД

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

получение данных - Функции в данной группе выполняют запрос на получение данных, подготовленный функциями зеленой группы.

результат запроса и/или его обработка - Функции в синей группе работают с данными, полученными из базы.

изменение данных - Функции из данной группы изменяют данные в базе.

Функция Описание

sql($baseSQL, $binds = null)

Задаем шаблон базового sql запроса.

                                    sql('SELECT * FROM dual')
                            

Вторым необязательным параметром является массив биндов, эквивалентно функции binds($binds).

sqlPart($alias, $sql)

Задаем часть sql запроса, которая будет вставлена в базовый запрос вместо $alias.
                                    sqlPart('/*_select_*/', 'round(dbms_random.value(:a, :b)) as val')
                                
Пример:
                                    $x = $gp->sql('SELECT /*_select_*/ FROM dual')
                                            ->sqlPart('/*_select_*/', 'round(dbms_random.value(:a, :b)) as val')
                                            ->binds(['a'=>1, 'b'=>100])
                                            ->first('val');
                                    // В результате получим такой sql:
                                    // SELECT round(dbms_random.value(:a, :b)) as val FROM dual
                                    // В переменную $x записывается число, к примеру 67
                                
ВАЖНО:
Вы должны задавать уникальные $alias. Псевдонимы должны быть заключены в /* */.

Бинды

binds($binds)

Задаем массив биндов. Причем если вы до этого устанавливали бинды с помощью функций binds, addBinds или bind, то весь массив биндов перезапишется.

Общий случай

                                    $gp->binds([
                                       'nameBind' => 123,
                                       ......
                                    ])
                            

Для Oracle

                                    $gp->binds([
                                       'nameBind1' => 123,
                                       'nameBind2 [12]' => &$str,
                                       'nameBind3 [int]' => 123,
                                       'nameBind4 [int 11]' => &$val,
                                       'nameBind5 [clob]' => &$bigText,
                                       'nameBind6 [bool]' => true,
                                       'nameBind7 [array(SQLT_INT)]' => $arr
                                    ]);
                                
  • nameBind - обычное описание бинда, по умолчанию подразумевается, что тип данных символьный (SQLT_CHR) и в 99% случаев этого достаточно.
  • nameBind [12] - такая запись означает, что тип данных - символьный, а в квадратных скобках указываем размер данных. В случае с GreenPig, указывать размер данных имеет смысл только для inout параметров, это значит что в эту переменную будут записаны данные из базы. Поэтому переменная должна передаваться по ссылке.
  • nameBind [int] - указывает на числовой тип данных (OCI_B_INT).
  • nameBind [int 11] - inout параметр числового типа. Переменная должна передаваться по ссылке.
  • nameBind [clob] - для записи в колонку с типом clob (OCI_B_CLOB). Для clob передавать размер данных не нужно, поэтому этот параметр автоматически становится inout. Если необходимо получить данные из БД, то переменная должна передаваться по ссылке.
  • nameBind [bool] - указывает на логический тип данных (OCI_B_BOL). Будьте внимательны, с этим типом много проблем.
  • nameBind [array(SQLT_INT)] - данная настройка позволяет биндить коллекции в Оракл, причем для этого под капотом используется специальная функция oci_bind_array_by_name(). В круглых скобках необходимо указать тип данных коллекции, вот доступные варианты: SQLT_NUM, SQLT_INT, SQLT_FLT, SQLT_AFC, SQLT_CHR, SQLT_VCS, SQLT_AVC, SQLT_STR, SQLT_LVC, SQLT_ODT.
GreenPig для работы с Oracle использует библиотеку OCI8. Для связывания биндов с sql используется функция (документация):
oci_bind_by_name ( resource $statement , string $bv_name , mixed &$variable , int $maxlength = -1 , int $type = SQLT_CHR )
Поэтому ['nameBind [int 11]' => &$val] будет представлена как oci_bind_by_name ($stmt , 'nameBind', $val , 11, OCI_B_INT)
Пример:
                                                    // У нас есть переменная $bigTxt и нам надо записать ее в колонку типа  clob, что-то с текстом
                                                    // сделать и записать данные обратно в переменную (GreenPig позволяет сделать это очень просто).
                                                    $bigTxt = 'bla bla bla';
                                                    print_r($bigTxt); // bla bla bla
                                                    $gp = GP::instance();
                                                    $gp->sql("INSERT INTO TEST (big_text) VALUES (:big_txt) returning TO_CLOB('guf guf guf') into :big_txt")
                                                       ->binds(['big_txt [clob]' => &$bigTxt])
                                                       ->execute();
                                                    print_r($bigTxt); // guf guf guf
                                                

Для MySQL

                                    binds([
                                       'nameBind1' => $val,
                                       'nameBind2 [int]' => 123,
                                       'nameBind3 [bool]' => $val,
                                       'nameBind4 [null]' => $val
                                    ])
                                
  • nameBind - обычное описание бинда, по умолчанию подразумевается, что тип данных символьный (PDO::PARAM_STR) и в 99% случаев этого достаточно.
  • nameBind [int] - указывает на числовой тип данных (PDO::PARAM_INT). К примеру необходимо указывать для LIMIT.
                                                $dbMySQL->sql("select * from test limit :limit")
                                                        ->binds(['limit [int]' => 10])
                                                        ->all();
                                            
  • nameBind [bool] - указывает на логический тип данных (PDO::PARAM_BOOL)
  • nameBind [null] - указывает на тип данных SQL NULL (PDO::PARAM_NULL)
GreenPig для работы с MySQL использует PDO. Для связывания биндов с sql используется функция ( документация):
bindParam(string|int $param, mixed &$var, int $type = PDO::PARAM_STR, int $maxLength = 0, mixed $driverOptions = null)
Поэтому ['nameBind [int]' => $val] будет представлена как $sth->bindParam(":nameBind", $val, PDO::PARAM_INT)

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

Нашел разные примеры для других БД, например для Db2, а для MySQL на разных форумах пишут, что этот функционал не работает. Также в официальной документации библиотеки MySQLi написано, что inout параметров нет. Возможно PDO и MySQLi используют один и тот же драйвер (не удалось найти информацию). Как бы там ни было inout параметры использовать не получится. Можно применять решение, которое предлагают на форумах для PDO (и в официальной документации по MySQLi).

                                                    $dbMySQL = GP::instance();
                                                    $dbMySQL->sql("set @number = 5")->execute();
                                                    // test_fun_inout(@var) - sql процедура, получает число @var прибовляет
                                                    // к нему 10 и записывает результат в эту же переменную
                                                    $dbMySQL->sql("call test_fun_inout(@number)")->execute();
                                                    $number = $dbMySQL->sql("select @number num")->first('num');
                                                    var_dump($number); // 15
                                                

addBinds($binds)

К массиву биндов добавляем новый массив биндов.
                                    addBinds([
                                       'name' => 'Вася',
                                       'old' => $old,
                                       .....
                                    ])
                            

Причем можно добавлять и по ссылке (для inout параметров у Oracle):

                                    addBinds(['nameBind' => &$val])
                            

bind($alias, $value)

Добавляем одно значение в массив биндов, причем, если псевдоним не уникальный, то произойдет перезапись значения.
                                    bind('number', 123);
                                    bind('text_article', $txt);
                            

Использовать переменную по ссылке тут нельзя, но можно получить измененное значение с помощью функции getBind():.

                                $qaz = -1;
                                $gp->binds(['id' => 21, 'rating' => 20])
                                   ->bind('qaz [13]', $qaz)
                                   ->sql("insert into table_name (id, rating) values (:id, :rating) returning 'xxx' into :qaz")
                                   ->execute();

                                print_r($qaz);                  // -1
                                print_r( $gp->getBind('qaz') ); // xxx
                            

linkBind($alias, &$value)

Добавляем одно значение в массив биндов по ссылке (причем, если псевдоним не уникальный, то произойдет перезапись значения).
                                $qaz = -1;
                                $gp->binds(['id' => 21, 'rating' => 20])
                                   ->linkBind('qaz [13]', $qaz)
                                   ->sql("insert into table_name (id, rating) values (:id, :rating) returning 'xxx' into :qaz")
                                   ->execute();

                                print_r($qaz); // xxx
                            

where($alias, $where, $beginKeyword = ‘where’)

В базовом шаблоне вместо $alias подставляется sql, который генерируется из массива $where (подробнее в разделе Where).
                                    where('/*where*/', ['id', '=', 344])
                                
$beginKeyword - ключевое слово, которое будет вставлено перед сгенерированным sql. Может принимать значения: where, and, or или пустоту.
                                $gp->sql("select * from product /*_whrere_*/")
                                   ->where('/*_whrere_*/', ['id', '=', 2])
                                   ->all();
                                // Сгенерируется sql:  select * from product where id = :greenpig_alias_where_1

                                $gp->sql("select * from product where id = 2 /*_whrere_*/")
                                   ->where('/*_whrere_*/', ['status', '=', 'delete'], 'and')
                                   ->all();
                                // Сгенерируется sql:  select * from product where id = 2 and status = :greenpig_alias_where_1
                            
УПРОЩЕННАЯ ЗАПИСЬ

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

                                where('/*wh*/', 2)
                                // или
                                where('/*wh*/', ['id', '=', 2])
                                // sql: where id = 2
                            

Также можно передать простой массив (с числовыми индексами), значения которого обязательно должны быть целыми числами. В этом случае данный массив будет интерпретирован как набор id:

                                where('/*wh*/', [4,8,16,32])
                                // или
                                where('/*wh*/', ['id', 'in', [4,8,16,32]])
                                // sql: where id in (4,8,16,32)
                            

Упрощенная запись также поддерживается для where части функций update() и delete().

whereJoin($aliasJoin, $nameTable, $aliasTable, $conditionJoin, $aliasWhere, $where, $beginKeywordWhere = 'where')

При запросе на выборку функция генерирует sql как для where части, так и для join части. Подробно принцип работы функции описан в пункте whereJoin().
(подробнее на хабр)

sort($nameColumn)

$nameColumn может быть строкой, и тогда сортируем только по одному указанному столбцу.
                                    $gp->sql("select * from product")->sort('name')->all();
                                    $gp->sql("select * from product")->sort('name desc')->all();
                                
$nameColumn может быть массивом, и тогда сортируем по нескольким столбцам.
                                    $gp->sql("select * from product")->sort(['name desc', 'date'])->all();
                                    // Формируется sql: select * from (select * from product) order by name desc, date
                                
ВАЖНО: указываем только название столбцов, без указания таблицы.
ВАЖНО: в случае агрегированного запроса сортировка возможна только по верхнему уровню.

pagination($currentPage, $numberRowOnPage)

При выборке из БД получаем $numberRowOnPage записей, находящихся на $currentPage странице.
                                    $gp->sql("select * from product")->pagination(1, 10);
                                
ВАЖНО: пагинация с агрегированным запросом считает только верхние, самые основные данные.

all($options = null, $isKeyPK = false)

Возвращает все строки запроса.
                                    // Возвращает все записи
                                    $gp->sql("select * from product")->all();
                                    // Возвращает первые 10 записей
                                    $gp->sql("select * from product")->pagination(1, 10)->all();
                                
Если нужна агрегация, то $options должен быть массивом правил, которые описывают как именно должна происходить группировка. подробнее описано ниже, в пункте Получение древовидных данных. Если необходимо чтобы ключами массивов были значения первичных ключей из БД, то параметру $isKeyPK необходимо присвоить true. Массив $option строится по следующим правилам:
  • Элементы массива - названия колонок.
  • На каждом уровне обязательно должен быть элемент массива с ключом pk. Значение данного элемента - название столбца по которому будут сгруппированы данные.
  • Если необходимо часть колонок поместить на уровень ниже, то в качестве ключа массива используется новое, выдуманное название, а в качестве значения будет массив, строящийся по правилам описанным выше.
                                    [
                                        'pk' => 'name_column_1',
                                        'name_column_2',
                                        ......
                                        'courseworks' => [
                                            'pk' => 'name_column_n',
                                            'name_column_n1'
                                        ]
                                    ]
                                

Если необходимо чтобы ключами массивов были значения первичных ключей из БД, то параметру $isKeyPK необходимо присвоить true.

Примеры:

student (s)
id name
3 Паша
coursework (c)
id student_id title status
6 3 «Азы» программирования и обучающие программы work
7 3 История возникновения Интернета work
                                    $gp = GP::instance();
                                    $gp->sql("SELECT s.id, s.name, c.id title_id, c.title FROM student s
                                              INNER JOIN coursework c ON c.student_id = s.id");

                                    $studentsSimple = $gp->all();
                                    // В обычном случае получим плоский результат:
                                    // [
                                    //     0 => [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'title_id' => 6,
                                    //         'title' => '«Азы» программирования и обучающие программы',
                                    //     ],
                                    //     1=> [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'title_id' => 7,
                                    //         'title' => 'История возникновения Интернет'
                                    //     ]
                                    // ]

                                    $studentsTreeData = $gp->all(['pk' => 'id', 'name',
                                                                  'courseworks' => ['pk' => 'title_id', 'title']]);
                                    // Получаем ответ с вложенными значениями:
                                    // [
                                    //     0 => [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'courseworks' => [
                                    //             0 => ['title_id' => 6, 'title' => '«Азы» программирования и обучающие программы'],
                                    //             1 => ['title_id' => 7, 'title' => 'История возникновения Интернет']
                                    //         ]
                                    //     ]
                                    // ]

                                    $studentsTreeDataPK = $gp->all(['pk' => 'id', 'name',
                                                                  'courseworks' => ['pk' => 'title_id', 'title']], true);
                                    // Получаем ответ с вложенными значениями (ключи массива - значения первичного ключа):
                                    // [
                                    //     3 => [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'courseworks' => [
                                    //             6 => ['title_id' => 6, 'title' => '«Азы» программирования и обучающие программы'],
                                    //             7 => ['title_id' => 7, 'title' => 'История возникновения Интернет']
                                    //         ]
                                    //     ]
                                    // ]
                                

Если мы хоти получить вложенный массив, где на верхнем уровне будут студенты, на втором уровне будут статусы курсовых работ для этих студентов, а на третьем уровне список названий курсовых, относящихся к данному статусу курсовых работ, то можно написать так:

                                    $gp = GP::instance();
                                    $gp->sql("SELECT s.id, s.name, c.id title_id, c.title, c.status FROM student s
                                              INNER JOIN coursework c ON c.student_id = s.id)
                                       ->all([
                                                'pk' => 'id', 'name',
                                                'courseworks' => [
                                                    'pk' => 'status',
                                                    'titles' => [
                                                        'pk' => 'title_id', 'title'
                                                    ]
                                                ]
                                             ], true);
                                    // [
                                    //     3 => [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'courseworks' => [
                                    //            'work' => [
                                    //                'status' => 'work',
                                    //                'titles' => [
                                    //                    6 => ['title_id' => 6, 'title' => '«Азы» программирования и обучающие программы'],
                                    //                    7 => ['title_id' => 7, 'title' => 'История возникновения Интернет']
                                    //                ]
                                    //            ]
                                    //         ]
                                    //     ]
                                    // ]
                                

Данный сформированный вложенный массив можно без дополнительной оброботки отправлять на фронтенд и там уже удобно с ним работать.

oneOrError($nameColumn = null)

Возвращает одну строку запроса. В случае если в результате запроса получаем больше одной строки - вызывается ошибка. Данную функцию необходимо использовать, когда критически важно, чтобы из БД была получена только одна строка. Если указать название колонки – $nameColumn, то получим содержимое конкретной ячейки.
                                    $name = $gp->sql("select * from product /*where*/")
                                               ->where('/*where*/', ['id', '=', 777])
                                               ->oneOrError('name');
                                

first($nameColumn = null)

Возвращает первую строку запроса. Если указать название колонки – nameColumn, то получим содержимое конкретной ячейки.
                                    $name = $gp->sql("select * from product")
                                               ->first('name');
                                

count($nameColumn = '*')

Возвращает количество записей (строк) запроса.
                                    // $countProduct - количество записей (строк) запроса

                                    // select count(*) from (select * from product)
                                    $countProduct = $gp->sql("select * from product")->count();
                                    // select count(name) from (select * from product)
                                    $countProduct = $gp->sql("select id, name from product")->count('name');
                                    // select count(distinct name) from (select * from product)
                                    $countProduct = $gp->sql("select id, name from product")->count('distinct name');
                                
 

execute()

Особая функция, которая может выполнить любой sql запрос и возвращает результат выполнения.

Если запрос - это выборка из БД, то execute() может работать со всеми функциями из зеленой группы.

Если запрос на изменения данных в БД (или в случае с Oracle - PL/SQL конструкция), то функция execute() может работать со всеми функциями из зеленой группы кроме: whereJoin(), sort(), pagination()

                                    $gp->sql("INSERT INTO product(id, basket_id)
                                              VALUES(nameSequence.NEXTVAL, :basket_id)
                                              RETURNING id INTO :id_val")
                                       ->binds(['basket_id' => $basketId, 'id_val[11]' => &$this->id])
                                       ->execute();

                                    $data = $gp->sql("select id, basket_id, name from product")
                                               ->sort('name')
                                               ->pagination(1, 10)
                                               ->execute();


                                    $gp->sql("BEGIN drovosek.update_basket(p_basket_id => :basket_id, p_mode => :mode); END;")
                                       ->binds(['basket_id' => 234, 'mode' => 'normal'])
                                       ->execute();
                                

insert($table, $parameters)

Вставляем в таблицу $table данные $parameters. Структура массива $parameters:
                                    [
                                        'название_колонки' => 'значение',
                                        'название_колонки' => ["LOWER('AAAAA')"],
                                        ......
                                    ]
                                
Пример:
                                    // sql: INSERT INTO table_name (column_name) VALUES (:greenpig_alias_insert_1)
                                    insert('table_name', ['column_name' => $val])
                                
Значение автоматически оборачивается псевдонимом и добавляется в массив биндов. Если вам необходимо добавить sql, то значение обрамляется квадратными скобками (объявляем массив). В таком случае данные вставляются в sql запрос напрямую и вам нужно будет значение добавить через функцию binds().
                                    // sql: INSERT INTO product (title, status_product) VALUES (:greenpig_alias_insert_1, LOWER(:status))
                                    $idProduct = $gp->binds(['status' => $status])
                                                    ->insert('product', [
                                                        'title' => $title,
                                                        'status_product' => ['LOWER(:status)']
                                                    ]);
                                

Для MySQL

Функция возвращает значение первичного ключа с автоинкрементом для новой вставленной записи. Грубо говоря возвращает "id" новой строки (под капотом просто вызывается функция PDO::lastInsertId()).

                                        $idProduct = $gp->insert('product', ['title' => $title]);
                                    

Для Oracle

Функция возвращает полностью всю новую вставленную строку. Если третьем параметром указать название колонки, то получим значение конкретной ячейки.

                                        $newRow = $gp->insert('product', ['title' => $title]);
                                        $idProduct = $gp->insert('product', ['title' => $title], 'id');
                                    

inserts($table, $parameters)

Вставляем в таблицу $table несколько строк.
                                    $gp->inserts('product', [['title' => $title1, 'age' => $val1],
                                                             ['title' => $title2, 'age' => $val2],
                                                             ['title' => $title3, 'age' => $val3]]);
                                
Функция возвращает количество вставленых записей.

update($table, $parameters, $where)

Обновление данных в таблице $table. Структура массива $parameters идентична той, что описана для функции insert().

$where - массив описывающий where часть запроса (подробнее в разделе WHERE).

                                    // sql: UPDATE table_name SET column_name = :greenpig_alias_update_1 WHERE id = :greenpig_alias_update_2
                                    update('table_name', ['column_name' => $val], ['id', '=', 1])
                                

                                    // sql: UPDATE product SET title = :greenpig_alias_update_1, status_product = LOWER(:status)
                                    //      WHERE id = :greenpig_alias_update_2
                                    $gp->bind('status', $status)
                                       ->update('product',
                                                ['title' => $title, 'status_product' => ['LOWER(:status)']],
                                                ['id', '=', $id]);
                                
Функция возвращает количество изменных строк.

insertUpdate($table, $paramInsert, $paramUpdate)

Пытаемся вставить новую запись из $paramInsert в таблицу $table. Если же вставить новую запись нельзя из-за нарушения уникальности, то обновляем конфликтующую запись из $paramUpdate. Функция возвращает ‘insert’ или ‘update’ в зависимости от выполненного действия.

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

Под капотом для mySql реализована такая конструкция:

                                                INSERT INTO name_table (a, b, c) VALUES (1, 2, 3)
                                                ON DUPLICATE KEY
                                                UPDATE b = 4
                                            

Для Oracle реализован аналог.

ПРИМЕР:

Допустим у нас есть пустая таблица student с двумя колонками email и name, причем значения в колонке email должны быть уникальными.

                                $gp->insertUpdate('student',
                                                  ['email' => 't@test.ru', 'name' => 'Паша'],
                                                  ['name' => 'Маша']);
                            

После выполнения функции в таблице будет так:

email name
t@test.ru Паша

Попробуем еще раз выполнить тот же код:

                                $gp->insertUpdate('student',
                                                  ['email' => 't@test.ru', 'name' => 'Паша'],
                                                  ['name' => 'Маша']);
                            

Теперь таблица выглядит следующим образом:

email name
t@test.ru Маша

delete($table, $where)

Удаляет данные в таблице $table.

$where - массив описывающий where часть запроса (подробнее в разделе WHERE).

                                // sql: DELETE FROM product WHERE id in (1, 2, 56, 777, 19000)
                                $countDelRow = $gp->delete('product', ['id', 'in', [1, 2, 56, 777, 19000]]);
                                // или можно записать по упрощенному варианту
                                $countDelRow = $gp->delete('product', [1, 2, 56, 777, 19000]);
                            

Функция возвращает количество удаленных строк.

Если функции передать вторым параметром именно true (===), то удаляются все записи из таблицы. $gp->delete('product', true). Будьте внимательны!

beginTransaction()

Начало транзакции, отключение автоматической фиксации.
                                    try
                                    {
                                        $gp->beginTransaction(); // начало транзакции
                                        .....
                                        $gp->update('product', ['status' => 'delete'], ['title', 'like' => 'iPhone*']);
                                        .....
                                        $gp->commit(); // записываем изменения
                                    } catch ( Exception $e ) {
                                        $gp->rollBack(); // откатываем транзакцию
                                    }
                                

commit()

Фиксирует транзакцию.
                                    try
                                    {
                                        $gp->beginTransaction(); // начало транзакции
                                        .....
                                        $gp->update('product', ['status' => 'delete'], ['title', 'like' => 'iPhone*']);
                                        .....
                                        $gp->commit(); // записываем изменения
                                    } catch ( Exception $e ) {
                                        $gp->rollBack(); // откатываем транзакцию
                                    }
                                

rollBack()

Откат транзакции.
                                    try
                                    {
                                        $gp->beginTransaction(); // начало транзакции
                                        .....
                                        $gp->update('product', ['status' => 'delete'], ['title', 'like' => 'iPhone*']);
                                        .....
                                        $gp->commit(); // записываем изменения
                                    } catch ( Exception $e ) {
                                        $gp->rollBack(); // откатываем транзакцию
                                    }
                                

getData($formatting = false)

Функции из голубой и красной группы получают данные из БД. Функция getData() возвращает данные, полученные одной из этих функций. Хранит информацию только последнего запроса. Если последняя выборка была с получением вложенных значений all($options), то функция getData() так же вернет древовидную структуру.

$keyFormatting - отвечает за форматирование ключей массива. Может принимать значения:

  • false - ключи массива по умолчанию.
  • upper - приводит все ключи массива к верхнему регистру.
  • lower - приводит все ключи массива к нижнему регистру.

getPagination()

Если была применина пагинация при выборке данных, то информацию о пагинации можно получить с помошью этой функции.
                                    $gp->getPagination();

                                    // Пример результата:
                                    [
                                        'currentPage' => 1,      // текущая страница
                                        'numberRowOnPage' => 10, // количество строк запроса на странице
                                        'numberAllRow' => 100,   // сколько всего строк запроса
                                        'numberAllPage' => 10    // сколько всего страниц
                                    ]
                            

getBind($alias = false)

Функция возвращает бинды, используемые в последнем выполненном запросе. Если передать $alias бинда, то вернет конкретный бинд, если ничего не передавать, то вернет массив всех биндов.

Может быть полезен для получения inout биндов. Или для получения технических биндов, добавленных библиотекой.

                                $gp->binds(['id' => 21, 'rating' => 20])
                                   ->bind('qaz [13]', -1)
                                   ->sql("insert into table_name (id, rating) values (:id, :rating) returning 'xxx' into :qaz")
                                   ->execute();

                                print_r( $gp->getBind('qaz') ); // xxx
                            

column($nameColumn)

Если в исходных данных (смотри функцию getData()) было множество строк, то данная функция формирует и возвращает обычный одномерный массив. Элементами сгенерированного массива являются данные из колонки $nameColumn.
                                    // Результат запроса:
                                    // [
                                    //     0 => [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'title_id' => 6,
                                    //         'title' => '«Азы» программирования и обучающие программы',
                                    //     ],
                                    //     1=> [
                                    //         'id' => 3,
                                    //         'name' => 'Паша',
                                    //         'title_id' => 7,
                                    //         'title' => 'История возникновения Интернет'
                                    //     ]
                                    // ]

                                    $titles = $gp->column('title');
                                    // $titles содержит массив:
                                    // ['«Азы» программирования и обучающие программы', 'История возникновения Интернет']
                                

map($pk, $columns = [])

Функция из исходного массива выборки формирует и возвращает ассоциативный массив, ключами которого будут данные из колонки $pk. Если параметр $columns не указывать, то данные из всех колонок сохраняться, но можно задать массив из названий колонок и тогда, в результате, будут данные только из перечисленных колонок. Пример:
                                    // исходный массив выборки
                                    // [
                                    //   0 => [ 'inn' => 111111222222,
                                    //          'name_org' => 'Красный буржуй',
                                    //          'address' => 'ул.Ленина д.3' ],
                                    //   1 => [ 'inn' => 333333444444,
                                    //          'name_org' => 'MakeGreatAgain',
                                    //          'address' => 'ул.Трампа д.666' ],
                                    //   2 => [ 'inn' => 555555666666,
                                    //          'name_org' => 'Собчак',
                                    //          'address' => 'ул.Лошадиная д.40' ]
                                    // ]

                                    map('inn');
                                    // результат
                                    // [
                                    //   111111222222 => [ 'inn' => 111111222222,
                                    //                     'name_org' => 'Красный буржуй',
                                    //                     'address' => 'ул.Ленина д.3' ],
                                    //   333333444444 => [ 'inn' => 333333444444,
                                    //                     'name_org' => 'MakeGreatAgain',
                                    //                     'address' => 'ул.Трампа д.666' ],
                                    //   555555666666 => [ 'inn' => 555555666666,
                                    //                     'name_org' => 'Собчак',
                                    //                     'address' => 'ул.Лошадиная д.40' ]
                                    // ]

                                    map('inn', ['name_org']);
                                    // результат
                                    // [
                                    //   111111222222 => ['name_org' => 'Красный буржуй'],
                                    //   333333444444 => ['name_org' => 'MakeGreatAgain'],
                                    //   555555666666 => ['name_org' => 'Собчак']
                                    // ]
                                

debugInfo()

Пока существует экземпляр класса (переменная), все запросы к БД логируются и данная функция возвращает массив информации для отладки, пример:
                                    $gp->debugInfo();

                                    // Пример результата:
                                    [
                                        // Массив запросов, сделанных пользователем, пока существует экземпляр класса.
                                        0 => [
                                            'numberQuery' => 999 // порядковый номер запроса
                                            // В рамках одного запроса под копотом может быть выполненно несколько технических
                                            // запросов, поэтому они группируются в массив.
                                            'querys' => [
                                                0 => [
                                                    'sql' => 'SELECT * FROM table WHERE id = :id', // выполняемый sql
                                                    'sqlWithBinds' => 'SELECT * FROM table WHERE id = 1', // выполняемый sql с подставленными биндами (для отладки)
                                                    'binds' => ['id' => 1], // массив биндов
                                                    'timeQuery' => 0.012    // время выполнения запроса
                                                ],
                                                .........
                                            ]
                                            'allTime' => 0.356 // общее время
                                        ],
                                        ........
                                    ]
                                

Получение древовидных данных

Where

                    ['nameColumn', 'like' => '*tEsT']
                    // sql:   nameColumn like :greenpig_alias_where_1

                    ['nameColumn', 'notLike' => '*tEsT']
                    // sql:   nameColumn not like :greenpig_alias_where_2

                    ['nameColumn', 'flex' => 'tEsT*']
                    // sql:   LOWER(nameColumn) like LOWER(:greenpig_alias_where_3)

                    ['nameColumn', 'notFlex' => '*tEsT']
                    // sql:   LOWER(nameColumn) not like LOWER(:greenpig_alias_where_4)

                    ['nameColumn', 'fullFlex' => 'tEsT']
                    // sql:   LOWER(nameColumn) like LOWER(:greenpig_alias_where_5)  значение 'tEsT' => '*tEsT*'

                    ['nameColumn', '=', 2]
                    // sql:   nameColumn = :greenpig_alias_where_6

                    ['nameColumn', '>', new \DateTime()]
                    // sql:   nameColumn > TO_DATE(:greenpig_alias_where_7, 'dd.mm.yyyy hh24:mi:ss')

                    ['nameColumn', 'in', [1,2, 'ggg']]
                    // sql:   nameColumn in (:greenpig_alias_where_8, :greenpig_alias_where_9, :greenpig_alias_where_10)

                    ['nameColumn', '=', 'sql'=>'7']
                    // sql:   nameColumn = 7

                    ['nameColumn', '=', 'date' => '12.12.2020']
                    // sql:   nameColumn = TO_DATE(:greenpig_alias_where_11, 'dd.mm.yyyy hh24:mi:ss')
                    ['nameColumn', '=', 'date' => ['2020.12.12', 'yyyy.mm.dd']]

                    ['nameColumn', 'between', 2, 4]
                    // sql:   nameColumn between :greenpig_alias_where_12 and :greenpig_alias_where_13

                    ['nameColumn', 'between', new \DateTime(), new \DateTime()]
                    // sql:   nameColumn between TO_DATE(:greenpig_alias_where_14, 'dd.mm.yyyy hh24:mi:ss') and TO_DATE(:greenpig_alias_where_15, 'dd.mm.yyyy hh24:mi:ss')

                    ['nameColumn', 'between', 'date1'=>'12:12:12', 'date2'=>'12:12:12']
                    // sql:   nameColumn between TO_DATE(:greenpig_alias_where_16, 'dd.mm.yyyy hh24:mi:ss') and TO_DATE(:greenpig_alias_where_17, 'dd.mm.yyyy hh24:mi:ss')
                    ['nameColumn', 'between', 'date1'=>['2020.12.12', 'yyyy.mm.dd'], 'date2'=>'12:12:12']
                    ['nameColumn', 'between', 'date1'=>'12:12:12', 'date2'=>['2020.12.12', 'yyyy.mm.dd']]
                    ['nameColumn', 'between', 'date1'=>['2020.12.12', 'yyyy.mm.dd'], 'date2'=>['2020.12.12', 'yyyy.mm.dd']]
                

whereJoin()

Exception GreenPig

Классы ошибок GreenPig:
  • GreenPigException - общая ошибка GP, от этого класса наследуются все остальные
  • GreenPigDatabaseException - ошибка связанная с БД
  • GreenPigWhereException - ошибка связанная с генерацией Where части sql запроса
  • GreenPigQueryException - ошибка связанная с основным классом Query

Функции GreenPigException

Функция Описание
Т.к. все исключения наследуются от стандартного класса Exception, то доступны все базовые функции:
  • getMessage() - возвращает сообщение об ошибке.
  • getCode() - возвращает код ошибки.
  • getFile() - возвращает имя файла в котором было сгенерировано исключение.
  • getLine() - возвращает номер строки в которой было сгенерировано исключение.
  • getTrace() - возвращает многомерный массив, содержащий последовательность методов, вызов которых привёл к генерированию исключения.
getNameException() Получаем “заголовок” ошибки, который строится так: название_класса_ошибки (путь_к_файлу_где_была_ошибка : номер_строки)
Пример: Исключение GreenPig\Exception\GreenPigWhereException (\pr-test\GP2\Where.php:125)
getErrorObject() При возникновении ошибки, как правило в Exception передается объект в котором возникла ошибка. Данная функция позволяет получить этот объект.
getErrorData($nameParameter = false) Если в у функции не указывать параметр, то получим массив, содержащий всю информацию связанную с ошибкой:
  • nameException - то же, что возвращает функция getNameException()
  • messageException - сообщение об ошибке, то же что возвращает функция getMessage()
  • errorObject - то же, что возвращает функция getErrorObject()
  • trace - то же, что возвращает функция getTrace()
  • moreParamForDebug - Для получения большей информации об ошибках иногда в Exception добавляется дополнительная информация, она хранится в массиве, который имеет следующую структуру:
                                            [
                                                ['описание', объект],
                                                .....
                                            ]
                                        
Пример:
                                Array
                                (
                                    [nameException] => Исключение GreenPig\Exception\GreenPigWhereException (\pr-test\GP2\Where.php:125)
                                    [messageException] => Если логическая операция состоит из 2 элементов, то второй элемент всегда имеет строковый или числовой тип и у него ключ должен быть следующим: like, notLike, flex, notFlex или fullFlex.
                                    [moreParamForDebug] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [0] => Логическое выражение where:
                                                    [1] => Array
                                                        (
                                                            [0] => dat
                                                            [1] => 31.05.2021
                                                        )

                                                )

                                        )

                                    [errorObject] => Array
                                        (
                                            [0] => dat
                                            [1] => 31.05.2021
                                        )

                                    [trace] =>  #0 /var/www/html/pr-test/GP2/Where.php(107): GreenPig\Where->identifyType()
                                                #1 /var/www/html/pr-test/GP2/Where.php(60): GreenPig\Where->genSql()
                                                #2 /var/www/html/pr-test/GP2/Query.php(171): GreenPig\Where->where()
                                                #3 /var/www/html/pr-test/GP2/t.php(98): GreenPig\Query->where()
                                                #4 {main}
                                )
                            
В качестве параметра функция может принимать слудующие значения: nameException, messageException, errorObject, trace, moreParamForDebug. Передав значение, мы получим не весь массив, а только интерисующую нас часть.
drawHtmlDebug() Для отладки можно использовать данную функцию, которая отрисовывает на экране в удобном виде содержимое функции getErrorData()
Пример:
Исключение GreenPig\Exception\GreenPigWhereException (\pr-test\GP2\Where.php:125)
Если логическая операция состоит из 2 элементов, то второй элемент всегда имеет строковый или числовой тип и у него ключ должен быть следующим: like, notLike, flex, notFlex или fullFlex.

Логическое выражение where:
Array
(
    [0] => dat
    [1] => 31.05.2021
)
                            

Значение объекта ошибки:
Array
(
    [0] => dat
    [1] => 31.05.2021
)
                            

Трассировка ошибок:
#0 /var/www/html/pr-test/GP2/Where.php(107): GreenPig\Where->identifyType()
#1 /var/www/html/pr-test/GP2/Where.php(60): GreenPig\Where->genSql()
#2 /var/www/html/pr-test/GP2/Query.php(171): GreenPig\Where->where()
#3 /var/www/html/pr-test/GP2/t.php(98): GreenPig\Query->where()
#4 {main}

Для того чтобы поймать исключения от GreenPig, достаточно ловить только исключения GreenPigException.

Пример:
                    try {

                        ............

                    } catch (\GreenPig\Exception\GreenPigException $gpE) {
                        $gpE->drawHtmlDebug();
                    }
                    catch (\Exception $e) {
                        GP::varDump($e, 'Exception');
                    }
                

Примеры