在這一章我們將會學習如何建立一個模組,這是組織頁面的結構化元素。同時我們也會學習如何創造一個分成一個動作也一個模板的頁面,之所以分成動作與模板,是因為MVC模式。連結與表彰是基本的頁面交互,我們將會學習如何在模板中插入這些元素並且在動作中進行處理。
創建一個模組框架
正是如我們在第2章所了解的,Symfony將頁面組織為模組。在創建一個頁面之前,我們需要建立一個模組,並且初始化為一個Symfony可以識別的檔案結構的空殼。
Symfony命令列自動化處理模組的建立。我們只需要呼叫init-module任務,並且使用程式名稱以及模組名稱作為參數。在前一章,我們建立了一個myapp程式。要為這個程式加入mymodule模組,我們可以輸入下面的指令:
> cd ~/myproject
> symfony init-module myapp mymodule
>> dir+ ~/myproject/apps/mymodule
>> dir+ ~/myproject/apps/mymodule/modmy/myapp > dir+ ~/myproject/apps/myapp/modules/mymodule/actions
>> file+ ~/myproject/apps/myapp/modules/mymodule/actions/actions.class.php
>> dir+ ~/myproject/apps/myappect/apps/myapp/apps/ modules/mymodule/config
>> dir+ ~/myproject/apps/myapp/modules/mymodule/lib
>> dir+ ~/myproject/apps/myapp/modules/mymodule/templates
> file+ ~/myproect/apps/file /modules/mymodule/templates/indexSuccess.php
>> dir+ ~/myproject/apps/myapp/modules/mymodule/validate
>>> file+ ~/myproject/test/functional/myapp/myuleActionsTest> 給myproject/test/functional/myapp/mymoduleActionsTest.php
>> tokens ~/myproject/apps/myapp/modules/mymodule/actions/actions.class.php
>> tokens ~/myproject/apps/myapp/modules/my templates/indexSuccess.php
與actions/,config/,lib/,templates/,validate/目錄相分離,這個指令只建立三個檔案。位於test/目錄中的一個為單元測試。 actions.class.php指向預設的模組歡迎頁面。 templates/indexSuccess.php檔案為空。
在actions/actions.class.php檔案中預設產生的動作:
<?php class mymoduleActions extends sfActions { public function executeIndex() { $this->forward('default', 'module'); } }
對於每一個新的模組,Symfony會建立一個預設的index動作。他是由一個名為executeIndex的動作方法以及一個名為indexSuccess.php的模板檔案所組成。我們可以透過下面的URL來瀏覽對應的頁面:
http://localhost/myapp_dev.php/mymodule/index
在這一章我們並不會使用預設的index動作,所以我們可以從actions.class.php檔案中移除executeIndex()方法,並且從templates/目錄中刪除indexSuccess.php檔。
除了命令列,Symfony還提供了其他的方法來初始化一個模組。其中一個方法就是手動來建立目錄與檔案。在許多情況下,一個模組的動作與模板就意味著操作給定資料表的資料。因為創建,獲取,更新,以及從一個資料表中刪除資料記錄的必須程式碼通常都是相同的,Symfony提供了一個名為框架的機制來我們產生這些程式碼。查看第十四章,我們可以了解更多關於這個技術的資訊。
新增一個頁面
在Symfony中,頁面後面的邏輯儲存在動作中,而表面則是在模板中。沒有邏輯的頁面仍然需要一個空的動作。
新增一個動作
"Hello,world!"頁面則會透過一個myAction的動作進行存取。要建立這個動作,只需要在mymoduleActions類別中加入一個executeMyAction方法,如下:
<?php class mymoduleActions extends sfActions { public function executeMyAction() { } }
动作方法的名字总是execute'Xxx'()的形式,其中名字的第二部分是动作的名字,并且第一个字母大写。
现在我们可以请求下面的URL:
http://localhost/myapp_dev.php/mymodule/myAction
Symfony将会抱怨丢失了myActionSuccess.php模板。这是正常的。在Symfony中,一个页面通常是由一个动和与一个模板组成的。
URL是响应的一部分
Symfony包含一个路由系统允许我们在实际的动作名与需要调用的URL格式之间有一个完整的分隔。这允许自定义URL的自定义格式,就如同他是响应的一部分。我们不再为文件的结构或是请求的参数据限制,一个动作的URL看起来就我们所希望的解析。例如,到一个名为article模块的索引动作调用通常如下面的样子:
http://localhost/myapp_dev.php/article/index?id=123
这个URL由一个数据获取一篇指定的文章。但是URL可以通过在routingyml配置文件中作一些小的改动而以一种完全不同的方式进行编写:
http://localhost/articles/europe/france/finance.html
这样的URL不仅对于搜索引擎友好的,他对于用户来说也是十分重要的,这样用户就可以将地址栏作为一个伪码命令来自定义查询,例如下面的例子:
http://localhost/articles/tagged/finance+france+euro
Symfony知道如何为用户解析并生成URL。路由系统会自动从一个简洁URL中脱去所请求的参数,并使其为动作可用。他同时也会格式化响应中所包含的超链接,从而使其看起来更为简洁。我们将会在第九章了解这个特性的更多内容。
总之,这就意味着我们命名程序的动作的方式不应受到调用他们的URL的样子的影响,而是受程序中动作的函数控制。一个动作的名了解释了动作实际所做的内容,而且通常为不定式格式中的一个动词(例如show,list,edit)。动作的名字可以做到对于终端用户完全不可见,从而不必担心使用显式的动作名。我们可以有效的利用代码注释来解释我们的函数功能,从而使代码更读。
添加一个模板
动作需要一个模板来进行封装。一个模板就是位于一个模块的templates/目录的一个文件,通常是以动作和动作的词尾来进行命名的。默认的动作词尾为"success",所以为myAction动作所创建的模板文件应命名为myActionSuccess.php。
模板只包含表现代码,所以在其中要包含尽可有少的PHP代码。实际上,一个显示"Hello,world!"的页面只有一行的代码的模板。
Hello, world!
<p>Hello, world!</p> <?php if ($test) { echo "<p>".time()."</p>"; } ?>
替代的PHP语法如下:
<p>Hello, world!</p> <?php if ($test): ?> <p><?php echo time(); ?></p> <?php endif; ?>
由动作向模板传递信息
动作的工作是要完成所有复杂的计算,数据读取以及测试,并且设置要输出或是测试的模板变量。Symfony使得动作类的属性在全局名字空间中为模板可用。下面显示如何由动作向模板传递信息。
在动作中设置动作属性从而使其为模板可用:
<?php class mymoduleActions extends sfActions { public function executeMyAction() { $today = getdate(); $this->hour = $today['hours']; } }
模板直接访问动作属性:
<p>Hello, world!</p> <?php if ($hour >= 18): ?> <p>Or should I say good evening? It's already <?php echo $hour ?>.</p> <?php endif; ?>
模板已经可以访问一些数据,而不需要在动作中设置任何变量。每一个模板通常可以调用$sf_context,$sf_request,$sf_params,$sf_user对象的方法。他们包含与当前内容,请求,请求参数以及会话相关的数据。我们很快就会学到他们的用法。
使用表单向用户收集信息
表单是向用户收集信息的一个好方法。使用HTML编写表单以及表单元素有时是相当麻烦的,尤其是当我们希望适用于XTHML时更是如此。我们可以用通常的方式在Symfony模板中包含表单元素,如下面所示,但是Symfony提供了帮助器从而使得这个任务更为简单。
模板可以包含通常的HTML代码:
<p>Hello, world!</p> <?php if ($hour >= 18): ?> <p>Or should I say good evening? It's already <?php echo $hour ?>.</p> <?php endif; ?>
一个帮助器是用在模板中的由Symfony定义的PHP函数。他输出HTML代码,并且比我们自己编写实际的HTML代码要快速得多。使用Symfony帮助器,我们用下面的代码得到的输出结果与上面通常的HTML代码相同:
<p>Hello, world!</p> <?php if ($hour >= 18): ?> <p>Or should I say good evening? It's already <?php echo $hour ?>.</p> <?php endif; ?>
如果在上面的代码中,我们认为使用帮助器的版本并不会比编写HTML代码快,那么我们可以考虑一下下面的情况:
<?php $card_list = array( 'VISA' => 'Visa', 'MAST' => 'MasterCard', 'AMEX' => 'American Express', 'DISC' => 'Discover'); echo select_tag('cc_type', options_for_select($card_list, 'AMEX')); ?>
这会得到下面的HTML输出结果:
<select name="cc_type" id="cc_type"> <option value="VISA">Visa</option> <option value="MAST">MasterCard</option> <option value="AMEX" selected="selected">American Express</option> <option value="DISC">Discover</option> </select>
在模板中使用的帮助器的好处就在于加快了编码的速度,代码的清晰与简洁。而代价就是我们需要花费时进行学习。所以我们可以在模板中不使用Symfony帮助器,并且以我们通常的方式来编写代码,但是这会是一个巨大的损失。
注意,短开放标记的用法(=,等同于
表单处理有专门的一章进行讨论,因为Symfony提供了许多工具,绝大数的帮助器,来使其简单。我们将会在第10章了解到关于帮助器更多的内容。
链接到另一个动作
我们现在已经知道在动作名字与需要调用的URL之间有一个分离。所以我们使用下面的方式来创建到另一个动作的链接时,他只会工作在默认的路由系统中。如果我们以后决定改变URL的样子,那么我们就需要查看所有的模板来改变超链接。
超链接,通常的方法:
<a href="/myapp_dev.php/mymodule/anotherAction?name=anonymous"> I never say my name </a>
要避免这样的麻烦,我们应总是使用link_to()帮助器来我们的程序动作创建超链接。下面的例子演示了超链接帮助器的用法。
link_to()帮助器:
<p>Hello, world!</p> <?php if ($hour >= 18): ?> <p>Or should I say good evening? It's already <?php echo $hour ?>.</p> <?php endif; ?>
生成的HTML与前面的相同,所不同的就在于当我们改变我们的路由规则时,所有的模板都会正确工作,并且重新格式化URL。
link_to()帮助器,就像其他的帮助器,会接受特定选项的额外参数和额外的标签属性。下面的例子显示了一个可选参数以及生成的HTML。选项参数或者是一个相关的数组或者是由空格分隔的显示key=value的简单字符串。
大多数帮助器接收一个可选参数:
// Option argument as an associative array
<?php echo link_to('I never say my name', 'mymodule/anotherAction?name=anonymous', array( 'class' => 'special_link', 'confirm' => 'Are you sure?', 'absolute' => true )) ?>
// Option argument as a string <?php echo link_to('I never say my name', 'mymodule/anotherAction?name=anonymous', 'class=special_link confirm=Are you sure? absolute=true') ?> // Both calls output the same => <a class="special_link" onclick="return confirm('Are you sure?');" href="http://localhost/myapp_dev.php/mymodule/anotherAction/name/anonymous"> I never say my name</a>
任何时候我们使用一个Symfony帮助器来输出一个HTML标记时,我们可以在可选参数中插入额外的标记属性。我们可以使用HTML 4.0的方式来编写这样的属性,而Symfony将会以更简洁的格式进行输出。这也就是为什么帮助器会比HTML更容易编写的原因。
因为他需要一个额外的分析与转换,字符串语法比数组语法要慢一些。
与表单帮助器相类似,链接帮助器有更多的数量和选项。第九章将会详细讨论他们。
由请求得到信息
无论用户通过表单发送信息(通常以POST请求的方式)还是通过URL(GET请求),我们可以通过带有sfActions对象的getRequestParameter()方法的动作来获取数据。下面的例子显示如何在anotherAction中得到name参数的值。
在动作中由请求参数得到数据:
<?php class mymoduleActions extends sfActions { ... public function executeAnotherAction() { $this->name = $this->getRequestParameter('name'); } }
如果数据的处理是简单的,我们甚至并不需要使用动作来得到请求参数。模板可以访问一个名为$sf_params的对象,他提供了get()方法来得到请求参数,就如同动作中的getRequestParameter()方法。
如果executeAnotherAction()方法是空的,下面的例子显示了anotherActionSuccess.php模板文件如何得到同样的name参数。
在模板中直接由请求参数得到数据:
<p>Hello, <?php echo $sf_params->get('name') ?>!</p>
那么为什么不使用$_POST,$_GET或是$_REQUEST变量呢?因为我们的URL将会不同的方式进行格式化(http://localhost/articles/europe/france/finance.html),这样通常的PHP变量就会再起作用,并且只有路由系统可以得到请求参数。而我们希望添加输入过滤来阻止恶意代码注入,而这只有当我们将所有的请求参数保存在一个简洁的参数装配器中才可能。
$sf_params对象比仅提供一个与数组等同的方法要强大得多。例如,如果我们只是希望检测一个请求参数是否存在,我们可以简单的使用$sf_params->has()方法,而不使用get()测试实际的值。
在模板中测试请求参数的存在:
has('name')): ?> <p>Hello, <?php echo $sf_params->get('name') ?>!</p>Hello, John Doe!
我们已经猜到了,这可以在单一的代码行中编写。与Symfony中大多数的获取方法一样,动作中的getRequestParameter()方法与模板中的$sf_params->get()(实际上调用同一个对象的相同主法)方法接受第二个参数:如果没有提供请求参数则使用默认值:
<p>Hello, <?php echo $sf_params->get('name', 'John Doe') ?>!</p>
总结
在Symfony中,页面是由一个动作(actions/actions.class.php文件中以execute为前缀的一个方法)和一个模板(templates/目录下的一个文件,通常以Success.php结尾)组成的。他们通地在程序中的函数组织在一个模块中。编写模板是由帮助器来完成的,而帮助器则是由Symfony提供的返回HTML代码的函数。而我们需要将URL看作响应的一部分,而URL在需要可以格式化,所以我们应避免在动作命名中使用到URL的直接引用或者是请求参数检索。
一旦我们知道了这些基本原则,我们就可以使用Symfony来编写一个完整的Web程序了。但是我们的路还有很长,因为我们在程序开发过程需要处理的每一个任务由一些Symfony特性来完成的。