2 мая 2015 г., 10:56

xPDO: все поля присоединённых таблиц типизирует как строковые

Пример запроса:
$query = $modx->newQuery('modResource'); $query->select(array('modResource.id AS resourceId')); $query->innerJoin('modTemplateVarTemplate', 'TVValues', array('TVValues.contentid = modResource.id')); $query->where(array('TVValues.tmplvarid:=' => 8));
Получаем SQL со строкой '8', а не числом 8:
SELECT modResource.id AS resourceId FROM `modx_site_content` AS `modResource` JOIN `modx_site_tmplvar_templates` `TVValues` ON TVValues.contentid = modResource.id WHERE `TVValues`.`tmplvarid` = '8'
Проверил разные варианты. Вывод такой: xPDO не получает мета-информацию о присоединённых таблицах (хотя парсит их отлично). Поэтому, все поля этих таблиц он приводит к строковому типу.
Как заставить xPDOQuery получать информацию о типах данных присоединяемых таблиц?
Заставить — никак. Он не может знать является ли указанный алиас алиасом xPDO-объекта или просто алиас временной таблицы (на самом деле может, но, думаю, не будет этого делать, слишком много тонкостей и вариантов здесь). А вот сформировать правильный запрос можно.
$query->innerJoin('modTemplateVarTemplate', 'TVValues', "TVValues.contentid = modResource.id AND TVValues.tmplvarid = 8");
Если уж очень хочется именно в where засунуть условие, то так:
$query->where(array('TVValues.tmplvarid = 8'));
P.S. И вероятно предполагалось modTemplateVarResource, а не modTemplateVarTemplate.
P.S. И вероятно предполагалось modTemplateVarResource, а не modTemplateVarTemplate.
Да, верно.
Он не может знать является ли указанный алиас алиасом xPDO-объекта или просто алиас временной таблицы
Так ведь в методе innerJoin первым параметром передаётся имя xPDO-объекта, вторым псевдоним, третьим — условие с использованием этого псевдонима. Если название таблицы в составе условия совпадает с псевдонимом, указанным вторым параметром, то однозначно это псевдоним xPDO-объекта, указанного первым параметром. Иначе быть не может, поскольку в выборке не могут участвовать 2 таблицы с одинаковыми псевдонимами.
Т.е. алгоритм проверки должен быть таким: 1) если имя таблицы в условии совпадает с псевдонимом присоединяемой таблицы => это и есть присоединяемая таблица => типизируем как положено 2) если имя таблицы в условии не совпадает с псевдонимом присоединяемой таблицы => парсим поле как строковое (т.к. ситуация неопределённая) И xPDO этой элементарной проверки почему-то не выполняет…
А вот сформировать правильный запрос можно.
Я просто пример привёл такой со стандартной таблицей. В этом примере — да, логически правильнее внести фильтрацию по TV в ON. Но во многих других случаях (в т.ч. и с участием собственных таблиц) дополнительные условия должны содержаться в WHERE. Особенно, если запрос большой и частично он строится в одних методах, частично — в других.
В любом случае, проблема имеет место, не зависимо от того, где присутствует условие — в WHERE или в ON.
Вообще, у меня в коде много десятков подобных запросов по всему коду. Всегда полагался на типизацию xPDO. А сейчас, похоже, придётся все запросы перешерстить. Странно, что все запросы выполнялись корректно… до сегодняшнего дня…
Всё-таки отказ от автоматической xPDO-типизации в условиях — не совсем хороший вариант. Хотя бы, те же самые операторы IN. Напрашивается такое решение: обёртка для join'ов, в которых: а) вручную читаем мета-данные б) определяем тип в) корректируем тип поля в объекте xPDOQuery (query['joins'][...])
Здорово поможете, если навскидку состряпаете эту обёртку. Если нет — поковыряюсь сам…
Если уж очень хочется именно в where засунуть условие, то так: $query->where(array('TVValues.tmplvarid = 8'));
И xPDO этой элементарной проверки почему-то не выполняет…
Отправляйте свой PR в xPDO, не вопрос.
Вообще, у меня в коде много десятков подобных запросов по всему коду. Всегда полагался на типизацию xPDO. А сейчас, похоже, придётся все запросы перешерстить. Странно, что все запросы выполнялись корректно… до сегодняшнего дня…
У вас где-то запрос не так выполняется (имеется ввиду получаете не те результаты)? Мускул сам типизацию проведет, это вам не сортировка. Хотите более полный контроль над типами? — юзайте CAST, CONVERT и т.п.
Здорово поможете, если навскидку состряпаете эту обёртку. Если нет — поковыряюсь сам…
Не вижу смысла. Не помогу с этим.
У вас где-то запрос не так выполняется (имеется ввиду получаете не те результаты)? Мускул сам типизацию проведет, это вам не сортировка.
Именно. Сравнение числового поля со строковой константой всегда даёт false (в этом случае MySQL числовые значения поля приводит к строковому типу)
Тьфу, что-то не то пишу. Должно нормально работать сравнение. Проверю ещё раз…
Сравнение числового поля со строковой константой всегда даёт false (в этом случае MySQL числовые значения поля приводит к строковому типу)
Да, вы явно не то пишете. Все будет ОК работать. Это в сортировках может получиться, что '87' будет меньше чем '9', но 8 == '8'.
Это в сортировках может получиться, что '87' будет меньше чем '9', но 8 == '8'.
И в условиях сравнения тоже.
Больше/меньше — да, но равно/не равно — нет.
Всё равно некошерно. Ладно, с TV понятно — если там сравнение на неравенство используется, нужно непосредственно прописывать условие в виде SQL. Но когда то же самое приходится делать с собственными типизированными таблицами — это уже извращение.
Еще раз — шлите пуллреквесты, улучшайте работу MODX-а. Я вот шлю. На сегодня более 10% всех активных PR в MODX — мои. И вы можете приложить усилия, чтобы MODX стал лучше.
На английском не очень получается. Впрочем, писал уже: github.com/modxcms/revolution/issues/12307 ноль внимания…
Хотя в версии 2.3.3 они там логику чуть изменили (так, что в описанных в этой теме условиях ошибки больше не вылезали), но «корячность» этой логики сохранилась (а может, я её не понимаю). Так или иначе в 2.3.3 эти 2 ошибки по-прежнему иногда вылезают, но гораздо реже и при других условиях (при каких — неизвестно).
Что касается сабжа, то лучше я обёртку напишу. Так будет надёжнее.
Issue — это только пожелания, обсуждения. Отправить готовый код — более предметно.
Что касается сабжа, то лучше я обёртку напишу. Так будет надёжнее.
Нет. С разбегу не получится. Не 5 минут…
Логика должна быть такой. Перед выполнением метода prepare() запускается наша супер-функция (или метод), которая в объекте xPDOQuery делает следующее: 1) проходит по всем JOIN-таблицам и в их condition'ах корректирует ['condition']['bind']['type'], если он не соответствует модели таблицы 2) нестроковые условия по TV полям преобразовывает в их SQL-аналоги с использованием CAST'ов в зависимости от типа TV, задаваемого в modx_site_tmplvars 3) то же самое — для полей, участвующих в ORDER BY и HAVING
Тогда работа с xPDO станет сказкой… А пока не горит. Нестроковые сравнения (< > <= >=) и сортировки по TV-полям и JOIN-полям оформляем в виде SQL с CAST'ами, всё остальное — оставляем как есть.

Добавить комментарий