Сталкивался с проблемами в pdoFetch

Это копия github.com/sergant210/pdoTools/issues/311. На гитхаб не читабельно из-за того, что апострофы на sql на markdown выделяются как код.

1) В этой строке в $fields приходил почему-то массив github.com/sergant210/pdoTools/blob/daf2e1c2062c2a3080bde221672a43c7dab2e11c/core/components/pdotools/model/pdotools/pdofetch.class.php#L368
Сейчас такое воспроизвести не удалось. Где-то видно сам косячил.

2)

'select'=>[
        'modResource'=>'DISTINCT pagetitle',
        ],
Выдает

0.0001791: SQL prepared "SELECT `` FROM `modx_site_content`
в отличии от

'select'=>[
        'modResource'=>'DISTINCT modResource.pagetitle',
        ],
Вообщем-то не страшно.

3) Не работают вложенные функции SQL
$pdo = $modx->getService("pdoFetch");
$default = [
    'select'=>[
        'modResource'=>'modResource.id,IF(SUM(modResource.published)/COUNT(*) = 1,1,0) as all_published',
        ],
    'groupby'=>'modResource.parent',
    'setTotal'=>0,
    'limit'=>10,
    'return'=>'data',
];
$pdo->setConfig($default);
$pdo->run();
echo "<pre>".$pdo->getTime()."</pre>";
Выдает

0.0001471: SQL prepared "SELECT modResource.id, IF(SUM(modResource.published)/COUNT(*) = 1, `1`, 0) as all_published FROM `modx_site_content`
ставит кавычку на `1`
Решение(может костыль) в строке github.com/sergant210/pdoTools/blob/daf2e1c2062c2a3080bde221672a43c7dab2e11c/core/components/pdotools/model/pdotools/pdofetch.class.php#L373
Добавить в регулярку AS, так как на функции стопроцентно алиас ставиться.
$fields = preg_replace_callback('/\(.*?\bAS\b/i', function($matches) {
                        return str_replace(",", "|", $matches[0]);
                    }, $fields);
4)
$miniShop2 = $modx->getService('miniShop2');
$pdo = $modx->getService("pdoFetch");
$default = [
    'includeTVs'=>'in_stock',
    'leftJoin'=>[
        'Data'=>[
            'class'=>'msProductData',
            'on'=>'Data.id = modResource.id'
            ]
        ],
    'select'=>[
        'modResource'=>'modResource.id',
        ],
    'sortby'=>'(IF(`Data`.`price` > 0, 1, 0)) DESC, `in_stock` desc',
    'setTotal'=>0,
    'limit'=>10,
    'return'=>'data',
];
$pdo->setConfig($default);
$pdo->run();
echo "<pre>".$pdo->getTime()."</pre>";
выдает двойные апострофы в сорт

0.0002031: Sorted by (IF(`Data`.`price` > 0, 1, 0)) DESC, ``TVin_stock`.`value`` desc,


Преодолел костылем
$sortby = str_replace("``","`",$sortby);
после строки
github.com/sergant210/pdoTools/blob/daf2e1c2062c2a3080bde221672a43c7dab2e11c/core/components/pdotools/model/pdotools/pdofetch.class.php#L516
Александр Туниеков
20 августа 2021, 17:41
modx.pro
1
626
+1
Поблагодарить автора Отправить деньги

Комментарии: 2

Сергей Шлоков
21 августа 2021, 08:17
0
1. Видимо нужна foolproof защита.
2. Или используем твой вариант с таблицей в виде префикса или такой
'select'=>[
    'modResource'=>'DISTINCT pagetitle as pagetitle',
],
Напомню, что можно работать напрямую с запросами через базовый API.
$query = $modx->newQuery('modResource');
$query->query['distinct'] = 'DISTINCT';
...
3. Это чудит xPDO. Он формирует запрос, а не pdoFetch. А вообще очень странная конструкция. Я бы так даже не додумался написать. Для сравнения на SQL сервере я бы написал так
'select'=>[
    'parent', 
    'SUM(published) = COUNT(*) as all_published'
],
Для сравнения на клиенте так
'select'=>[
     'parent', 
     'SUM(published) as all_published',
     'COUNT(*) as count'
],
А потом в цикле проверил бы на равенство два последних поля.
Но так как запрос простой и без логики, я бы не стал вообще использовать xPDO, а сделал бы маленький и лёгкий запрос (без кучи объектов xPDO)
$table = $modx->getTableName('modResource');
$sql = "SELECT parent, SUM(published) = COUNT(*) as all_published FROM $table GROUP BY parent";
$statement = $modx->prepare($sql);
if ($statement->execute()) {
    $result = $statement->fetchAll(PDO::FETCH_ASSOC);
}
4. Скорее всего тоже xPDO шалит. Проверю.
    Александр Туниеков
    21 августа 2021, 10:30
    0
    2. А я добавил исключение
    preg_match('/DISTINCT/i',
                            $fields)
    В блоке
    if ($fields == 'all' || $fields == '*' || empty($fields)) {
                            $fields = $this->modx->getSelectColumns($class, $alias);
                        } elseif(preg_match('/DISTINCT/i',
                            $fields)){
                        }else {
                            $fields = $this->modx->getSelectColumns($class, $alias, '',
                                array_map('trim', explode(',', $fields)));
                        }
    3.
    Это чудит xPDO.
    В курсе. Если select строка, xPDO разбивает select по запятым и затем решает, что 1 это колонка и ставит апострофы.
    Блок
    if (is_string($fields) && strpos($fields, '(') !== false) {
                        // Commas in functions
                        $fields = preg_replace_callback('/\(.*?\)/', function($matches) {
                            return str_replace(",", "|", $matches[0]);
                        }, $fields);
                        $fields = explode(',', $fields);
                        foreach ($fields as &$field) {
                            $field = str_replace('|', ',', $field);
                        }
                        $this->query->select($fields);
                        $this->addTime('Added selection of <b>' . $class . '</b>: <small>' . str_replace('`' . $alias . '`.',
                                '', implode(',', $fields)) . '</small>', microtime(true) - $time);
                    }
    вообще не был бы нужен, если бы XPDO не чудила.

    Но так как запрос простой и без логики, я бы не стал вообще использовать xPDO, а сделал бы маленький и лёгкий запрос (без кучи объектов xPDO)
    У меня pdoTools в компонентах используется, чтобы изменить запрос не трогая сам компонент. Например из excelconvertrule
    {
        "class": "MaterialZakupList",
        "leftJoin": {
            "MaterialZakup": {
                "class": "MaterialZakup",
                "on": "MaterialZakup.id = MaterialZakupList.order_id"
            },
            "gtsBAccount": {
                "class": "gtsBAccount",
                "on": "gtsBAccount.id = MaterialZakup.account_id"
            },
            "raschetsMaterial": {
                "class": "raschetsMaterial",
                "on": "raschetsMaterial.id = MaterialZakupList.mat_id"
            },
            "Orgs": {
                "class": "Orgs",
                "on": "Orgs.id = MaterialZakup.org_id"
            }
        },
        "select": {
            "MaterialZakupList": "*",
            "Orgs": "Orgs.shortname",
            "gtsBAccount": "gtsBAccount.label as account",
            "raschetsMaterial": "IF(MaterialZakupList.prihod_date IS NULL,'в дороге','получено') as status1,IF(MaterialZakupList.type_id = 3,raschetsMaterial.name,MaterialZakupList.tovar) as tovar1,raschetsMaterial.type,raschetsMaterial.metall,raschetsMaterial.ed_izm",
            "MaterialZakup": "MaterialZakup.date,MaterialZakup.document"
        },
        "sortby": {
            "MaterialZakupList.id": "ASC"
        }
    }
    При экспорте в excel без правок pdoTools на строке
    IF(MaterialZakupList.prihod_date IS NULL,'в дороге','получено')
    'в дороге' обрамляется апострофами.
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    2