Конструктор MySQL запросов к базе ModX
2010-11-10
Очень часто нужно получить данные дополнительных полей для страницы в сниппете, и для этого есть необходимые встроенны функции. Но если нужно получить данные нескольких страниц то их нужно перебирать в цикле и для каждой вызывать функцию, что очень затратно потому что делается много запросов к базе. Поэтому обычно пишится один MySQL запрос на выборку данных. Но они очень получаются очень длинными, особенно если много дополнительных полей, и что бы облегчить себе жизнь я написал несколько простых функций.

Сначало немного описания того как ModX хранит данные.

Основные данны хранятся в таблице site_content а дополнительны поля хранятся в таблице site_tmplvar_contentvalues, ниже представлен конструктор таблицы site_tmplvar_contentvalues.

CREATE TABLE `site_tmplvar_contentvalues` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `tmplvarid` int(10) NOT NULL DEFAULT '0' COMMENT 'Template Variable id',
 `contentid` int(10) NOT NULL DEFAULT '0' COMMENT 'Site Content Id',
 `value` text,
 PRIMARY KEY (`id`),
 KEY `idx_tmplvarid` (`tmplvarid`),
 KEY `idx_id` (`contentid`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='Site Template Variables Content Values Link Table'

Значение каждого дополнительного поля хранится в отдельной записи с кодом поля (tmplvarid) и id страницы (contentid) к которым приписываются значения дополнительного кода. Такая конструкция приводит к тому что приходится писать запрос с множеством объединений.

Например такой:

SELECT 
    t.id,
    t.pagetitle, 
    tblogContent.value as blogContent, 
    tdocumentTags.value as documentTags, 
    tloginName.value as loginName 
FROM 
    site_content t 
    LEFT OUTER JOIN site_tmplvar_contentvalues tblogContent 
        ON tblogContent.tmplvarid = 1 AND tblogContent.contentid=t.id 
    LEFT OUTER JOIN site_tmplvar_contentvalues tdocumentTags 
        ON tdocumentTags.tmplvarid = 3 AND tdocumentTags.contentid=t.id 
    LEFT OUTER JOIN site_tmplvar_contentvalues tloginName 
        ON tloginName.tmplvarid = 2 AND tloginName.contentid=t.id
WHERE
    t.published = 1 AND
    t.hidemenu = 0 AND
    t.deleted = 0 AND
    t.parent = 1

И это еще все просто, если будет множество дополнительных полей то запрос может быть еще длиннее. Но зато есть огромный плюс, таким запросом можно получить все необходимые данные.

И для того чтобы не писать такие запросы каждый раз я написал несколько функций.

Первая функция. Это вспомогательная функция, которая читает все дополнительные поля для определенного шаблон. Она нужна только для того чтобы самому не писать список полей. Впринцыпе ее можно и не использовать т.к. можно список полей составить самому.

/**
 * Прочитать список TV полей у шаблона.
 * @id_template integer код шаблона
 * @return array массив полей принадлежащих шаблону, ключи это код дополнительных полей, содержимое название доп.поля
 */
function getFieldsOfTemplate($id_template)
{
    global $modx;
    
    $prefix = $modx->db->config['table_prefix']; // префикс таблицы
    
    // прочитать список полей принадлежащих данному шаблону
    $sql = "SELECT st.id, st.name
                FROM {$prefix}site_tmplvars st, {$prefix}site_tmplvar_templates stt
                WHERE 
                    st.id = stt.tmplvarid AND
                    stt.templateid = $id_template";
    $fields = array();
    if($res = $modx->db->query($sql)) {
        while($row = $modx->db->getRow($res)) {
            $fields[$row['id']] = $row['name'];
        }
    }
    
    return $fields;
}

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

Вторая функция. Создает секцию FROM (смотри экземпляр запроса, выше на страице). В качестве fields нужно передать массив дополнительных полей составленный вручную или с помощью функции getFieldsOfTemplate() (смотри выше). Полученный результат нужно дописать указав условия и перечислив список полей для выборки и можно использовать в качестве запроса к базе.

/**
 * Получить секцию FROM для mysql запроса, для последующей выборке нужных полей
 * @fields array масив со списком дополнительных полей, ключи этого массива код этого поля
 * @table_name string имя псевдонима таблицы с помощью которого можно обращаться к данным. По умолчанию "t"
 *     К данным из страницы можно обратиться, например так t.pagetitle
 *     К дополнительным полям как псевдоним плюс имя дополнительного поля,
 *     например к дополнительному полю image нужно обратиться timage.value
 * @return string часть sql запроса
 */
function getSqlSectionFrom($fields, $table_name = "t")
{
    global $modx;
    
    $prefix = $modx->db->config['table_prefix']; // префикс таблицы
    
    $sql_result = " FROM {$prefix}site_content $table_name ";
    foreach($fields as $key => $field) {
        $sql_result .= "LEFT OUTER JOIN {$prefix}site_tmplvar_contentvalues {$table_name}{$field} ON {$table_name}{$field}.tmplvarid = $key AND {$table_name}{$field}.contentid=t.id ";
    }
    
    return $sql_result;
}

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

/**
 * Получить выборку определенных полей из таблицы site_content и дополнительных полей
 * @fields string список требуемых полей из таблицы site_content (поля перечисляются в одну строку через запитую)
 * @tvfields array масив со списком дополнительных полей, ключи этого массива код этого поля
 * @table_name string имя псевдонима таблицы с помощью которого можно обращаться к данным. По умолчанию "t"
 *     К данным из страницы можно обратиться, например так t.pagetitle
 *     К дополнительным полям как псевдоним плюс имя дополнительного поля,
 *     например к дополнительному полю image нужно обратиться timage.value
 * @return srting sql запрос, только части SELECT и FROM
 */
function getSqlFields($fields, $tvfields, $table_name = "t")
{
    // переписать имена полей из таблицы site_content
    $sc_fields = explode(',', $fields);
    for($i=0; $i<count($sc_fields); $i++) {
        $sc_fields[$i] = $table_name.'.'.$sc_fields[$i];
    }
    
    // переписать имена дополнительных полей
    foreach($tvfields as $i) {
        $sc_fields[] = "{$table_name}{$i}.value as {$i}";
    }
    
    return "SELECT ".implode(', ', $sc_fields)." ".getSqlSectionFrom($tvfields, $table_name = "t");
}

И наконец небольшой пример, в нем мы получим запрос к базе, представленный выше в качестве примера и сделаем выборку из базы.

$sql = getSqlFields('id,pagetitle', array(1=>'blogContent', 3=>'documentTags', 2=>'loginName'));
$sql .= ' WHERE t.published = 1 AND t.hidemenu = 0 AND t.deleted = 0 AND t.parent = 1 ';

$res = $modx->db->query($sql);
while($row = $modx->db->getRow($res)) {
    echo "{$row->id}:{$row->pagetitle} - {$row->blogContent} - {$row->documentTags} - {$row->loginName}<br/>";
}
www.000webhost.com