umanit / tree-bundle
管理数据库中的内容树
Requires
- php: ^8.0
- doctrine/doctrine-bundle: ^2.7
- doctrine/orm: ^2.13
- gedmo/doctrine-extensions: ~3.9
- symfony/framework-bundle: ^5.4|^6.0
README
此包允许您轻松管理内容类型路由,包括slug、面包屑和SEO
通用配置
由于所有内容的路由都由包管理,您必须在您的 config/routes.yaml
的末尾注册唯一路由(以免覆盖您的自定义路由)
umanit_tree:
resource: "@UmanitTreeBundle/Resources/config/routes.yaml"
prefix: /
如果您不使用Symfony Flex,请在 config/bundles.php
中注册该包。
Umanit\TreeBundle\UmanitTreeBundle::class => ['all' => true],
在您的 services.yaml
或单独的 packages/gedmo.yaml
中添加Gedmo配置。查看文档
services: # Doctrine Extension listeners to handle behaviors gedmo.listener.tree: class: Gedmo\Tree\TreeListener tags: - { name: doctrine.event_subscriber, connection: default, priority: 10 } calls: - [setAnnotationReader, ['@annotation_reader']] Gedmo\Translatable\TranslatableListener: tags: - { name: doctrine.event_subscriber, connection: default } calls: - [setAnnotationReader, ['@annotation_reader']] - [setDefaultLocale, ['%locale%']] - [setTranslationFallback, [false]] gedmo.listener.timestampable: class: Gedmo\Timestampable\TimestampableListener tags: - { name: doctrine.event_subscriber, connection: default } calls: - [setAnnotationReader, ['@annotation_reader']] gedmo.listener.sluggable: class: Gedmo\Sluggable\SluggableListener tags: - { name: doctrine.event_subscriber, connection: default, priority: 100 } calls: - [setAnnotationReader, ['@annotation_reader']] gedmo.listener.sortable: class: Gedmo\Sortable\SortableListener tags: - { name: doctrine.event_subscriber, connection: default } calls: - [setAnnotationReader, ['@annotation_reader']] gedmo.listener.loggable: class: Gedmo\Loggable\LoggableListener tags: - { name: doctrine.event_subscriber, connection: default } calls: - [setAnnotationReader, ['@annotation_reader']]
更新数据库模式以添加我们的模型
bin/console doctrine:schema:update --force
或者如果您使用 DoctrineMigrationsBundle
bin/console doctrine:migrations:migrate
现在,您必须创建根节点。默认对象是 RootEntity,但您可以在配置中覆盖它(参数 umanit_tree.root_class
)。
要创建根节点,请执行以下命令
bin/console umanit:tree:initialize
创建一个新的节点类型
现在,您可以轻松管理新的节点类型
- 创建一个实体
- 实现
Umanit\TreeBundle\Model\TreeNodeInterface
和Umanit\TreeBundle\Model\SeoInterface
- 现在,您可以使用
Umanit\TreeBundle\Model\TreeNodeTrait
和Umanit\TreeBundle\Model\SeoTrait
来为大多数需要实现的方法提供默认实现
TreeNodeInterface 的 4 个方法是
public function getTreeNodeName(): string;
: 返回节点名称,用于构建路由的slugpublic function getParents(): array;
: 返回父对象。例如,如果您想为"My Product"创建路径 /category/my-product,则必须返回一个包含实现 TreeNodeInterface 的对象 "Category" 的数组public function createRootNodeByDefault(): bool;
: 如果节点有或没有父节点,是否应该创建节点?(例如 /my-product)public function getLocale(): ?string
: 对象的本地化(由 Symfony 的$request->getLocale()
识别的本地化)
您可以使用 Umanit\TreeBundle\Model\SeoInterface
和 Umanit\TreeBundle\Model\SeoTrait
来管理一些SEO选项,如标题、描述和关键词,并自动将属性 "seoMetadata" 添加到实体中
绑定控制器并获取实体
为了绑定控制器(或模板)到实体,您必须配置 node_types
键
node_types: - class: App\Entity\Page # your entity controller: App\Controller\PageController::get # action to call
实体(或节点)将可在请求属性中访问。
public function getAction(Request $request) { $entity = $request->attributes->get('contentObject'); // Your entity $node = $request->attributes->get('contentNode'); // TreeBundle's node }
创建父节点选择器
用法示例
$builder ->add('parents', \Umanit\TreeBundle\Form\Type\TreeNodeType::class, [ 'required' => false, 'by_reference' => false, ]);
创建SEO元数据表单
用法示例
$builder ->add('seoMetadata', \Umanit\TreeBundle\Form\Type\SeoMetadataType::class, [ 'required' => false, ]);
创建链接选择器
您可以创建指向一个或多个节点(或外部链接)的链接。
在将包含链接的实体中,添加与实体 Umanit\TreeBundle\Entity\Link
的关系。
在您的表单中,您可以使用 Umanit\TreeBundle\Form\Type\LinkType
来具体化关系。默认情况下,您将有两个字段,"内部链接"(一个文本字段)和"外部链接"(一个选择框)。默认情况下,选择框将是空的。您必须通过提供允许的模型来填充它。您可以保留一个具有选项的字段:allow_internal: false
或 allow_external: false
。注意:一次只能填写一个字段。
您可以使用 label_internal
和 label_external
定义标签
用法示例
$builder ->add('link', 'umanit_link_type_translatable', [ 'label' => 'Link', // List of content types available 'models' => [ 'Page' => 'Umanit\App\Entity\Page', 'Article' => 'Umanit\App\Entity\Article', ], ], [ // Filters for some content types (if needed) 'query_filters' => [ 'App\Entity\Page' => ['locale' => 'en'], ], 'allow_external' => false, ] ]);
事件
您可以订阅一些事件来修改某些行为
(为了)
umanit.node.before_update
:在实体任何节点保存之前被调用umanit.node.parent_register
:允许向实体添加/删除父级umanit.node.updated
:当实体保存其节点和父级后被调用一次
Twig 辅助函数
get_seo_title(default = "", override = false)
get_seo_description(default = "", override = false)
get_seo_keywords(default = "", override = false)
如果路由由实现 SeoInterface 的实体管理,则返回当前文档的标题、描述和关键词。否则,使用默认值(来自配置),或如果设置了,则使用 "default" 参数的值。如果将 override 设置为 true,则始终使用 "default" 参数的值。
get_breadcrumb(elements = array())
返回面包屑(名称/链接数组)。如果路由由实体管理,它将解析当前实体的所有父级。您可以使用 "elements" 参数添加额外的链接。一个名称/链接数组。
get_path(object, parentObject = null, root = false, absolute = false, parameters = [])
返回给定实体的路由(如果实体实现了 TreeNodeInterface)
get_path_from_node(node, absolute = false, parameters = [])
返回给定节点(Umanit\TreeBundle\Entity\Node
实例)的路由
get_path_from_link(link)
返回给定链接实例(Umanit\TreeBundle\Entity\Link
实例)的路径
is_external_link(link)
如果给定的链接指向外部 URL(Umanit\TreeBundle\Entity\Link
实例),则返回 true。
使用菜单管理
/!\ 由于性能考虑,我们仅支持 PostgreSQL。
按照以下两个步骤开始
1. 创建您的菜单实体(使用注解或属性)
namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Umanit\TreeBundle\Entity\AbstractMenu as BaseMenu; use Umanit\TreeBundle\Repository\MenuRepository; /** * Menu * * @ORM\Table(name="menu") * @ORM\Entity(repositoryClass="Umanit\TreeBundle\Repository\MenuRepository") // Using TreeBundle's repository is * mandatory * @ORM\HasLifecycleCallbacks() // This is mandatory too */ #[ORM\Table(name: 'menu')] #[ORM\Entity(repositoryClass: MenuRepository::class)] // Using TreeBundle's repository is mandatory #[ORM\HasLifecycleCallbacks] // Mandatory class Menu extends BaseMenu { }
2. 配置 TreeBundle 以使用您的菜单实体
# app/config/config.yml umanit_tree: # ... menu_entity_class: App\Entity\Menu
使用方法
前端
TreeBundle 不提供菜单的模板。一个全局 twig 变量被注入到您的网站中,请使用它来构建您的菜单模板。
示例
<nav class="nav-primary"> <ul class="nav-primary__list nav-primary__lvl-1"> {% for menu in menus %} {% if menu.position == 'primary' %} <li class="nav-primary__item"> <a href="{{ menu.link is empty ? '#' : get_path_from_link(menu.link) }}" class="nav-primary__link"> {{- menu.title|raw -}} {#- <br class="hidden-xs hidden-sm"> -#} </a> {% if menu.children is not empty %} <ul class="nav-primary__list nav-primary__lvl-2"> {% for subMenu in menu.children %} {% if subMenu.link is empty %} <li class="nav-primary__label">{{ subMenu.title|raw }}</li> {% else %} <li class="nav-primary__item"> <a href="{{ get_path_from_link(subMenu.link) }}" class="nav-primary__link" <span class="nav-primary__text">{{ subMenu.title|raw }}</span> </a> </li> {% endif %} {% endfor %} </ul> {% endif %} </li> {% endif %} {% endfor %} </ul> </nav>
菜单管理
提供了一个 CRUD,用于管理您的菜单。它在路由 tree_admin_menu_dashboard
、/admin/menu 上可用。
/!\ 您需要具有 ROLE_TREE_MENU_ADMIN
角色才能访问该路由。
首先运行 php bin/console assets:install
以获取您的 web/public 目录中的资产。
自定义管理布局
可以通过设置 admin_layout
配置值来根据需要自定义布局。
示例:如果您想使用 Sonata Admin 的布局
# config.yml umanit_tree: # ... admin_layout: '@SonataAdmin/standard_layout.html.twig' # Default is '@UmanitTree/admin/default_layout.html.twig'
菜单管理有 3 个 JavaScript 依赖项,您还应该将它们包含在内。查看默认布局文件 default_layout.html.twig。
<!-- @UmanitTree/admin/default_layout.html.twig --> <script src="{{ asset('bundles/umanittree/vendor/js/jquery-ui.min.js') }}"></script> <script src="{{ asset('bundles/umanittree/vendor/js/jquery.fancytree-all-deps.min.js') }}"></script> <script src="{{ asset('bundles/umanittree/vendor/js/jquery.fancytree.dnd.js') }}"></script>
TreeBundle 附带这些资产,您可以使用它们或您自己的。
再次,如果您想与 SonataAdmin 一起使用,请按以下方式配置
sonata_admin: # ... assets: extra_stylesheets: # TreeBundle's assets - bundles/umanittree/css/admin.css - bundles/umanittree/css/vendor/ui.fancytree.min.css extra_javascripts: # TreeBundle's assets - bundles/umanittree/js/vendor/jquery-ui.min.js - bundles/umanittree/js/vendor/jquery.fancytree-all-deps.min.js - bundles/umanittree/js/vendor/jquery.fancytree.dnd.js
自定义管理表单
假设您在菜单实体上添加了一个图像属性,并想使用 VichUploader 来管理它。
首先,创建一个表单类型
namespace App\Form; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Vich\UploaderBundle\Form\Type\VichImageType; use Umanit\TreeBundle\Form\Type\MenuType as BaseMenuType; class MenuType extends BaseMenuType { public function buildForm(FormBuilderInterface $builder, array $options) { parent::buildForm($builder, $options); $builder ->add('imageFile', VichImageType::class, [ 'label' => 'Image', 'required' => false, 'allow_delete' => true, 'attr' => [ 'imagine_pattern' => 'admin', ], ]) ->add('altImage') ; } }
然后将您的表单类型添加到 TreeBundle 的配置中
umanit_tree: # ... menu_form_class: App\Form\MenuType
配置参考
umanit_tree: locale: '%locale%' # Optional. Default locale to use root_class: '\Umanit\TreeBundle\Entity\RootEntity' # Optional. Class for the root node. If you have a homepage object, put it there admin_layout: '@UmanitTree/admin/default_layout.html.twig' # Optional. Default layout for the menu admin section menu_form_class: 'Umanit\TreeBundle\Form\Type\MenuType' # Optional. Default form for Menu menu_entity_class: 'App\Entity\Menu' # Optional. Your menu entity. Required if you want to use the menu admin menus: ['primary'] # Optional. Configure you menus. # Defines configuration per node types. You can set a specific controller per class and set if the node type must appear in the menu admin. node_types: # Prototype - class: ~ # Required. Ex. : App\Entity\Page controller: ~ # Optional. Default FrameworkBundle:Template:template. Ex. : App:Page:show template: ~ # Required if controller is not set. menu: ~ # Optional. Default is false # Seo default values and translation domain seo: redirect_301: true # Redirect old URLs to new ones default_title: 'Umanit Tree' default_description: 'Umanit tree bundle' default_keywords: 'umanit, web, bundle, symfony2' translation_domain: 'messages' # Root node and translation domain for breadcrumb elements breadcrumb: root_name: 'Home' translation_domain: 'messages'