MySQL без SQL в PHP

в 9:55, , рубрики: mysql, php, sql

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

Это мой первый опыт написания статьи, прошу сильно не пинать.

Хочу представить на обзор и обсуждение мною написанные функции, без которых работа с MySQL в PHP меня очень раздражала. Я, честно, искал нечто подобное в интернете, но не нашел. Если вы знаете более или менее удобный и в тоже время производительный способ того, чего я пытался добиться, напишите в комментариях пожалуйста. Если вы сможете встроить функции в класс, я не разбирался с классами в PHP, или как-то улучшить их в производительности или функционале я не против. На счет производительности ничего сказать не могу, не тестировал, но думаю для небольших проекторов не критично. Я делал это для себя и решил что возможно пригодится еще кому-нибудь.

Посмотреть или скачать _db можно по этой ссылке.

Теперь все по порядку.

refValues — это вспомогательная функция, многие возможно уже сталкивались с ней при работе с mysqli. Её я просто скопировал откуда-то. Встречается она один раз, в функции _db.

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

$h="localhost"; // Сервер MySQL
$l="deight"; // логин
$p="pas"; // пароль
$b="base"; // имя базы
$_bd->set_charset("cp1251"); // кодировка

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

_db — эта функция служит для выполнения обычного SQL запроса, на основе sqli. Принимает первым аргументом строку в SQL, где все переменные обозначены знаком вопроса, вторым строку состоящую из символов типа переменных (i:integer, s:string), третьим массив переменных и в четвертый запишется результат выполнения.

$stmt=_db("UPDATE table1 SET table1.name = ? WHERE table1.id = ?",'si',['name',1]);
$stmt->fetch();
$stmt->close();

$table1=null;
$table2=null;
$stmt=_db("SELECT table1.id, table1.name, table2.text FROM table1,table2 WHERE table1.id > ? AND table2.table1_id = table1.id LIMIT 0,1",'i',[1],[$table1,$table2]);
$stmt->fetch();
$stmt->close();
var_dump($table1);
var_dump($table2);

_db_limit

Функция возвращает строку вида 'LIMIT 0,12', второй аргумент это количество записей, а первый с какой записи начинать умноженное на количество записей (либо число, либо объект со свойством limit):

echo _db_limit(0);
//LIMIT 0, 12

$post=new stdClass;
$post->limit=0;
echo _db_limit($post,15);
//LIMIT 0,15

$post->limit=2;//Удобство в том что это номер страницы начиная с ноля
echo _db_limit($post,20);
//LIMIT 40,20

_dbq и _dbw — вспомогательные функции, которые и обеспечивают красоту последующих функций.

Все выше перечисленные функции, кроме _db_limit, я не использую в коде, они нужны только что бы обеспечить работоспособность остальных. Теперь самое интересное.

_dbs — функция для выборки данных. Первый аргумент это строка. В ней указаны таблицы, которые используются в выборке и их поля. Строка имеет вид:

$s='table1:id,name,text;table2:help;table3;table4';

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

true — если нужно выбрать одну запись, в этом случае возвращается сразу результат и выборка закрывается.
integer — если нужно выбрать определенное кол-во записей.
string — строка в которой можно указать команды LIMIT, ORDER, GROUP BY и все остальное что помещается в конец запроса SQL.
Обьект — используется если нужно помимо выборки использовать еще и sum или count.

Все последующие аргументы это массивы условий.

Массивы условий — это массивы, которые преобразуются в то, что в SQL находится после WHERE.

Первый элемент это строка с полем и условием ('id=' или 'id>' или 'id!=' или 'name LIKE' и т.д.).
Второй это значение условия, если значение равно null то условие не учитывается.
Третий это тип ('i':integer, 's':string, 'b':BINARY, 'l': оборачивает значение символами % и используется совместно с условием LIKE).
Если указать тип 'p', то значением поля будет считаться поле, указанное во втором параметре, таблицы указанной в пятом параметре.
Четвертый элемент массива это номер таблицы, указанной в первом аргументе функции в очереди слева на право начиная с ноля. Пятый то же что и четвертый, только для значения условия, т.е. второго элемента массива при указании типа 'p'.

Примеры:

// SELECT table1.id, table1.name, table1.text FROM table1 LIMIT 0,1
$stmt=_dbs('table1:id,name,text',true);
$table1=$stmt->table1; // $table1 будет объект со свойствами id,name и text

// SELECT table1.id, table1.name FROM table1 WHERE table1.id=1 LIMIT 0,10
$stmt=_dbs('table1:id,name',10,['id=',1]);
$table1=$stmt->result->table1;
while($stmt->fetch())
  echo "$table1->id. $table1->name";
$stmt->close();

// SELECT table1.id, table1.name, table2.text FROM table1,table2 WHERE table2.table1_id = table1.id LIMIT 0,12
$stmt=_dbs('table1:id,name;table2:text',_db_limit(0),
    ['table1_id=','id','p',1] // Функция берет имя таблицы для table1_id из первого аргумента _dbs слева на право начиная с ноля
);
$table1=$stmt->result->table1;
$table2=$stmt->result->table2;
while($stmt->fetch())
  echo "$table1->id. $table1->name ($table2->text)";
$stmt->close();

Я не просто так написал массивЫ, их можно указывать сколько угодно, а еще вкладывать друг в друга. Массивы условий объединяются в зависимости от уровня вложенности друг в друга. Изначально обедняются через AND. Если массив верхнего уровня будет состоять из массивов условий то они объединятся по средствам OR. Словами объяснять тяжело лучше покажу на перемер.

// SELECT table1.id, table1.name, table1.text FROM table1 WHERE table1.id>1 AND table1.id<5
$stmt=_dbs('table1:id,name,text',
    ['id>',1],
    ['id<',5]
);

// SELECT table1.id FROM table1 WHERE table1.id>1 OR table1.id<5
$stmt=_dbs('table1:id',
    [
        ['id>',1],
        ['id<',5]
    ]
);

// SELECT table1.id FROM table1 WHERE (table1.id>5 OR table1.id=1) AND (table1.id<10 OR table1.id=13)
$stmt=_dbs('table1:id',
    [
        ['id>',5],
        ['id=',1]
    ],
    [
        ['id<',10],
        ['id=',13]
    ]
);

// SELECT table1.id FROM table1 WHERE (table1.id>5 OR (table1.id<4 AND table1.id!=1)) AND (table1.id<10 OR table1.id=13)
$stmt=_dbs('table1:id',
    [
        ['id>',5],
        [
            ['id<',4],
            ['id!=',1]
        ]
    ],
    [
        ['id<',10],
        ['id=',13]
    ]
);

Теперь последнее в _dbs. Если нужно получить сумму полей и количество (возможно вы найдете еще этому применения), для этого вторым аргументом можно передать объект. Сразу пример.

$count=new stdClass;
$count->count='count(*)';
$count->summa='sum(lot.rate)';
$stmt=_dbs('lot',$count,['date>',time()],['owner!=',1]);
$stmt->fetch();
$stmt->close();
echo "Количество $count->count, сумма $count->summa";

В принципе все понятно, значение всех свойств объекта записываются через запятую после SELECT. Но тут есть исключения, это свойства limit и join. Limit просто вставляется в конец запроса, а join перед WHERE.

_dbi — вставляет запись в таблицу. Функция возвращает id записи. Аргументы во всех функциях очень похожи, здесь в первом аргументе примерно тоже что и в _dbs только первая таблица это то куда мы вставляем запись, после двоеточия перечисляем поля, которые надо заполнить. Остальные таблицы используются для выборки значения их полей.

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

i — integer используется по умолчанию
s — строка
число — номер таблицы из которой необходимо взять данные, слева на право начиная с ноля.

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

//INSERT INTO table1(name,city) VALUES ('название',1)
_dbi('table1:name.s,city',['название',1]);

//INSERT INTO table1(name,city) SELECT city.naming,city.id FROM city WHERE city.id=1
_dbi('table1:name.1,city.1;city',['naming','id'],['id=',1,'i',1]);

//INSERT INTO table1(name,city,value) SELECT city.naming,city.id,city.val+1 FROM city WHERE city.id=1
_dbi('table1:name.1,city.1,value.1;city',['naming','id','val+1'],['id=',1,'i',1]);

_dbu

Редактирует запись и возвращает кол-во затронутых записей. Все работает по той же схеме. За исключением следующих особенностей.
Нельзя редактировать брав данные из других таблиц, но можно указывать в качестве типа поля цифру 0. Можно указывать третьим параметром true, число или строку, работает как второй параметр в _dbs (нельзя писать 'LIMIT 1,2', только 'LIMIT 1').

//UPDATE table1 SET name='Имя',text='Текст',value=1,ua=table1.ua+1 WHERE table1.id=2
_dbu('table1:name.s,text.s,value,ua.0',['Имя','Текст',1,'ua+1'],['id=',2]);

Так же можно использовать массив вместо значения, что бы обрамить его текстом

//UPDATE table1 SET name='Имя',text=concat(text,'Текст'),value=1,ua=table1.ua+1 WHERE table1.id=1
_dbu('table1:name.s,text.s,value,ua.0',['Имя',['concat(text,',"Текст",')'],1,'ua+1'],['id=',1,'i',1]);

_dbp

Работает как _dbu, только если функция не изменит ни одной записи, добавит новую. Возвращает либо количество отредактированных записей, либо id добавленной (согласен нелогично).

_dba — выборка всех полей первой записи у таблицы. Для работы этой функции надо чтобы был установлен php5-mysqlnd. Сделать это можно следующей командой на сервере:

apt-get install aptitude
aptitude install php5-mysqlnd

Первый аргумент имя таблицы, остальное массивы условий.

_dbc — подсчитывает количество записей в таблице, аргументы как в _dbs, кроме второго параметра.

_dbd — удаляет запись в таблице, аргументы как в _dbs только указывать можно одну таблицу.

_dbds — удаляет выборку, аргументы как в _dbs, только первой идет таблица из которой удаляется выборка и через двоеточие уникальное поле id.

_db_fetch — возвращает всю выборку в массиве и закрывает ее. Во второй аргумент записывается кол-во выбранных записей.
Использую для того что бы не открывать нового соединения с бд.

$stmt=_dbs('table1:id,name;table2:table3_id',_db_limit(0),['id=','table1_id','p',1]);
$count=0;
foreach(_db_fetch($stmt,$count) as $v){
    list($table1,$table2)=[$v->table1,$v->table2];
    $c=_dbc('table3',['id=',$table2->table3_id]);
    echo "$table1->name всего: $c";
}
var_dump($count);

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

Автор: deight

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js