现在我们把顶部的菜单渲染一下，先来处理主导航菜单。  
TEEBB使用了`KnpMenuBundle`，使用`KnpMenuBundle`提供的`knp_menu_render`函数渲染菜单，第一个参数我们传入菜单的别名 
```twig 
{{ knp_menu_render('main_nav') }}  
```
刷新，主菜单已经显示了出来，但是样式有些丑，我们需要创建一个菜单的模板，在knp_menu_render 函数中传入template参数，
```twig
{{knp_menu_render('main_nav', {template: 'menu/main_nav.html.twig'})}}
```
在templates目录中添加menu目录，添加main_nav.html.twig文件，
menu模板需要 继承knp_menu.html.twig文件，这是knpmenubundle提供的twig模板，
```twig
{% extends 'knp_menu.html.twig' %}，
```

我们找到knp_menu.html.twig文件，

当前版本TEEBB的菜单功能并没有完整实现knpmenu的完整功能，后续版本我会完善。这里
`list block`是菜单列表的样式，我们需要修改，
`item block`是菜单项的样式，我们也需要修改，

复制`list block`，主菜单需要给ul标签添加 "navbar-nav" 样式类  

定义`item block`，复制主菜单的菜单项，先把父级菜单显示出来，  
再处理子级菜单，  
使用item.hasChildren判断当前菜单是否有子菜单。   
如果有子菜单，我们使用item.children 来获取子菜单，并循环输出就可以了。  

完成主菜单，刷新页面，主菜单已经成功显示了。  

现在处理二级菜单，同样的操作，二级菜单就很容易了，这里我直接复制已经修改好的模板文件。  

刷新页面，我们看到底部 Doctrine 图标有很多条查询，顶部的两个菜单一般不需要修改，我们可以将菜单加入缓存中。  

在`teebb_core`变量类里，有`hasCache`方法和`getCache`方法，  
getCache传入一个key做为缓存的key， $data是需要缓存的数据，  

我们使用菜单的别名做为`cacheKey`，  
在twig中做下判断如果有当前有缓存则直接从缓存中获取菜单，
如果没有菜单再获取菜单并将菜单存入缓存。

```twig
{% if teebb_core.hasCache('main_nav') %}
    {% set main_nav = teebb_core.cache('main_nav') %}
{% else %}
    {% set mainNavHtml = knp_menu_render('main_nav', {template: 'menu/main_nav.html.twig'}) %}
    {% set main_nav = teebb_core.cache('main_nav', mainNavHtml) %}
{% endif %}
```
同样的方法 来处理二级导航菜单。

刷新页面，数据库的查询还是很多，这是第一次查询需要将数据写入缓存。

使用`teebb_core`的`getCache`方法你可以将任意的数据存入缓存。

TEEBB系统的默认缓存时间是24小时，你可以在`teebb_core.yaml`配置文件里进行修改。
菜单系统有针对缓存的事件处理，如果你更新了菜单，缓存会自动失效。

现在，我们打开顶部的公告内容，页面打开了，但是很丑。

下一节，我们将内容的详情页面完成，你将会学到bundle中Twig模板的覆盖。
