Деревья
Простое дерево
Строится при помощи добавления в модель поля типа Родитель parent, которое хранит id родителя текущей записи. Для корневых записей значение данного поля равно -1.
Пример простого использования дерева при создании меню вложенных страниц.
class Pages extends Model
{
protected $name = 'Меню страниц';
protected $model_elements = [
['Активировать', 'bool', 'active', ['on_create' => true]],
['Отображать в меню', 'bool', 'in_menu', ['on_create' => true]],
['Название', 'char', 'name', ['required' => true]],
['Родительский раздел', 'parent', 'parent', ['max_depth' => 3]],
['Позиция', 'order', 'order'],
['Содержание', 'text', 'content', ['rich_text' => true]]
];
}
Связанное дерево
Для создания структуры вида каталог -> каталог -> каталог -> товар или альбом -> альбом -> изображение нужно связать модель с полем parent с другой моделью таким образом, чтобы конечный родитель первой модели являлся внешним ключом для записей другой модели.
Пример создания связанного дерева для многоуровневого каталога товаров.
class Catalogs extends Model
{
protected $name = 'Разделы каталога';
protected $model_elements = [
['Активен', 'bool', 'active', ['on_create' => true]],
['Название', 'char', 'name', ['required' => true]],
['Каталог', 'parent', 'parent', ['parent_for' => 'Products']],
['Ссылка', 'url', 'url'],
['Позиция', 'order', 'order']
];
}
class Products extends Model
{
protected $name = 'Товары каталога';
protected $model_elements = [
['Активен', 'bool', 'active', ['on_create' => true]],
['Название', 'char', 'name', ['required' => true]],
['Раздел каталога', 'enum', 'parent', ['foreign_key' => 'Catalogs', 'is_parent' => true]],
['Цена', 'int', 'price', ['required' => true]],
['Изображения', 'multi_images','images'],
['Описание', 'text', 'desc', ['rich_text' => true]],
['Позиция', 'order', 'order']
];
}
Специальные методы деревьев
Для быстрого доступа к родителям и потомкам в дереве предусмотрены специальные методы.
- getParents($id) - возвращает массив родительских записей (до записи со значением родительского поля равным -1). В результирующем массиве ключи - id записей, значения - названия записей соглаcно параметру '$name_field' из раздела Настройка модели.
- getChildren($id) - возвращает массив всех дочерних записей, рекурсивно обходя все ветви дерева. Результирующий массив строится аналогично предыдущему методу.
displayBreadcrumbs($id, $url_first [, $url_field]) - отображает ссылочный путь до записи с переданным id. Возвращает последовательность html тэгов в виде: ссылка -> ссылка -> ссылка -> span.
Входящие параметры:
$id текущей записи
$url_first - первая часть url, необязательный параметр
$url_field - название поля в котором хранится текстовая ссылка для данной записи (для построения url вида /catalog/toys).
Для предустановленной модели Pages ссылки автоматически строятся в виде /page/4 и /contacts (без первой части).
//Ищем запись
$catalog = $mv -> catalogs -> find(43);
//Родительские записи
$parents = $mv -> catalogs -> getParents($catalog -> id);
Debug::pre($parents);
//Дочерние записи
$children = $mv -> catalogs -> getChildren($catalog -> id);
Debug::pre($children);
//Ссылочный путь для раздела каталога
echo $mv -> catalogs -> displayBreadcrumbs($catalog -> id, 'category', 'url');
//Ссылочный путь для товара каталога
//Результат будет в виде ссылок: Игрушки -> Мячи -> Мяч красный
echo $mv -> products -> displayBreadcrumbs($product -> id, 'category');
Перечисленные методы могут также вызываться изнутри объекта модели. В примере ниже происходит определение страницы каталога на основе переданного URL.
class Catalogs extends Model
{
...
//Ищем активную запись по URL вида '/catalog/23' или '/catalog/tools'
//Маршрут вида'/catalog/*' => 'view-catalog.php' в файле 'config/routes.php'
public function defineCatalog(Router $router)
{
$url_parts = $router -> getUrlParts();
$record = null;
if(is_numeric($url_parts[1]))
$record = $this -> find(['id' => $url_parts[1], 'active' => 1]);
else
$record = $this -> find(['url' => $url_parts[1], 'active' => 1]);
if($record !== null)
{
$this -> id = $record -> id;
$this -> parents = $this -> getParents($record -> id);
}
return $record;
}
Рекурсивно строим меню каталога в модели Catalogs.
<?
public function displayCatalogMenu($parent)
{
//Корневой раздел или следующий по глубине рекурсивно
$rows = $this -> select(['parent' => $parent, 'active' => 1, 'order->asc' => 'order']);
$root_path = $this -> root_path.'catalog/';
$html = '';
foreach($rows as $row)
{
//Активный пункт меню
$active = '';
//Дочерние элементы
$children = [];
//Ищем дочерние элементы и ставим активный пункт
if($row['id'] == $this -> id || array_key_exists($row['id'], $this -> parents))
{
$children = $this -> select(['parent' => $row['id'], 'active' => 1]);
$active = ' class="active"';
}
//Формируем ссылку
$url = $root_path.($row['url'] ? $row['url'] : $row['id']);
$html .= '<li".$active."><a href="'.$url.'">'.$row['name'].'</a>';
//Выводим дочерние элементы если они есть
if(count($children) > 0)
$html .= '<ul>'.$this -> displayCatalogMenu($row['id']).'</ul>';
$html .= '</li>';
}
return $html;
}
Предыдущий раздел
Внешние ключи