Парсинг плейсхолдеров strikes back

Фанат

oncle terrible
Команда форума
PHP:
$sql ="SELECT * FROM table WHERE foo=:foo AND bar=:baz";
$data = DB::getAll($sql, compact('foo','baz'));
Отлично. Вместо четырех раз мы каждое имя поля пишем всего три :)
В то время как для позиционных хватит одного
PHP:
$sql ="SELECT * FROM table WHERE foo=? AND bar=?";
$data = DB::getAll($sql, func_get_args());
Но в общем да,это бессмысленный холиварный топик. Если всем нравится писать каждое поле по 4 раза, а из выражения foo=? понять имя переменной ну никак никак не получается, то наверное я чего-то не понимаю. В любом случае, обсуждать его бессмысленно - поддержка позиционных слишком традиционная штука, чтобы ее выкидывать. Не стоит путать возможность (не использовать) с запретом (на использование). А вкусы давайте здесь обсуждать не будем.
 
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Фанат, я бы не ориентировался на мнение зала. Лучше предложить решение, которое нравится, получить поток чего-нибудь, и если в нем будет что-то логичное - на
это и ориентироваться, а на абстрактных конях далеко не уехать :) Одни привыкли к yii и db::рarse()->query() выглядит как родное, а другим только DI, только $Container->get('QueryParser');
 

riff

Новичок
К слову об этом.
Не знаю как Фанат относится к той "мысли вслух", а я у себя не стал навешивать этот функционал на парсер.
Когда столкнулся с вилкой field IS NULL / field = value написал так:
WHERE IF(?i:my_var = NULL, field IS NULL, field = ?i:my_var).
про то, чтобы my_var мог быть значением или массивом (в данном примере), пока не думал.
 

Фанат

oncle terrible
Команда форума
К слову об этом.
Не знаю как Фанат относится к той "мысли вслух", а я у себя не стал навешивать этот функционал на парсер.
Ну, лазить в запрос я боюсь.
Благо ВНЕЗАПНО для меня в мускуле оказался оператор <=>
Код:
WHERE field field <=> ?i:my_var
 

ksnk

прохожий
Вероятно про "массивный" плейсхолдер Фанат вспомнил с моей подачи :) Простые квадратные скобки, конечно, проще моего `?[`, хотя возможно не все так просто. В моей версии можно указывать порядковый номер параметра.
На мой взгляд не нужен спец синтаксис для массивов.
Да ну. Вот пример нескольких запросов с "моим" языком. Натырено из живого проекта.
$data - ассоциативный массив с нефиксированным, вообще говоря, числом параметров.
Код:
$db()->insert("INSERT INTO ?_user SET ?[?k=?]",$data);
$id_brand - массив с индексами
Код:
$db()->select("select NAME,ID from ?_element where ID in(?[?2])",$id_brand)
$rec- массив полей для поиска на совпадение.
Код:
$db()->delete('delete from ?_items  where ?[?k=?| and ]', $rec);
ну и вставка значения $data - ассоциативны массив
Код:
$db('debug once')->query(
                'insert into ?_video (`LASTUPDATE`,?[?k]) ' .
                    'values (NOW(),?1[?2]) on duplicate key update  ' .
                    '`LASTUPDATE`=NOW(),?1[?1k=VALUES(?1k)];',
                $data
            );
Для каждого случая плейсхолдер не смастеришь. Хотя вариантов, конечно, конечное значение.
 

riff

Новичок
Да ну. Вот пример нескольких запросов с "моим" языком.
Код:
$db()->insert("INSERT INTO ?_user SET ?[?k=?]",$data);
Код:
INSERT INTO ?t SET ?set
Код:
$db()->select("select NAME,ID from ?_element where ID in(?[?2])",$id_brand)
Код:
SELECT name,id FROM ?t WHERE id IN (?i)
Код:
$db()->delete('delete from ?_items  where ?[?k=?| and ]', $rec);
Возьму на себя смелость сказать, что Фанат на это не пойдет.

'`LASTUPDATE`=NOW(),?1[?1k=VALUES(?1k)];
Извини, но это ужас, я не понимаю что здесь написано.

Итог: Всё же не нужны.
 
Последнее редактирование:

ksnk

прохожий
Ежели использовать и типы и имена, то невозможно смешивать позиционные и именованные параметры. Хотя языку это совсем не мешает.
  • ?_ - префикс таблицы
Если связываться с "массивными" шаблонами как у меня, то необходимо реализовать еще и позицию параметра, иначе не задать перебор только индексов или нужные комбинации индекс-значение.
Позиционные:
  • ?Nx, ?x, ? - параметр с номером N или следующий по порядку. С типом x. Если не указан - строка.
  • ?N[ template | comma ], ?N[...], ?[] - массив - параметр с номером N. Внутренность квадратных скобок является отдельным шаблоном, на вход которого передаются пары ключ-значение. Все обработанные пары склеиваются с помощью `comma` или `,`, если не указано.
Вероятно, можно "упростить" `?[` и `?N[` до `[` и `N[`

Именованные: Imho, удобнее задавать тип префиксом. Так получится более читабельный запрос, чем в суффиксной
  • x:name - плейсхолдер с типом x и именем name.
  • [ template | comma ]:name - параметр-массив с именем name.
 

ksnk

прохожий
А формат sprintf'а простой? или регулярки понятны сразу сходу? Мне например непонятен твой set, я обязательно полезу в документацию и буду выяснять, как он работает с параметрои и какие буковки генерит.
Извини, но это ужас, я не понимаю что здесь написано.
?1[?1k=VALUES(?1k)] - берем параметр, который передан нам в первой позиции. Для каждой пары ключ-значение разрешаем шаблон. Нам нужен только первый элемент из пары. получится что то вроде
Код:
`name`=VALUES(`name`),`email`=VALUES(`email`), ...
 

riff

Новичок
Это сложно, очень сложно. Никто не будет это читать, не говоря уже про понять и, как следствие, использовать. (это было к предыдущему сообщению)
 
Последнее редактирование:
  • Like
Реакции: Gas

Фанат

oncle terrible
Команда форума
я согласен с тем что это может укоротить запись, но для меня это все-таки выглядит ужасом.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
задача с плейсхолдерами и массивами мне сильно напоминает ... глифы :)
http://modis.ispras.ru/Lizorkin/private/patterns.pdf стр. 51 - да-да, та самая скучная писанина, которую все пропускают, открывая список паттернов как справочник
 

ksnk

прохожий
grigori, речь, насколько мне представляется, о форме записи sql-шаблонов. А глифы, они уже про реализацию. Не?

Про ужас-ужас. А вот так?
PHP:
$db()->insert("INSERT INTO ?_user SET ?[k:key=:value]",$data);
PHP:
$db('debug once')->query(
     'insert into ?_video (`LASTUPDATE`,[k:key]:data) values (NOW(),[:value]:data)
      on duplicate key update  `LASTUPDATE`=NOW(),[k:key=VALUES(k:key)]:data;',
     array('data'=>$data)
);
а так будет менее ужасно? Придется ввести предопределенные имена key и value для массивных шаблончиков
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
У меня такое ощущение, что от замены placeholder'ов вы плавно перешли не то к шаблонному движку для SQL'я, не то к написанию query builder'а. В первом варианте не проще ли взять готовый шаблонизатор типа Twig'а и обмазать его helper'ами для SQL? Во втором --- не проще ли писать уже полноценный билдер с похапэ-синтаксисом, который по крайней мере будет читаемым и подсвечивающимся во всяких IDE? Потому что увиденное в #27 и в #32 меня с децел пугает.
 
  • Like
Реакции: AmdY

Фанат

oncle terrible
Команда форума
У меня такое ощущение, что от замены placeholder'ов вы плавно перешли.
Не-не-не, Девид Блейн!
Это только ksnk продвигает такую идею.

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

По факту, у нас есть три условно "скалярных" типа, которые имеет смысл размножать через запятую: это строка, число и идентификатор. То есть
Код:
SELECT (?n) FROM t; # SELECT (`a`,`b`,`c`) FROM t;
SELECT id FROM t WHERE cat IN (?i); #  SELECT id FROM t WHERE cat IN (1,2,3)
INSERT INTO (?n) VALUES (?s) # INSERT INTO (`a`,`b`,`c`) VALUES ('a','b','c')
От идеи автоматом определять массивы я отказался - Григорий вправил мне мозги на тему, что такой автомат легко сделает бажный запрос типа
Код:
SELECT * FROM t WHERE id = 1,2,3
Поэтому нужен модификатор.
Я думаю над вариантами
Код:
SELECT id FROM t WHERE cat IN (?ai);
и
SELECT id FROM t WHERE cat IN ([?i]);
Вот Фикс говорит, правда, что квадратные скобки заняты МС-ом. но с другой стороны, когда еще до того МС-а руки дойдутю С третьей - для тупо ?ai не нужно усложнять парсер
 

riff

Новичок
можно не "?n", а "?c" и/или "?col" ? (от слова "column")
+ для "from t" добавить "?t"
Код:
query('
    SELECT * FROM ?t:products products
    INNER JOIN ?t:catalogs catalogs ON products.catalog_id = catalogs.id
', [
    'products' => 'my_shop_products', // предположим у таблицы в базе "сложносоставное" название
    'catalogs' => 'my_shop_catalogs', // а в запросе, в качестве alias, будем использовать простенькое
]);
тоже, но не именов.
Код:
query('
    SELECT * FROM ?t products
    INNER JOIN ?t catalogs ON products.catalog_id = catalogs.id
', [
    'my_shop_products',
    'my_shop_catalogs',
]);
это строка, число и идентификатор
в предыдущей теме говорили, напомню : "?i" число(int), не забудь про "?f" (float)

Я думаю над вариантами
я бы предпочёл 1-й вариант, мне, лично, проще догадаться о содержимом по названию "?ai", (хотя я бы предпочёл "?list"/"?list_i", но если делается версия в которой можно добавлять свои имена, то это не принципиально).
 
Последнее редактирование:

флоппик

promotor fidei
Команда форума
Партнер клуба
Поэтому нужен модификатор.
Я думаю над вариантами
Код:
SELECT id FROM t WHERE cat IN (?ai);
и
SELECT id FROM t WHERE cat IN ([?i]);
Вот Фикс говорит, правда, что квадратные скобки заняты МС-ом. но с другой стороны, когда еще до того МС-а руки дойдутю С третьей - для тупо ?ai не нужно усложнять парсер
На правах брейншторма
?i[] , ?i+ , ?ii
третий вариант еще можно добить до ?iiiiii :)
 

флоппик

promotor fidei
Команда форума
Партнер клуба
я бы выбрал первый, очевидно почему - по закону наименьшего удивления он лучше всего подходит
 

riff

Новичок
флоппик, мне вариант с ?ii понравился (?ss, ?cc, ...) просто и однозначно - это список.

----------3 мин. спустя -------
мдя, чуть подумав, он не подходит:
если Фанат будет разрешать осмысленные наименования плейсхолдеров (?int), то увы.

UPD: Хотя чего это я. Ничем они друг другу не мешают.
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
Да, я очень хочу наделать синонимов.
В этом смысле снимается даже мой вопрос про "зачем ?c и ?t вместо ?n"?
То есть, я реально не понимаю, зачем два раздельных плейсхолдера для идентификаторов. А для имени БД не нужен, кстати?
Но если делать алиасы, то ради бога - пусть расцветают и ?c, и ?t, и ?n
 
Сверху