承接 datastructure/tree 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

datastructure/tree

最新稳定版本:v1.0.0

Composer 安装命令:

composer require datastructure/tree

包简介

A clean and flexible PHP library for building and manipulating tree structures

README 文档

README

Чистая и гибкая PHP библиотека для построения и манипулирования древовидными структурами. Разработана с использованием принципов SOLID и чистого кода для PHP 8+.

Возможности

  • 🌳 Универсальные N-арные деревья - создавайте деревья с любым количеством потомков
  • 🔍 Мощная система поиска - находите узлы по различным критериям
  • 🔄 Гибкие итераторы - обход в глубину (DFS) и в ширину (BFS)
  • Динамические атрибуты - добавляйте любые свойства к узлам на лету
  • 🛡️ Типобезопасность - строгая типизация PHP 8
  • Полностью протестировано - высокое покрытие тестами
  • 📦 Standalone - работает независимо от фреймворков

Требования

  • PHP >= 8.0

Установка

composer require datastructure/tree

Быстрый старт

<?php

use DataStructure\Tree\Node;
use DataStructure\Tree\Tree;

// Создание узлов
$root = new Node('root', ['name' => 'Корневая категория']);
$electronics = new Node('electronics', ['name' => 'Электроника']);
$computers = new Node('computers', ['name' => 'Компьютеры']);

// Построение дерева
$root->addChild($electronics);
$electronics->addChild($computers);

// Создание дерева
$tree = new Tree($root);

// Количество узлов
echo $tree->count(); // 3

Основное использование

Работа с узлами

Создание узлов с динамическими атрибутами

$node = new Node('product-1', [
    'name' => 'Ноутбук',
    'price' => 1500,
    'stock' => 10
]);

// Получение атрибутов
echo $node->getAttribute('name'); // "Ноутбук"
echo $node->getAttribute('discount', 0); // 0 (значение по умолчанию)

// Установка атрибутов
$node->setAttribute('discount', 10);
$node->setAttribute('featured', true);

// Проверка наличия атрибута
if ($node->hasAttribute('price')) {
    // ...
}

// Удаление атрибута
$node->removeAttribute('stock');

Работа с иерархией

$parent = new Node('parent');
$child1 = new Node('child1');
$child2 = new Node('child2');

// Добавление потомков
$parent->addChild($child1);
$parent->addChild($child2);

// Проверки
$parent->hasChildren(); // true
$parent->isRoot();      // true
$child1->isLeaf();      // true

// Получение связей
$child1->getParent();      // $parent
$parent->getChildren();    // [$child1, $child2]

// Получение предков и потомков
$child1->getAncestors();   // Все узлы от родителя до корня
$parent->getDescendants(); // Все дочерние узлы рекурсивно

// Глубина узла
$child1->getDepth(); // 1 (корень = 0)

Работа с деревьями

Создание дерева

$root = new Node('root', ['name' => 'Каталог']);
$tree = new Tree($root);

// Или из массива
$tree = Tree::fromArray([
    'id' => 'root',
    'attributes' => ['name' => 'Каталог'],
    'children' => [
        [
            'id' => 'child1',
            'attributes' => ['name' => 'Категория 1'],
            'children' => []
        ]
    ]
]);

Обход дерева

// Обход в глубину (DFS)
foreach ($tree->traverseDepthFirst() as $node) {
    echo $node->getAttribute('name') . "\n";
}

// Обход в ширину (BFS)
foreach ($tree->traverseBreadthFirst() as $node) {
    echo $node->getAttribute('name') . "\n";
}

// Выполнение функции для каждого узла
$tree->walk(function($node) {
    $node->setAttribute('visited', true);
});

Поиск узлов

Простой поиск

// Найти первый узел
$node = $tree->find(function($node) {
    return $node->getAttribute('price') > 1000;
});

// Найти все узлы
$nodes = $tree->findAll(function($node) {
    return $node->getAttribute('category') === 'electronics';
});

// Поиск по ID
$node = $tree->findById('product-123');

// Поиск по атрибуту
$nodes = $tree->findByAttribute('status', 'active');

Расширенный поиск с критериями

use DataStructure\Tree\Search\SearchCriteria;

$criteria = (new SearchCriteria())
    ->whereAttribute('status', 'active')
    ->whereDepthGreaterThan(0)
    ->whereIsLeaf();

// Найти первый подходящий узел
$node = $tree->find($criteria->toCallable());

// Найти все подходящие узлы
$nodes = $tree->findAll($criteria->toCallable());

Доступные методы SearchCriteria

$criteria = new SearchCriteria();

// Поиск по ID
$criteria->whereId('node-123');

// Поиск по атрибуту
$criteria->whereAttribute('type', 'category');

// Проверка наличия атрибута
$criteria->whereHasAttribute('price');

// Поиск корневых узлов
$criteria->whereIsRoot();

// Поиск листовых узлов
$criteria->whereIsLeaf();

// Поиск по глубине
$criteria->whereDepth(2);
$criteria->whereDepthGreaterThan(1);
$criteria->whereDepthLessThan(3);

// Кастомное условие
$criteria->where(function($node) {
    return str_starts_with($node->getId(), 'product-');
});

// Комбинирование условий
$criteria
    ->whereAttribute('status', 'active')
    ->whereDepthGreaterThan(0)
    ->where(fn($n) => $n->getAttribute('price') < 100);

Замена данных узла

$node = $tree->findById('product-1');

// Заменить/обновить атрибуты узла
$tree->replace($node, [
    'name' => 'Новое имя',
    'price' => 1200,
    'updated_at' => time()
]);

// Старые атрибуты сохраняются, новые добавляются/обновляются

Дополнительные методы дерева

// Подсчет всех узлов
$tree->count(); // 15

// Максимальная глубина дерева
$tree->getMaxDepth(); // 3

// Получение всех листовых узлов
$leaves = $tree->getLeaves();

// Получение узлов на определенной глубине
$level2Nodes = $tree->getNodesAtDepth(2);

// Проверка существования узла
$exists = $tree->exists(fn($n) => $n->getId() === 'target-id');

// Преобразование в массив
$array = $tree->toArray();

Примеры использования

Пример 1: Каталог товаров

use DataStructure\Tree\Node;
use DataStructure\Tree\Tree;

// Создание структуры каталога
$catalog = new Node('catalog', ['name' => 'Каталог']);
$electronics = new Node('electronics', ['name' => 'Электроника', 'active' => true]);
$computers = new Node('computers', ['name' => 'Компьютеры', 'active' => true]);
$laptops = new Node('laptops', ['name' => 'Ноутбуки', 'active' => true]);

$catalog->addChild($electronics);
$electronics->addChild($computers);
$computers->addChild($laptops);

$tree = new Tree($catalog);

// Поиск активных категорий
$activeCategories = $tree->findAll(fn($n) => $n->getAttribute('active') === true);

// Получение всех подкатегорий электроники
$electronics = $tree->findById('electronics');
$subcategories = $electronics->getDescendants();

Пример 2: Организационная структура

$ceo = new Node('ceo', ['name' => 'CEO', 'salary' => 200000]);
$cto = new Node('cto', ['name' => 'CTO', 'salary' => 150000]);
$developer1 = new Node('dev1', ['name' => 'Senior Developer', 'salary' => 100000]);
$developer2 = new Node('dev2', ['name' => 'Junior Developer', 'salary' => 60000]);

$ceo->addChild($cto);
$cto->addChild($developer1);
$cto->addChild($developer2);

$tree = new Tree($ceo);

// Найти всех с зарплатой больше 80000
$highEarners = $tree->findAll(fn($n) => $n->getAttribute('salary') > 80000);

// Получить всех подчиненных CTO
$ctoNode = $tree->findById('cto');
$subordinates = $ctoNode->getDescendants();

Пример 3: Система меню

$menu = new Node('main-menu', ['label' => 'Главное меню']);
$home = new Node('home', ['label' => 'Главная', 'url' => '/', 'visible' => true]);
$products = new Node('products', ['label' => 'Товары', 'url' => '/products', 'visible' => true]);
$sale = new Node('sale', ['label' => 'Распродажа', 'url' => '/sale', 'visible' => true]);

$menu->addChild($home);
$menu->addChild($products);
$products->addChild($sale);

$tree = new Tree($menu);

// Получить только видимые пункты меню
$visibleItems = $tree->findAll(fn($n) => $n->getAttribute('visible') === true);

// Обновить URL
$saleNode = $tree->findById('sale');
$tree->replace($saleNode, ['url' => '/special-sale', 'highlight' => true]);

Тестирование

# Установка зависимостей
composer install

# Запуск тестов
./vendor/bin/phpunit

# Запуск с покрытием
./vendor/bin/phpunit --coverage-html coverage

Архитектура

Библиотека следует принципам SOLID и чистого кода:

  • Single Responsibility: Каждый класс имеет одну ответственность
  • Open/Closed: Легко расширяется через интерфейсы
  • Liskov Substitution: Все реализации соответствуют контрактам
  • Interface Segregation: Интерфейсы разделены по функциональности
  • Dependency Inversion: Зависимости от абстракций, а не от реализаций

Структура проекта

src/
├── NodeInterface.php          # Интерфейс узла
├── Node.php                   # Реализация узла
├── TreeInterface.php          # Интерфейс дерева
├── Tree.php                   # Реализация дерева
├── Iterator/
│   ├── DepthFirstIterator.php  # Обход в глубину
│   └── BreadthFirstIterator.php # Обход в ширину
├── Search/
│   ├── SearchCriteria.php      # Построитель критериев поиска
│   └── NodeSearcher.php        # Поисковик узлов
└── Exception/
    ├── TreeException.php       # Базовое исключение
    └── NodeNotFoundException.php # Исключение "узел не найден"

Производительность

  • Эффективные алгоритмы обхода с использованием итераторов
  • Ленивая инициализация поисковика
  • Минимальное потребление памяти
  • Защита от циклических ссылок

Лицензия

MIT License

Автор

Edige

Вклад

Приветствуются pull requests! Для больших изменений сначала откройте issue для обсуждения предлагаемых изменений.

Дополнительные ресурсы

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-20