arodax/doctrine-extensions-tree

Doctrine 的树形扩展

v3.2.4 2022-10-13 22:23 UTC

This package is auto-updated.

Last update: 2024-09-14 02:30:11 UTC


README

Licence MIT Build Status contributions welcome downloads total

此扩展允许您使用 Doctrine ORM 在数据库中以层次结构存储数据。

注意:此代码是 Atlantic18/DoctrineExtensions 2.4.x 分支的树形扩展的硬分支。

许可

MIT

变更日志

  • 3.2.3 修复了缓存项 ID 生成器

  • 3.2.2 当安装 symfony/cache 时,自动缓存实体元数据。

  • 3.2.1 修复了 XML 驱动程序中类和文件名不匹配的问题

  • 3.2.0 废弃了 TreeRight、TreeLeft、TreeClosure、TreeRoot、TreePath、TreeLevel 注解,使用不带 "Tree" 前缀的它们(例如,Right、Left、Closure ...)

  • 3.2.0 添加了对原生 PHP 属性的支持

  • 3.1.0 将 TreeAdapterInterface 重命名为 AdapterInterface

  • 3.0.0 将命名空间更改为 Arodax\Doctrine\Extensions\Tree,包已重命名为 arodax/doctrine-extensions-tree。请确保您在 config/packages/doctrine.yaml 中更改路径和命名空间 - 以下安装指南中有示例!

  • 2.0.0 doctrine/common 包的最低兼容版本已提高至 3.0.*

  • 1.0.3 实现了 #2020,删除了查询中的硬编码父列实例

  • 1.0.2 添加了原始扩展中缺少的存储库

  • 1.0.1 实现了 #2001,修复了导致左/右顺序错误的问题

安装

使用 composer 安装扩展

composer require arodax/doctrine-extensions-tree

在 Symfony 项目中使用

目前还没有 flex 脚本,因此您需要手动通过将以下内容添加到配置文件中来启用扩展

config/packages/doctrine.yaml

parameters:
    ...   
doctrine:
    dbal:
        ...
    orm:
        ...
        mappings:
            ...                
            Arodax\Doctrine\Extensions\Tree:
                is_bundle: false
                type: annotation #attribute
                dir: '%kernel.project_dir%/vendor/arodax/doctrine-extensions-tree/src/Entity'
                prefix: 'Arodax\Doctrine\Extensions\Tree\Entity'

config/services/doctrine.yaml

parameters:
    ...

services:
    ...
    Arodax\Doctrine\Extensions\Tree\TreeSubscriber:
        class: Arodax\Doctrine\Extensions\Tree\TreeSubscriber
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ '@annotation_reader' ] ]

为层次结构树准备实体

注释您的实体

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Arodax\Doctrine\Extensions\Tree\Mapping\Annotation as Tree;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CategoryRepository")
 * @Tree\Tree(type="nested")
 */
class Category
{

    /**
     * @var integer
     *
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @Tree\Left
     * @ORM\Column(name="lft", type="integer")
     */
     #[Tree\Left]
    private $lft;

    /**
     * @Tree\Level
     * @ORM\Column(name="lvl", type="integer")
     */
     #[Tree\Level]
    private $lvl;

    /**
     * @Tree\Right()
     * @ORM\Column(name="rgt", type="integer")
     */
     #[Tree\Right]
    private $rgt;

    /**
     * @Tree\Root()
     * @ORM\ManyToOne(targetEntity="MenuItem")
     * @ORM\JoinColumn(name="tree_root", referencedColumnName="id", onDelete="CASCADE")
     */
     #[Tree\Root]
    private $root;

    /**
     * @Tree\ParentNode()
     * @ORM\ManyToOne(targetEntity="MenuItem", inversedBy="children")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
     */
     #[Tree\ParentNode]
    private $parent;

    /**
     * @ORM\OneToMany(targetEntity="MenuItem", mappedBy="parent")
     * @ORM\OrderBy({"lft" = "ASC"})
     */
    private $children;

扩展仓库

Arodax\Doctrine\Extensions\Tree\Entity\Repository\NestedTreeRepository 扩展您的实体仓库,这允许您使用特殊方法来处理树

<?php

namespace App\Repository\Core\Menu;

use App\Entity\Category;
use Arodax\Doctrine\Extensions\Tree\Entity\Repository\NestedTreeRepository;

class MenuItemRepository extends NestedTreeRepository
{
    //
}

用法

保存树

保存一些类别

<?php

$food = new Category();
$food->setTitle('Food');

$fruits = new Category();
$fruits->setTitle('Fruits');
$fruits->setParent($food);

$vegetables = new Category();
$vegetables->setTitle('Vegetables');
$vegetables->setParent($food);

$carrots = new Category();
$carrots->setTitle('Carrots');
$carrots->setParent($vegetables);

$this->em->persist($food);
$this->em->persist($fruits);
$this->em->persist($vegetables);
$this->em->persist($carrots);
$this->em->flush();

flush 后的结果将生成食品树

food (1-8)
    /fruits (2-3)
    /vegetables (4-7)
        /carrots (5-6)

在不同位置插入节点

<?php

$food = new Category();
$food->setTitle('Food');

$fruits = new Category();
$fruits->setTitle('Fruits');

$vegetables = new Category();
$vegetables->setTitle('Vegetables');

$carrots = new Category();
$carrots->setTitle('Carrots');

$treeRepository
    ->persistAsFirstChild($food)
    ->persistAsFirstChildOf($fruits, $food)
    ->persistAsLastChildOf($vegetables, $food)
    ->persistAsNextSiblingOf($carrots, $fruits);

$em->flush();

使用仓库函数

<?php

$repo = $em->getRepository('Entity\Category');

$food = $repo->findOneByTitle('Food');
echo $repo->childCount($food);
// prints: 3
echo $repo->childCount($food, true/*direct*/);
// prints: 2
$children = $repo->children($food);
// $children contains:
// 3 nodes
$children = $repo->children($food, false, 'title');
// will sort the children by title
$carrots = $repo->findOneByTitle('Carrots');
$path = $repo->getPath($carrots);
/* $path contains:
   0 => Food
   1 => Vegetables
   2 => Carrots
*/

// verification and recovery of tree
$repo->verify();
// can return TRUE if tree is valid, or array of errors found on tree
$repo->recover();
$em->flush(); // important: flush recovered nodes
// if tree has errors it will try to fix all tree nodes

// UNSAFE: be sure to backup before running this method when necessary, if you can use $em->remove($node);
// which would cascade to children
// single node removal
$vegies = $repo->findOneByTitle('Vegetables');
$repo->removeFromTree($vegies);
$em->clear(); // clear cached nodes
// it will remove this node from tree and reparent all children

// reordering the tree
$food = $repo->findOneByTitle('Food');
$repo->reorder($food, 'title');
// it will reorder all "Food" tree node left-right values by the title

有关更多示例和用法,请参阅原始包文档: https://github.com/Atlantic18/DoctrineExtensions/blob/v2.4.x/doc/tree.md