В предыдущей статье была описана двухуровневая иерархическая фильтрация, при которой с каждым разделом связаны собственные уникальные категории, даже если их слаги или названия не уникальны среди категорий в целом (такая возможность была добавлена только ради улучшения гибкости и не влияет на уникальность категорий). Как сделать фильтрацию с общими для всех разделов значениями вторичного признака, можно прочитать в данной статье.
Прежде всего необходимо решить, какой из двух независимых признаков будет первичным, а какой вторичным. В качестве первичного будет уместно выбрать тот признак, по которому фильтрация может выполняться автономно. Если фильтрация может выполняться автономно по каждому из признаков, в качестве первичного следует выбрать тот признак, который представляет наибольшую значимость для сайта, например содержит основные разделы сайта. Если и тут наблюдается равнозначность признаков, в качестве первичного можно выбрать любой из признаков, главное не допускать при этом вариативность адресов, т.е. возможность использовать на равных адреса форматов /property1/property2 и /property2/property1.
Для отбора по двум независимым признакам используется сложное условие с логический связкой И (AND) между первым и вторым простыми условиями:
`field1`=значение1 AND `field2`=значение2
Для сокрытия реальных полей и значений, задействованных в этом условии, а также для автоматической проверки существования поступающих состояний фильтра можно использовать возможности G-Drive. Чтобы не хранить состояния в виде всевозможных комбинаций значений для первого и второго простых условий, можно размещать значения для первого условия в таблице категорий, а для второго – в таблице объектов. Чтобы категории ссылались на одну и ту же таблицу объектов, можно использовать представления.
Реализуем фильтрацию элементов по разделам и типам. Код создания таблицы разделов (site_categories) и примерных записей этой таблицы можно взять из предыдущей статьи, заменив имя модуля на filterp2. Создание таблицы типов и примерных записей этой таблицы (тут же можно создать упоминавшиеся выше представления):
CREATE TABLE `site_types` ( `type` tinyint(3) unsigned NOT NULL AUTO_INCREMENT, `id` varchar(16) NOT NULL, `name` tinytext NOT NULL, PRIMARY KEY (`type`), UNIQUE (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3; INSERT INTO `site_types` (`type`, `id`, `name`) VALUES (1, 'type-1', 'Тип 1'), (2, 'type-2', 'Тип 2'); CREATE ALGORITHM=MERGE VIEW `site_div_1` AS SELECT * FROM `site_types`; CREATE ALGORITHM=MERGE VIEW `site_div_2` AS SELECT * FROM `site_types`;
Для одной из категорий можно не создавать представление, а назвать таблицу объектов в точном соответствии с предполагаемым именем этого представления, например site_div_1 вместо site_types.
Создание таблицы фильтруемых элементов и примерных записей этой таблицы:
CREATE TABLE `site_items` ( `item` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` varchar(16) NOT NULL, `name` tinytext NOT NULL, `div` tinyint(3) unsigned NOT NULL, `type` tinyint(3) unsigned NOT NULL, PRIMARY KEY (`item`), UNIQUE (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5; INSERT INTO `site_items` (`item`, `id`, `name`, `div`, `type`) VALUES (1, 'item-1', 'Элемент 1', 1, 1), (2, 'item-2', 'Элемент 2', 1, 2), (3, 'item-3', 'Элемент 3', 2, 1), (4, 'item-4', 'Элемент 4', 2, 2);
Основной код модуля (filterp2.c.php):
<?php require INCLUDE_PATH.'query.php'; require INCLUDE_PATH.'getrow.php'; require INCLUDE_PATH.'pagelink.php'; $pp = 10; $fields = '`items`.*'; $tables = '`items`'; $where = '1'; if ($r0['div']) { $fields .= ",'{$r0['id']}' `div_id`,'{$link->escape_string($r0['name'])}' `div_name`"; $where .= " AND `div`={$r0['div']}"; } else { $fields .= ',`categories`.`id` `div_id`,`categories`.`name` `div_name`'; $tables .= ' LEFT JOIN `categories` USING(`div`)'; } if (isset($r1)) { $fields .= ",'{$r1['id']}' `type_id`,'{$link->escape_string($r1['name'])}' `type_name`"; $where .= " AND `type`={$r1['type']}"; } else { $fields .= ',`types`.`id` `type_id`,`types`.`name` `type_name`'; $tables .= ' LEFT JOIN `types` USING(`type`)'; } $result = query("SELECT FLOOR((COUNT(*)+".($pp - 1).")/$pp) FROM `items` WHERE $where"); list($pc) = $result->fetch_row(); $result->free(); rotate01(); if ($pn == 0 || $pn > $pc) { error(404); return; } $result = query("SELECT $fields FROM $tables WHERE $where ORDER BY `item` LIMIT ".(($pn - 1) * $pp).",$pp");
В приведенном коде опущен табличный префикс (site_). Код ротатора номеров страниц 0 и 1 (функции rotate01) описан в заключительной части статьи Как сделать пагинацию? и в первом комментарии к ней. Содержимое включаемого файла getrow.php (оно будет использовано в шаблоне страниц) представлено в статье Как сделать вывод списка статей?
Простейший шаблон страниц фильтра (filterp2.php):
<h1><?= $page['name'] ?> (стр. <?= $pn ?> из <?= $pc ?>)</h1> <?php while ($row = getrow($result)): ?> <h2><?= $row['name'] ?></h2> <p> <a href="/<?= $row['div_id'] ?>"><?= $row['div_name'] ?></a> / <a href="/<?= $row['div_id'] ?>/<?= $row['type_id'] ?>"><?= $row['type_name'] ?></a> </p> <?php endwhile; ?>
Для автономной фильтрации по типам можно создать специальный раздел div-all (или types, чтобы не создавать соответствующее представление) с нулевым идентификатором. Чтобы сделать недействительным адрес /div-all (или /types), в котором не указан тип, можно установить для категории режим разрешений 3 вместо 2 (значение поля bits 63 вместо 62). Также можно совместить этот раздел со специальной страницей без фильтрации, о создании которой рассказывалось в предыдущей статье, как это показано ниже.
Демонстрация: /filter-p2 и с автономной фильтрацией по типам /filter-p2/type-1 и /filter-p2/type-2.
Комментарии: 0
Отправить комментарий