In Part II we configured doctrine to connect to our database, created some managed entities out of plain old PHP objects using annotations and then used those annotations to let doctrine create our database tables for us. In this part, we will set up some routing, use controllers and learn about templating with Twig. We are going to be setting up some static pages to show how routing, controllers and templates work at a high level. In the next part we will start getting to some dynamic content and database interaction.
第二部分,我们配置Doctrine去连接我们的数据库,利用POPO的注释去创建受管实体类,然后使用这些注释去让Doctrine创建我们的数据表。在本部分,我们将设置一些路由、使用控制器,并学习twig模板。我们打算设置一些静态页来展现路由、控制器和模板是如何高水平工作的。在下一部分,我们将开始动态内容和数据库交互方面的内容。

Before we go further lets talk about testing. It just so happens that symfony2 has some fantastic testing support, so lets go ahead and try to be good, responsible programmers and take a test-driven development (TDD) approach to our blog application. To write tests for symfony2 you need to have PHPUnit 3.5.11 or greater installed on your system. Go ahead and install that if you haven’t already. I won’t go step-by-step through the installation here, but it is easy enough and you can find instructions here.
在我们进一步深入之前先讲讲测试。正巧,Symfony2对测试有着不错的支持,因此就让我们成为一名有责任心的好程序员,使用测试驱动开发模式来构建我们的博客应用。为了为Symfony2编写测试,您需要PHPUnit3.5.11或更高的版本。如果您还没安装,那么就去安装吧。在这里我不会一步步地演示安装,安装非常简单,您可以在这里找到演示。

Now that we have PHPUnit installed we can start writing some simple tests. For now we will only be testing two pages. In this part of the tutorial we are going to add a “home” page and an “about” page. So lets create some new test classes to accomplish this. We need to create two new classes which we will use to test our controllers. First we need to add a Controller directory in the src/Company/BlogBundle/Tests directory. In the Controller directory we just created, add a file called BlogControllerTest.php. Here is the code for the class:
现在我们已经安装了PHPUnit,我们可以开始写一些简单的测试程序。现在我们将只测试两个页面。在教程的这个部分,我们打算添加一个“home”页面和一个“about”页面。因此让我们创建一些新的测试类来实现它。我们需要创建两个新类用来测试我们的控制器。首先我们需要在src/Company/BlogBundle/Tests目录中添加一个Controller目录,在我们刚刚创建的Controller目录中,添加一个名为BlogControlleTest.php的文件。以下是该类的代码:

  1. namespace Company\BlogBundle\Tests\Controller;  
  2.    
  3. use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;  
  4.    
  5. class BlogControllerTest extends WebTestCase  
  6. {  
  7.     public function testIndex()  
  8.     {  
  9.         $client = $this->createClient();  
  10.    
  11.         $crawler = $client->request('GET''/');  
  12.    
  13.         $this->assertTrue($client->getResponse()->getStatusCode() == '200' );  
  14.    
  15.         $this->assertTrue($crawler->filter('title:contains("Home")')->count() > 0);  
  16.    
  17.         $this->assertTrue($crawler->filter('h2:contains("Welcome to the Blog")')->count() > 0);  
  18.     }  
  19. }

Lets go through this class. As usual we declare our namespace which matches our directory structure. Then we declare that we are going to use the WebTestCase class. This class is provided by symfony2 to be the base class for your test classes. The method name testIndex indicates that we want to test the index action of our BlogController class, which is what we will map to the home page in the routing shortly. We will be digging deeper into testing in future parts of this tutorial, so I will not get deeply into everything that is being done here. Just know that in this method we are sending a GET request to the route matching the ‘/’ pattern. Then we are taking the response generated and testing it to verify that it was a success, that the title element contains the string “Home” and that there is an h2 element on the page that contains the string “Welcome to the Blog”.
我们过一遍这个类。通常我们声明的命名空间是与我们目录结构相匹配的。然后我们声明我们打算使用WebTestCase类。这个类由symfony2提供,是您测试类的基类。TestIndex方法是我们想要测试BlogController类的index动作,我们将会在路由里将它映射到home页。因为我们打算在这个教程的后续部分对测试做深入讨论,所以我对这里所做的就不一一深入介绍了。您只需要知道在该方法中我们发送了一个GET请求,以便让路由去匹配‘/’条目。然后我们将生成响应并且测试它是否成功,title元素包含“Home”字符串,并且页面上的h2元素也包含了“Welcome to The Blog”字符串。

That is it for the “home” page test. Now lets create a test for the “about” page. I personally prefer to create a controller that handles all of my static pages. This way I just need one route to handle all of them. We will see how this works very shortly. For now lets create another test class file in the src/Company/BlogBundle/Tests/Controller directory named PagesControllerTest.php. This class should look familiar.
这是“home”页面的测试。现在我们再创建一个“about”页面的测试。我个人比较喜欢创建一个控制器去处理所有的静态页面。这样我只需要一个路由就可以处理所有的东西。很快我们就可以知道它是如何实现的。现在让我们在src/Company/BlogBundle/Tests/Controller 目录中创建一个名叫PagesControllerTest.php的文件。该类看起来应该很熟悉。

  1. namespace Company\BlogBundle\Tests\Controller; 
  2.   
  3. use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; 
  4.   
  5. class PagesControllerTest extends WebTestCase 
  6.     public function testShow() 
  7.     { 
  8.         $client = $this->createClient(); 
  9.   
  10.         $crawler = $client->request('GET''/about'); 
  11.   
  12.         $this->assertTrue($client->getResponse()->getStatusCode() == '200' ); 
  13.   
  14.         $this->assertTrue($crawler->filter('title:contains("About")')->count() > 0); 
  15.   
  16.         $this->assertTrue($crawler->filter('h2:contains("About")')->count() > 0); 
  17.     } 

Here we are running a similar test to the one we just completed. The testShow method will test the show action of the PagesController. We will create this controller in our bundle shortly. The PagesController will handle all of our requests to pages with static content. In this test we are sending a GET request to the route matching ‘/about’ and verifying that the response was a success, that the title element that contains the string “About” and that it also has a h2 element that contains the string “About.” These tests are very simple, but are fine for now. We will be adding more complex tests, once we start to add more complex functionality to our blog.
在这里我们运行了一个与上一个非常相似的测试。testShow方法将测试PagesController的show动作。稍后我们将在我们的Bundle中创建该控制器。PagesController会处理所有到静态内容页面的请求。在这个测试里我们将一个GET请求发送到匹配‘/about’的路由,并确认响应是否成功、title元素是否包含了“About”字符串并且h2元素也包含“About”字符串。这些测试非常简单,但对于现在而言却非常好。当我们向我们的博客中添加更为复杂的功能时,我们将会相应添加更为复杂的测试。

Running the tests is very simple as symfony2 has already provided a default PHPUnit xml config file in the app directory. Just open up a terminal and run the following command from your base project directory:
运行测试非常简单,symfony2在其app目录里已经提供了一个默认的PHPUnit的xml.conf文件。只需打开终端,并在项目根目录里运行以下的命令:

  1. phpunit -c app/

You should see the that we have 2 tests and 6 assertions and they all fail. Now we need to write code to make those tests pass. This is the core concept of TDD. Write tests that fail, then implement the functionality to make them pass. After you have done this you can safely refactor code and implement new features and be confident that your old code has not broken. The initial investment of writing tests up front will pay off the larger your application grows in size.
您将看到我们有两个测试和6个断点,它们都失败了。现在我们需要编写代码来使它们通过。这是TDD的核心理念。编写测试,失败了就实现功能使其通过。在您这样做之后,您可以安全的重构代码、实现新功能,并确保不会破坏您的旧代码。在前期编写测试所花费的投入在您应用程序得到较大增长时显得十分值得。

The first thing we need to do to get our tests passing is to set up the routing. Routing is one of the most importants parts of your application. If your routing is incorrect or missing then the symfony2 framework would never be able to translate a request from a client into a bundle controller action. This is the key functionality that the routing component provides. It parses out a request and matches that against your routing configuration, then it uses the route that it matches and invokes your controller and the corresponding action. Then your action returns a Response object. The symfony2 framework then takes this Response object and outputs it to the client.
要想使测试通过的话,我们要做的第一件事就是设置路由。路由是你程序中最重要的部分之一。如果你的路由不正确或错过的话,那么Symfony2框架将永远不能将你的请求从客户端传递到Bundle的控制器动作中。这是路由组件所提供的关键功能。它解析请求并匹配你的路由配置,然后执行你的控制器中相应的动作。然后该动作会返回一个Response对象。Symfony2框架会将这个Response对象输出到客户端。

Lets get started by opening up the routing.yml file in the app/config directory. This is the main routing configuration file for the application. When you open this file you will see the following:
让我们从app/config目录中的routing.yml文件开始。这是应用程序的主路由配置文件。当你打开这个文件,你将会看到下列语句:

  1. Blog: 
  2.     resource: "@BlogBundle/Resources/config/routing.yml" 

Here we have imported the routing.yml file located in our BlogBundle. The resource  simply appends all the routes for the BlogBundle to the app routing configuration. This is a simple way to add routes defined in the various bundles you will use to your application without having to rewrite them or copy and paste anything. It is very simple and efficient. So now that we have learned how to import routes from your bundle, lets take a look at our BlogBundle routing.yml. Open up the routing.yml file located in the src/Company/BlogBundle/Resources/config directory. Remove any existing routes that were auto-generated. Our goal is to get the tests to pass that we wrote earlier. We need to add routes to match the patterns ‘/’ and ‘/about’.  Here are those routes:
在这里,我们已经导入了BlogBundle中的routing.yml文件。resource可以很容易地将BlogBundle的所有路由都添加到配置文件里。这是一个简单的方式,可以很方便地添加定义在各个Bundle中的路由,您可以在您的应用程序中使用,而无须去重新编 写或拷贝粘贴它,它非常简单高效。我们已经学会了如何从您的Bundles中导入路由,现在站我们看一下BlogBundle的routing.yml。打开 src/Company/BlogBundle/Resources/config目录中的routing.yml文件。删除所有自动生成的路由。我们的目标是让我们先前写的测试通过。我们需要添加匹配‘/’和‘/about’的路由。以下是这些路由:

  1. show_page: 
  2.     pattern:  /{page} 
  3.     defaults: { _controller: BlogBundle:Pages:show } 
  4.  
  5. homepage: 
  6.     pattern:  / 
  7.     defaults: { _controller: BlogBundle:Blog:index } 

We have defined two routes. One named show_page and one named homepage. Each route that you define has a pattern that the symfony2 routing component tries to match. A route also has has the defaults parameter. Here you define the special _controller parameter which tells symfony2 what controller and action to map this route to as well as default values for any custom parameters you put in your route. The show_page route has a _controller value of BlogBundle:Pages:show. The symfony2 framework will take this string and determine that it should look in the BlogBundle for the PagesController and call the show action. Not so hard, right?
我们有了两个路由。一个名为show_page,另一个名为homepage。每一条你所定义的路由都有一个patten,是Symfony2路由组件尝试匹配的。路由还有一个defaults参数。在这里你定义了一个特定的_controller参数,该参数告诉Symfony2哪个控制器和动作被映射到这个路由以及在您路由中您自定义参数的缺省值。Show_page路由有一个_controller值是BlogBundle:Pages:show。Symfony2框架将使用该字符串在BlogBundle中查找PagesController控制器,并调用show动作。这不是很难,对吧?

In the show_page route we have defined a custom parameter named page. Any word wrapped in brackets will be transformed into a variable for your controller to use. In our example, our controller will have access to a variable named $page which holds the value of the string after the ‘/’ character. One important thing to remember is that the order in which your routes are defined matters. The symfony2 routing framework will choose the first route that it finds which matches the requirements. So that means that if we had a route with the pattern ‘/contact’ declared before the route with pattern ‘/{page}’ then the request would map to the ‘/contact’ route. If the ‘/contact’ route was declared after the ‘/{page}’ route, the a request with ‘/contact’ would match the ‘/{page}’ route. Easy enough right? There are other parameters and requirements that you can put on routes, such as the HTTP verb, but we have covered what you need to get started. We will be diving much deeper into the routing system in future parts when we start to work with the database and our entities. Hopefully you can see how the show_page route as we have defined it is very helpful in rendering static pages in a generic way. If you don’t, then don’t worry it will be made very clear in just a moment.
在show_page路由中,我们定义了一个自定义参数page。在大括号中的任何单词将被转换成您控制器中的变量来使用。在我们的例子中,控制器将会访问一个名叫$page的变量,该变量有着‘/’后面的字符串值。一个重要的事情就是必须记住在您定义路由的顺序问题。Symfony2路由框架将会选择它所找到的第一条满足要求的路由。这就意味着如果我们有一条‘/contact’路由声明在‘/{page}’路由之前,那么请求将映射‘/contact’路由。如果‘/contact’路由声明在‘/{page}’路由之后,那么先前匹配‘/contact’的请求将会匹配‘/{page}’路由。非常简单,不是吗?这里您还可以添加其他的参数和要求到路由里,比如HTTP动词,但我们已经介绍了您入门所需的内容。当我们开始使用数据库和我们的实体工作时,我们将会在后面部分深入学习路由系统。幸运的是你可以看到在一般情况下,我们所定义的show_page路由对渲染静态页面是非常有帮助的。如果您还不太清楚,别担心,稍后它将会变得非常清晰。

Now that we have our routing setup we are on our way to making our tests go from red to green. Next we need to create our controllers and actions. Navigate to the src/Company/BlogBundle/Controller directory and open the BlogController.php file. Make your file look like this:
现在我们已经有了我们的路由配置,这使得我们的测试道路上的红灯变成绿灯。接下来我们需要创建控制器和动作。打开src/Company/BlogBundle/Controller 目录中的BlogController.php文件。让您的文件看上去如下所示:

  1. // BlogController.php 
  2.   
  3. namespace Company\BlogBundle\Controller; 
  4.   
  5. use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
  6.   
  7. class BlogController extends Controller 
  8.     public function indexAction() 
  9.     { 
  10.         return $this->render('BlogBundle:Blog:index.html.twig'); 
  11.     } 

In our BlogController class we are defining an indexAction method. If you take a look back at the homepage route that we defined we declared that the route should map to the indexAction in the BlogController of the BlogBundle. This is exactly what we have implemented here. The action in the controller should return a Response object and that is exactly what the render method inherited from Symfony\Bundle\FrameworkBundle\Controller\Controller does. The render method takes as a parameter the name of the template to render. Lets translate the string BlogBundle:Blog:index.html.twig. Here we are telling the controller to render the template in the src/Company/BlogBundle/Resources/views/Blog folder named index.html.twig.
在BlogController类中我们定义了一个indexAction方法。如果您回顾一下我们定义的homepage路由,您将会发现我们声明该路由是映射到BlogBundle中BlogController控制器的indexAction方法的。这正是我们在这里实现的。控制器中的动作会返回一个Response对象,那是从Symfony\Bundle\FrameworkBundle\Controller\Controller 继承过来的render方法。render方法将模板名作为参数去渲染。让我们翻译一下BlogBundle:Blog:index.html.twig字符串 。在这里我们告诉控制器去渲染src/Company/BlogBundle/Resources/views/Blog文件夹中名为index.html.twig的模板。

Next we need to implement our PagesController. Create a new file named PagesController.php in the src/Company/BlogBundle/Controller directory. Here is the source for this file:
接下来我们需要实现我们的PagesController。在src/Company/BlogBundle/Controller 目录中创建一个名为PagesController.php的新文件。下面是该文件的源代码:

  1. // PagesController.php 
  2.   
  3. namespace Company\BlogBundle\Controller; 
  4.   
  5. use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
  6. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 
  7.   
  8. class PagesController extends Controller 
  9.     public function showAction($page
  10.     { 
  11.         $template = sprintf('BlogBundle:Pages:%s.html.twig'$page); 
  12.   
  13.         if (!$this->get('templating')->exists($template)) { 
  14.             throw new NotFoundHttpException("The specified page could not be found."); 
  15.         } 
  16.         return $this->render($template); 
  17.     } 

This is almost exactly like our BlogController except for two things. Instead of indexAction we have the showAction. Remember that page parameter in our route that we wanted to use in our controller? To use it in our action we simply declare it as a parameter to the action method. The symfony2 framework will transform the string in the route to a variable named $page. This action is set up to render a static page based on the value of the $page parameter. All we do in this action is render the template with the page value inserted into the template name string. Now to create new static pages all we have to do is create a template file with the appropriate name and put it in our src/Company/BlogBundle/Resources/views/Pages folder. That’s it. We don’t even have to modify the routing. Pretty cool, huh? You don’t have to handle your static pages this way, but I find this makes it easy.
除了两点之外,上述代码和我们的BlogController几乎完全一样。我们用showAction替代了indexAction。还记得我们想在控制器中使用的那个路由page参数吗?要在我们的动作中使用它,只需要简单地将它声明为动作方法的参数。设置该动作是为了渲染一个基于$page参数的静态页。我们在该动作中所做的就是将带有page值的模板插入到模板名字符串中。现在要创建新的静态页,我们所有要做的就是创建一个适当 的模板,并将其放在src/Company/BlogBundle/Resources/views/Pages文件夹中。就这样,我们甚至不需要去修改路由。很酷吧?这样您就不必处理去您的静态页,但我发现这样可以很容易。

Now that we have our routing and controllers set up, all that is left is to tackle some templating with Twig. Twig is a new templating language that is packaged with symfony2. You are not required to use Twig. You can use regular old PHP templates if you would like, but I am going to use Twig in this tutorial. Twig is powerful and allows for template inheritance, filters and much more. If you want to read about why Twig is so awesome then check out this page.
现在我们完成了路由和控制器的设置,剩下的就是使用twig来制作模板了。Twig是symfony2自带的一种新的模板语言。您并没有被要求使用Twig。如果您喜欢的话您可以使用常规的PHP模板,但是在本教程里我还是打算使用Twig。Twig很强大并支持模板继承、过滤等诸多功能。如果您想了解为什么Twig这么棒,请查阅这里

Twig supports template inheritance. This allows templates to inherit from one another much like an object inherits functionality from its parent classes. In this tutorial I am going to use a three-level inheritance structure. We will have a base.html.twig. This file will contain the html, head and body elements. It will also contain any javascripts and style sheets that we want every page in our application to include. We will also have a layout.html.twig file where we will keep things like the main header, navigation and footer for the application. We will use Twig blocks to allow our other templates to include their content into these. If you are coming from symfony1 then you can think of a block as a slot. We will see an example of a block shortly.
Twig支持模板继承。这就允许模板可以象对象继承其父类一样,从其它模板中继承。在本教程中我打算使用三层继承结构。我们有一个base.html.twig。该文件包含html、head和body元素。它还包含了一些在我们应用程序中的每一页都需要包含的JavaScript和样式表。我们还将有一个layout.html.twig文件,该文件保存着应用程序主要的页头、导航和页脚。我们还将使用Twig的区块(Block),让我们其它的模板将其内容包含进来。如果您使用过symfony1,那么您可以将一个区块(Block)看成一个插槽(slot)。稍后我们将看到一个关于区块(Block)的例子。

The base.html.twig file is located in the app/views folder [Update: A change has been made to the framework after I wrote this part. The base.html.twig file should now reside in the app/Resources/views folder]. Open up the base.html.twig file and change your copy to match the following:
Base.html.twig文件放在app/views文件夹中[更新:在我写本部分之后,框架有了改变。base.html.twig文件现在放置在app/Resources/views文件夹中]。打开base.html.twig文件,将您的代码改成下面情况:

  1. <!DOCTYPE html> 
  2. <html> 
  3.     <head> 
  4.         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  5.         <title>{% block title %}symfony2 Blog Tutorial{% endblock %}</title> 
  6.         <link rel="shortcut icon" href="/favicon.ico" /> 
  7.         <!--[if lt IE 9]> 
  8.         <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> 
  9.         <![endif]--> 
  10.         <link rel="stylesheet" type="text/css" href="{{ asset('css/reset.css') }}" media="screen" /> 
  11.         {% block head %}{% endblock %} 
  12.     </head> 
  13.     <body> 
  14.         {% block body %}{% endblock %} 
  15.     </body> 
  16. </html> 

Lets inspect this file in detail. We are going to use html5 so we have declared an html5 doctype. In our head section we have pretty much the standard stuff. We include an html5 shiv if the browser is Internet Explorer and the version number is less than 9. Now if we take a look at the title element, we can see a block declaration. Here we have a block with the name of title. The content between the block declaration is the content that will be included in the page if no child template fills the block with its own content. We also have blocks named head and body. The head block is there to allow any child of this template to include head content that it may need to include. The body block should not need any explanation.
让我们仔细检查一下这个文件。因为我们打算使用html5,因此我们声明了一个html5的doctype。我们的head部分是非常标准的。如果浏览器是IE并且它的版本小于9的话,我们包含了一个html5的shiv。现在如果我们看一下title元素,我们可以看到一个区块的声明。在这里,我们有一个名为title的区块。如果没有子模板用它自己的内容去装填区块内容的话,那么页面将包含在区块声明之间的内容。我们还有名为head和body的区块。Head区块允许该模板的任意子模板包含他们所需的head内容。Body 区块就不多做解释了。

We also include a reset stylesheet in our head section. You can download the reset stylesheet I am using here. Place this file in the web/css folder. In Twig, to output a value you use the {{ … }} syntax. You can think of this as the PHP echo function. Using the asset function to output your asset urls will make your application more portable. Lets move on to the layout.html.twig file located in the src/Company/BlogBundle/Resources/views folder. Open this file and change yours to look like the following:
我们还在我们的head部分包含了一个reset样式表。您可以在这里下载我所使用的reset样式表。将该文件放到web/css文件夹中。在Twig里您可以使用{{……}}语法来输出值。您可以把它看做PHP的echo函数。使用asset函数输出您的URL,可以使您的应用程序移植性更好。让我们再看看src/Company/BlogBundle/Resources/views 文件夹下的layout.html.twig文件。打开该文件并将您的代码如下所示:

  1. {% extends "::base.html.twig" %} 
  2.   
  3. {% block head %} 
  4.     <link rel="stylesheet" type="text/css" href="{{ asset('bundles/blog/css/blog.css') }}" media="screen" /> 
  5. {% endblock %} 
  6.   
  7. {% block body %} 
  8.     <div id="container"> 
  9.         <header class="clearfix"> 
  10.             <h1> 
  11.                 symfony2 Blog Tutorial 
  12.             </h1> 
  13.             <nav> 
  14.                 <ul> 
  15.                     <li> 
  16.                         <a href="{{ path('show_page', { 'page' : 'about' }) }}"> 
  17.                             About 
  18.                         </a> 
  19.                     </li> 
  20.                 </ul> 
  21.             </nav> 
  22.         </header> 
  23.         <div id="content"> 
  24.             {% block content %}No content.{% endblock %} 
  25.         </div> 
  26.         <footer class="clearfix"> 
  27.             <p class="symfony2"> 
  28.                 Powered by <a href="http://www.symfony.com" target="_blank">symfony2</a> 
  29.             </p> 
  30.             <p class="copy"> 
  31.                 &copy; Company 2011 
  32.             </p> 
  33.         </footer> 
  34.     </div> 
  35. {% endblock %} 

As you can see, in the layout.html.twig the first thing we do is extend the base.html.twig file. Also, we have made use of the blocks that we setup in the base.html.twig file. We have included another stylesheet which you can download here, but this time we have included it from the bundle resources instead of the app resources. Many bundles that you use will package resources along with them. Luckily, symfony2 provides us with a console command that will publish these assets into our web folder. After you put the blog.css file in the src/Company/BlogBundle/Resources/public/css folder, open a terminal and navigate to your base project directory. Then run the following command:
正如你所看到的那样,layout.html.twig做的第一件事就是继承base.html.twig文件。另外,我们使用了我们在base.html.twig设置的区块。我们还包含了另一个样式表,您可以从这里下载,但这次我们是从Bundle的Resources而非从app的Resources中包含的。您所用的很多Bundle都是和Resources一起单独打包的。幸运的是,symfony2给我们提供了一个控制台命令行可以将这些资源发布到我们的web文件夹中。在您将blog.css文件放到src/Company/BlogBundle/Resources/public/css 文件夹后,打开终端,并进入您的项目根目录下。然后运行以下命令:

  1. php app/console assets:install web

This will copy all of the assets in the public folder of your bundles to the application’s web directory so you can include them in your templates. Next we define our body content. The only new thing in this section is the Twig path function. This returns a url to the route that you specify. This is similar to the symfony1 url_for function.
这样你Bundle公共文件夹中所有资源将被全部复制到应用程序的web文件夹里,以便您可以在您的模板中包含它们。接下来定义我们的body内容。在这部分,唯一新的就是Twig的path函数。该函数将URL返回到您指定的路由。这与Symfony1中的url_for函数类似。

Now all we have to do is create our index.html.twig and about.html.twig. The index.html.twig file should be placed in the src/Company/BlogBundle/Resources/views/Blog folder and should look like this:
现在我们要做的是创建我们的index.html.twig和about.html.twig。index.html.twig文件将放在src/Company/BlogBundle/Resources/views/Blog 文件夹中,其代码如下所示:

  1. {% extends "BlogBundle::layout.html.twig" %} 
  2.   
  3. {% block title %} 
  4.     symfony2 Blog Tutorial | Home 
  5. {% endblock %} 
  6.   
  7. {% block content %} 
  8.     <h2>Welcome to the Blog!</h2> 
  9. {% endblock %} 

Knowing what you now know about Twig, you should have no problem understanding this template. All that is left is to create the about.html.twig template and put it in the src/Company/BlogBundle/Resources/views/Pages folder. It should look look like this:
在了解了您现在所需知道的有关Twig内容,理解上述模板应该没什么问题。剩下的就是创建about.html.twig文件,并将其放置在src/Company/BlogBundle/Resources/views/Pages 文件夹中,如下所示:

  1. {% extends "BlogBundle::layout.html.twig" %} 
  2.   
  3. {% block title %} 
  4.     symfony2 Blog Tutorial | About 
  5. {% endblock %} 
  6.   
  7. {% block content %} 
  8.     <h2>About</h2> 
  9. {% endblock %} 

When you look at these two files remember back to the tests we wrote and that our goal was to get these tests to pass. These templates are obviously very simple, but don’t worry we will be making them look a lot better and more complicated in the upcoming tutorials.
当您看到这两个文件,并回想起我们的目标是要通过我们所编写的测试时,这些模板显然非常简单,但别担心,在后面的教程中我们将使它们看起来更好、更复杂。

If you have followed along exactly, your home page and about page should look like the following screenshots.
如果您严格遵循上述步骤,您的首页和关于页将如下图所示。

symfony2 Blog Tutorial Home Page

symfony2 Blog Tutorial Home Page (Click for full size)
Symfony2博客教程首页

symfony2 Blog Tutorial - About Page

symfony2 Blog Tutorial About Page (Click for full size)
Symfony2博客教程关于页

Pretty boring site so far, huh? Don’t worry we are on our way to a cool symfony2 powered blog! We finally have all the code in place to re-run our tests and now they should be passing. Success!
呵呵,到目前为止网站相当无聊?别担心,按照我们的方式我们将得到一个很酷、很强大的Symfony2博客!最终我们所有的代码都就位了,重新运行我们的测试,现在测试应该通过了。成功!

This has been a much longer post than I had originally intended, but we learned a lot about routing, controllers and templating. We even managed to squeeze some testing in too. But alas, we have only scraped the surface of each of these topics. Next time we will start creating some pages with dynamic content and work with the database and doctrine2. I may also revisit the routing a little and include some of the Controller annotations available in the FrameworkExtraBundle. Doing these things means that we will most definitely be diving further into routing, controllers and templates. These posts take hours to prepare, so I apologize if some time passes before I get the next one posted. I really didn’t know it would be this much work to write all this out! So hang in there with me! Until next time…
本部分花费了比我原来设想更长得多的篇幅,但我们学到了许多关于路由、控制器和模板的知识。我们甚至设法挤进了一些测试的内容。但可惜的是,我们只是很肤浅地对各个主题有了一些表面的了解。下一次我们将开始创建一些与数据库和Doctrine一起工作的动态页面。我也许还会小小地重新审视路由并包含FrameworkExtraBundle中可用的一些控制器注释。做这些事意味着我肯定要更加深入了解路由、控制器和模板。这些文章需要很长时间去准备,所以在我写出下一篇文章之前,如果过去了很长的时间,我道歉。我真的不知道将所有的这些写出来要做如此多的工作!所以同我一起等待吧!直到下一次...