Symfony 框架实战教程——第五天:KnpMenuBundle 创建菜单项+结合 Twitter Bootstrap3

Symfony 框架实战教程——第五天:KnpMenuBundle 创建菜单项+结合 Twitter Bootstrap3

Chris Yue 9 comments
Posts

昨天以及昨天(里)我们实现了用户功能,到目前为止,功能都挺好用的,就是界面实在不敢恭维,我都不好意思截图。所以今天我们还是来装修一下我们的界面。

做界面最好从交互比较多的页面入手,目前交互元素比较丰富的,有新闻列表页和新闻显示页。

为了开发快速,但也不会让界面太难看,我打算直接用 Twitter Boostrap3了。

如果是看过我前几期文章的同学一定能猜得到,我一定又要说:“是的,像 boostrap3 这种常用的前端开发框架,相应的 Bundle 也是有的”。没错!这次我向大家推荐 MopaBootstrapBundle,里面包含了使用 LESS/SASS 编译 Boostrap CSS,支持让 Symfony 框架以及常见 Bundle 比如我们的用过的 KnpPaginatorBundle 在模板渲染阶段自动使用 Bootstrap 的标签形式输出。还是会给大家的开发带来一定的方便。

当然,其他的选择也是有的,目前我们公司的项目采用的就是直接用 Bower 来安装项目需要的各种 js/css。

不过且慢,对于我们这种小应用来说,为了更快的开发速度,我打算采取直接引入第三方网站提供的 js 和 css 公共库镜像。最近我做一些小站都会采用这种方式,除了不用去维护这些静态文件,也能天然实现“JS/CSS 应 Cookie Free“这样的最佳实践。

目前H5的页面已经非常流行了,也为了我们能快速出页面,我想直接使用 HTML5 应该都没意见吧。好吧,说到这里……我又有要推荐的:HTML5 boilerplate。Boilerplate 就是样板文件的意思,不是直接用,而是给大家做参考用的。那么我们就把它复制到我们的模板中。

可能大家已经注意到了,在 app/Resources/views 目录里面有一个 base.html.twig。目前我们接触过的模板文件,开头第一句都是

{% extends 'base.html.twig' %}

大家都知道子类继承父类,都需要 extends 一下,这里是一个意思。

这里有些同学可能又注意到,有的模板文件在 base.html.twig 之前,会有两个冒号::,出现这种情况的模板文件应该都是使用 DoctrineBundle 的代码生成工具生成的。冒号在指定模板路径的时候,是有特殊含义的,这里我们先不用管它,因为 Symfony2.6 已经推荐忽略这两个冒号的写法。

好的,现在我们将 HTML5 Boilerplate 项目里 src 目录下的 index.html 里的代码拷贝到 app/Resources/views/base.html.twig,并做一些调整:

<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Symfony 新闻</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="{{ asset('/css/main.css') }}">
</head>
<body>
    <script src="{{ asset('/js/main.js') }}"></script>
</body>
</html>

因为教程只是为了演示目的,保持文章尽可能小的篇幅,我把一些浏览器检验,Google 统计代码之类的东西,都已经删除掉了。实际项目中,你最好每行代码都过一遍。

然后我们把 boostrap3 所需要的 js 和 css 加载进来,这里我们采用百度提供的公共代码库:

<head>
    ...
    <link rel="stylesheet" href="//apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.css">
</head>

<body>
    ...
    <script src="//apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="//apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
</body>

注意我写的镜像路径是 // 开头,这样写的好处是,如果你的网站是支持 HTTPS 访问,那么静态文件镜像地址也是 https 开头,防止浏览器出现“安全链接中包含不安全链接”的提示。当然,也得确认镜像提供网站是否支持 https 访问。

添加上我们的 Logo,感觉首页已经有点像模像样了呢:

<body>
    <nav class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#main-nav">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="{{ path('home') }}">Symfony News</a>
            </div>

            <div class="collapse navbar-collapse" id="main-nav">                    
            </div>
        </div>
    </nav>
</body>

接下来是添加导航栏里的菜单项:目前只有一项——新闻。我们可以按照 Boostrap3 的方式开始码代码,不过需要注意的是,如果当前正在新闻栏目的页面,新闻菜单还应该是选中的状态。先不要着急想如何解决这个问题,因为又有现成的 Bundle 实现了这个需求:KnpMenuBundle

$ composer require knplabs/knp-menu-bundle

注册Bundle:

// app/AppKernel.php

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Knp\Bundle\MenuBundle\KnpMenuBundle(),
        );
    }
}

创建菜单的类:

// src/AppBundle/Menu/Builder.php

namespace AppBundle\Menu;

use Knp\Menu\FactoryInterface;

class Builder
{
    public function navMenu(FactoryInterface $factory, array $options) 
    {
        $menu = $factory->createItem('root');
        $menu->setChildrenAttribute('class', 'nav navbar-nav');

        $menu->addChild('home', [
            'route' => 'home',
            'label' => '首页',
        ]); 

        $menu->addChild('news', [
            'route' => 'news',
            'label' => '新闻'
        ]);

        return $menu;
    }
}

最后在模板文件里使用 KnpMenuBundle 来渲染菜单:

{# app/Resources/views/base.html.twig #}
<nav class="navbar navbar-default navbar-fixed-top">
    <div class="container">
    ...
        <div class="collapse navbar-collapse" id="main-nav">
            {{ knp_menu_render('AppBundle:Builder:navMenu', {'currentClass': 'active'}) }}
        </div>
    </div>
</nav>

并在自定义 css 文件里添加如下样式:

<!-- web/css/main.css -->
body {
    margin-top: 70px;
}

这样我们就得到了一个崭新的导航条!注意,KnpMenu 已经帮我们自动处理好了当前菜单的选中状态:

menu

OK,让我们点进“新闻页面”,好吧,虽然比之前好了一些,但依然还有太多改进的空间:

{# app/Resources/views/news/index.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    <div class="row">
        <div class="col-sm-8">
            <p><a class="btn btn-primary" href="{{ path('news_new') }}">+ 发表新闻</a></p>
            {% for entity in pagination %}
                <section>
                    <header><h2><a href="{{ path('news_show', { 'id': entity.id }) }}">{{ entity.title }}</a></h2></header>
                    <p>{{ entity.body }}</p>
                </section>
            {% endfor %}
            {{ knp_pagination_render(pagination) }}
        </div>
    </div>
{% endblock %}

另外 base 模板依然需要保留 body block:

<body>
        ...
    </nav>    

    <div class="container">
        <div class="content">
            {% block body %}{% endblock %}
        </div>
    </div>

    <script ...
</body>

刷新页面看几看效果,页码显示还是太简陋,还好 KnpBundle 里自带了 bootstrap3 风格的模板,我们来设置使用它:

# app/config/config.yml
knp_paginator:
    template:
        pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig

虽然依然很简单,但是不是要比以前好看多了呢?

symfony screenshot news list

好吧,我突然觉得前端要写的代码还真多,所以还是分成两天来说好了

可能有的小伙伴改着改着 base.html.twig 的代码就乱了,这里我把最后的 base 模板都发上来

{# app/Resources/views/base.html.twig #}
<!doctype html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>symfony.cn</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="apple-touch-icon" href="apple-touch-icon.png">
        <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.min.css">
        <link rel="stylesheet" href="{{ asset('/css/main.css') }}">
    </head>
    <body>
        <!-- Add your site or application content here -->
        <nav class="navbar navbar-default navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#main-nav">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">Symfony News</a>
                </div>

                <div class="collapse navbar-collapse" id="main-nav">
                    {{ knp_menu_render('AppBundle:Builder:navMenu', {'currentClass': 'active'}) }}
                </div>
            </div>
        </nav>
        <div class="container">
            <div class="content">
                {% block body %}{% endblock %}
            </div>
        </div>
        <script src="//apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
        <script src="//cdn.bootcss.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
    </body>
</html>

第六天

Symfony 框架实战教程——第五天:KnpMenuBundle 创建菜单项+结合 Twitter Bootstrap3 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

写作累,服务器还越来越贵
求分担,祝愿好人一生平安
天使打赏人

9 Comments

txsing

五月 22, 2015 在 4:46 下午

{{ knp_menu_render('BloggerBlogBundle:Menu:IndexMenu:navMenu', {'currentClass': 'active'}) }}

An exception has been thrown during the rendering of a template (“The menu “BloggerBlogBundle:Menu:IndexMenu:navMenu” is not defined.”) in ::base.html.twig at line 27.

已经要崩溃了….
这个目录下明明就有这个文件啊!!!!!!!!! T-T

    Chris Yue

    五月 25, 2015 在 4:26 下午

    BloggerBlogBundle:Menu\IndexMenu:navMenu

    我也中过招,我只能说不能想当然

     

    天宇

    八月 23, 2016 在 11:13 上午

    按步骤做的,最后报错了。麻烦帮忙看下,谢谢!

    {{ knp_menu_render('AppBundle:Builder:navMenu', {'currentClass': 'active'}) }}

    An exception has been thrown during the rendering of a template (“Unable to generate a URL for the named route “home” as such route does not exist.”) in base.html.twig at line 28.
    500 Internal Server Error – Twig_Error_Runtime
    1 linked Exception:

    RouteNotFoundException »

     

    Chris Yue

    八月 25, 2016 在 10:57 下午

    错误信息为 “没有名为 home 的路由”,请你检查一下

     

ccna30

三月 30, 2015 在 9:55 下午

我的问题是崭新的导航条已经出现,但是点击新闻没有反应,依然是这个界面。

    ccna30

    三月 30, 2015 在 10:22 下午

    我在base.html.twig中定义了block,然后news/index.html.twig中利用这个block,新闻便可以显示了。

     

    Chris Yue

    三月 31, 2015 在 5:43 下午

    这个的确是怪我没写清除,已经补充必要步骤

     

ken

三月 26, 2015 在 10:19 下午

按照楼主的步骤做,出现了一些无法解决的新问题,楼主可以共享一下你的这个案例代码吗?可以发到邮箱吗?fuhuayishiyanyun@163.com 非常感谢,

    Chris Yue

    三月 27, 2015 在 9:36 下午

    我建议你还是在这里提出是什么问题吧,有可能是大家都会遇到的问题

     

发表评论

36 + = 40