创建你的第一个Symfony页面


创建一个新页面 - 无论是HTML还是JSON输出 - 都是一个简单的“两步”操作:

  1. 创建一个路由:路由(route)是一个指向你的页面URL(比如/about),同时映射到一个控制器。

  2. 创建一个控制器:控制器(controller)是你为了构造页面而写的功能。你要拿到发送来的请求请求信息,用它创建一个Symfony的Response对象,令其包含HTML内容,JSON字符串或是其他

如同在网上每一次互动都是从HTTP请求开始,你的任务单纯而简单:理解请求,返回回复。

创建一个页面:路由和控制器 

在开始之前,确保你已经阅读了安装和配置Symfony章节,同时已经可以访问浏览器中的Symfony程序。

假设你要新建一个/lucky/number页面,用于生成一个随机的幸运数字并且输出它。那么,要先创建一个类并添加方法,用于在某人访问/lucky/number时被执行:

// src/AppBundle/Controller/LuckyController.phpnamespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Component\HttpFoundation\Response; class LuckyController{
    /**
     * @Route("/lucky/number")
     */
    public function numberAction()
    {
        $number = rand(0, 100);         return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }}


在开始之前测试一下

http:// localhost:8000 / lucky / number

如果你在Apache或Nginx上正确设置了虚拟主机,就可以把http://localhost:8000部分替换为你自己的主机名,比如http://symfony.dev/app_dev.php/lucky/number

如果你看到一个幸运数字被输出,那么恭喜!不过在玩转乐透之前,先要了解它是如何工作的。

numberAction上面@Route被称为注释,它定义了URL匹配。你也可以在YAML(或其他格式)里写路由:请参考路由章节。实际上,文档中的多数路由例程都有“标签”,以显示每种格式的配置

注释下面的方法 - numberAction- 被称为控制器,这是你控制页面的地方。这个唯一的原则是:一个控制器必须返回一个Symfony的响应对象(你最终将学习到活用这个原则)。

创建一个JSON响应 

在控制器中返回的Response对象可以包含HTML,JSON甚至二进制文件比如图片或PDF。你可以轻松设置HTTP头信息或HTTP状态码。

假设你要创建一个JSON返回值,则只需要LuckyController再添加一个方法:

  // src/AppBundle/Controller/LuckyController.php // ...class LuckyController{
    // ...     /**
     * @Route("/api/lucky/number")
     */
    public function apiNumberAction()
    {
        $data = array(
            'lucky_number' => rand(0, 100),
        );         return new Response(
            json_encode($data),
            200,
            array('Content-Type' => 'application/json')
        );
    }}

   

在浏览器中测一下

http:// localhost:8000 / api / lucky / number

你更可将代码精简为超好用的JsonResponse

// src/AppBundle/Controller/LuckyController.php // ...// --> don't forget this new use statement 别忘了这行新的use声明use Symfony\Component\HttpFoundation\JsonResponse; class LuckyController{
    // ...     /**
     * @Route("/api/lucky/number")
     */
    public function apiNumberAction()
    {
        $data = array(
            'lucky_number' => rand(0, 100),
        );         // calls json_encode and sets the Content-Type header
        // 自动调用json_encode并设置Content-Type头
        return new JsonResponse($data);
    }}


动态URL匹配:/ lucky / number / {count} 

窝,你做得不错但Symfony的路由还可以做得更多假设你希望用户可以到/lucky/number/5来立即生成幸运数字5,更新路由,在使其拥有尾部一个{wildcard}通配符:

注释:// src/AppBundle/Controller/LuckyController.php // ...class LuckyController{
    /**
     * @Route("/lucky/number/{count}")
     */
    public function numberAction()
    {
        // ...
    }     // ...}
YAML:# app/config/routing.ymllucky_number:
    path:     /lucky/number/{count}
    defaults: { _controller: AppBundle:Lucky:number }
XML:<!-- app/config/routing.xml --><?xml version="1.0" encoding="UTF-8" ?><routes xmlns="http://symfony.com/schema/routing"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/routing        http://symfony.com/schema/routing/routing-1.0.xsd">     <route id="lucky_number" path="/lucky/number/{count}">
        <default key="_controller">AppBundle:Lucky:number</default>
    </route></routes>
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('lucky_number', new Route('/lucky/number/{count}', array(
    '_controller' => 'AppBundle:Lucky:number',))); return $collection;

因为{count}占位符,页面URL变得不一样了。现在它要求URL匹配/lucky/number/*,比如/lucky/number/5。这样一来你可以在控制器中收到并使用这个值:

// src/AppBundle/Controller/LuckyController.php// ... class LuckyController{     /**
     * @Route("/lucky/number/{count}")
     */
    public function numberAction($count)
    {
        $numbers = array();
        for ($i = 0; $i < $count; $i++) {
            $numbers[] = rand(0, 100);
        }
        $numbersList = implode(', ', $numbers);         return new Response(
            '<html><body>Lucky numbers: '.$numbersList.'</body></html>'
        );
    }     // ...}

/lucky/number/xx测试一下,把xx换为任意数字:

http:// localhost:8000 / api / lucky / number / 7

你应该看到幸运数字7被输出!在控制器中增加一个$placeholder参数,你就可以得到路由中任何一个{placeholder}占位符的值只要确保它们的名字相同即可。

系统-路由还可以做更多,像是支持多个占位符(比如/blog/{category}/{page}),令占位符可选以及强制占位符匹配一个正则表达式(比如{count}必须为数字)。

路线章节寻找更多,即可成为路由专家。

渲染模板(利用容器) 

如果你在控制器中返回HTML,你可能需要渲染模板。幸运的是,Symfony拥有Twig:一种易学的模板语言,强大却相当有趣。

目前,LuckyController没有继承任何基类。此时引用Twig(或其他Symfony工具)最简单的方式,就是继承Symfony的Controller基类:

// src/AppBundle/Controller/LuckyController.php  
// ...
// --> add this new use statement
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
class LuckyController extends Controller
{    // ...}

使用模板服务 

这些并不改变任何东西,但是把你带入Symfony的容器/容器:这是一个数组模样的对象,允许你从中取出系统级的每一个有用对象这些有用的对象被称为服务(服务) ,其中Symfony送出了一个专门用于渲染模板的服务对象,还有一个用来记录日志的,以及许多许多。

渲染Twig模板,要求到的服务名为templating

// src/AppBundle/Controller/LuckyController.php // ...class LuckyController extends Controller{
    /**
     * @Route("/lucky/number/{count}")
     */
    public function numberAction($count)
    {
        // ...
        $numbersList = implode(', ', $numbers);         $html = $this->container->get('templating')->render(
            'lucky/number.html.twig',
            array('luckyNumberList' => $numbersList)
        );         return new Response($html);
    }     // ...}


“服务容器”十分重要,学习过程中你会掌握大量相关知识。现在,你只需了解它拥有很多对象,然后你可以通过“昵称”(比如templatinglogger)利用get()来得到其中的任何一个。templating服务,是TwigEngine的实例,里面有个render()方法。

但是还可以再简单些!通过继承Controller基类,你可以使用很多快捷方法,比如render()

// src/AppBundle/Controller/LuckyController.php // .../**
 * @Route("/lucky/number/{count}")
 */public function numberAction($count){
    // ...     /*
    $html = $this->container->get('templating')->render(
        'lucky/number.html.twig',
        array('luckyNumberList' => $numbersList)
    );
 
    return new Response($html);
    */     // render: a shortcut that does the same as above 快捷方法
    return $this->render(
        'lucky/number.html.twig',
        array('luckyNumberList' => $numbersList)
        );
  }

关于控制器中的快捷方法是如何运作的请参阅控制器章节。

高级用户请参考如何将控制器定义为服务

创建模板 

如果你现在刷新页面,会得到一个报错:

无法找到模板“lucky / number.html.twig”

修复它要靠创建一个新的app/Resources/views/lucky目录,再将number.html.twig置于其中:

枝条:{# app/Resources/views/lucky/number.html.twig #}
     {% extends 'base.html.twig' %} 
     {% block body %} <h1>Lucky Numbers: {{ luckyNumberList }}</h1>
    {% endblock %}
PHP:<!-- app/Resources/views/lucky/number.html.php -->
<?php $view->extend('base.html.php') ?> 
<?php $view['slots']->start('body') ?>   
 <h1>Lucky Numbers: 
<?php echo $view->escape($luckyNumberList) ?>
<?php $view['slots']->stop() ?>

Twig欢迎你!这个简单的文件已经展示了基本功能:像是{{ variableName }}语法被用于打印一些东西。而luckyNumberList这个变量是你在控制器中的render()方法中将其传递进来。

% extends 'base.html.twig' %对应的是一个布局文件,它们位于app / Resources / views / base.html.twig,这个文件随Symfony项目的建立而存在。它相当地单纯(只是一个未经样式处理的HTML结构),供你定制。{% block body %}部分则是使用了嫩枝模板的继承系统/继承系统,将来的其间置于内容父模板base.html.twig布局中的相应位置。

立即刷新页面

http:// localhost:8000 / lucky / number / 9

如果你查看页面源代码,你会看到完整的HTML骨架,多亏了base.html.twig

这只是Twig威力的极小部分。若你希望精通Twig的语法,数组循环,输出其他模板乃至更多超酷功能,应该参阅创建并使用模板

浏览整个项目 

你已经创建了一个弹性的URL,渲染了一个“使用了继承功能”的模板,并且输出了JSON响应。

是时候浏览你的项目中的文件并且去掉它们的神秘光环了。之前你已经在两个极为重要的文件夹中进行作业:

app/

内容配置文件和模板。大体上,只要不是 PHP代码的材料都放在这里。


src/

你的PHP程序之所在99%的时间你都会工作在src/(PHP文件)或app/(其他东东)之下。随着你的技术实力高大上起来,你会学习到每个文件夹下都发生了什么。

app/目录也存在其他内容,app/AppKernel.php像是,你要用它来开启新捆绑(它是app/下面很少的PHP文件之一)。

src/目录下暂时只有一个目录 - src/AppBundle- 所有的东西都在这里面。一个包,像一个“插件”,你可以找到开源捆绑,然后把它们安装到你的项目中,但就算是你自己的代码,也是处理束之中 - 典型的就是AppBundle(尽管这捆毫无特殊之处)。为了深入了解捆绑,以及为何你应该创建多个捆绑(提示:在项目之间共享代码),请参阅Bundle系统章节。

那么项目中的其他文件夹又是什么情况呢?

web/

它是整个项目的文档根目录,存在可公开访问的文件,比如CSS,图片以及用于执行应用程序(app_dev.phpapp.php)的Symfony的前端控制器(前端控制器)。


tests/

你程序的自动测试(如单元测试/单元测试)被存放在这里。


bin/

用于存放二进制(binary)文件。最重要的是console文件,它被用于控制中执行Symfony命令。


var/

这是那些自动生成的文件被存放的地方,比如缓存文件(var/cache/)和日志文件(var/logs/)。


vendor/

通过依赖管理器Composer,第三方类库,包,bundles被下载到这里。你应该不去编辑这个目录下的东西。

Symfony是弹性化的。如果需要,你可以覆写默认的目录结构。参考如何覆盖Symfony的默认目录结构

程序级别的配置 

Symfony内置了几个原生束(打开你的app/AppKernel.php文件查看),你可以安装更多.bundles的主要配置文件是app/config/config.yml

YAML:# app/config/config.yml # ...framework:
    secret: '%secret%'
    router:
    resource: '%kernel.root_dir%/config/routing.yml'
    # ...twig:
    debug:            '%kernel.debug%'
    strict_variables: '%kernel.debug%' # ...
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:framework="http://symfony.com/schema/dic/symfony"    xmlns:twig="http://symfony.com/schema/dic/twig"    xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd        http://symfony.com/schema/dic/symfony        http://symfony.com/schema/dic/symfony/symfony-1.0.xsd        http://symfony.com/schema/dic/twig        http://symfony.com/schema/dic/twig/twig-1.0.xsd">     <!-- ... -->     <framework:config secret="%secret%">
        <framework:router resource="%kernel.root_dir%/config/routing.xml" />
        <!-- ... -->
    </framework:config>     <!-- Twig Configuration -->
    <twig:config debug="%kernel.debug%" strict-variables="%kernel.debug%" />     <!-- ... --></container>
PHP:// app/config/config.php// ... $container->loadFromExtension('framework', array(
    'secret' => '%secret%',
    'router' => array(
        'resource' => '%kernel.root_dir%/config/routing.php',
    ),
    // ...)); // Twig Configuration$container->loadFromExtension('twig', array(
    'debug'            => '%kernel.debug%',
    'strict_variables' => '%kernel.debug%',)); // ...

framework根键配置的是FrameworkBundle,twig根键配置的是TwigBundle,诸如此类.Symfony中的许多行为都可以被控制,只需改变配置文件的一些选项即可。要搞清原因,请阅读配置参考

或者,通过超好用的bin/console命令,取得一个根键下被剥离出的完整配置样板:

$  php bin/console config:dump-reference framework

Symfony配置系统的威力绝大,包括环境,导入和参数等。为了掌握所有这些,参考配置章节。

下一步? 

恭喜你!你已经开始掌握Symfony,并将学到以全新方式来打造美丽的,功能性的,快速的,可维护的程序。

好了,你还需要熟悉下面这些章节,才能完成对基础架构的掌握:

  • 控制器

  • 路由

  • 创建和使用模板

然后,在中文书,还要学习服务容器表单系统,使用Doctrine(如果你需要查询数据库的话)以及更多!

还有一本中文菜谱,打包了更加高深的“如何”文章以解决更多问题。

祝各位学得高兴!