# symfony开发_使用Symfony 2开发Web应用程序

symfony开发

In Part 1, I have shown you how to set up Symfony 2 and link up the database. We also covered some fundamental concepts of the framework. In this part, we will link things up by creating routes, controllers, entities/repositories, and views to get the site up and running.

### 路线 (Routes)

Our instance of Symfony uses the YAML format to configure the routes for the application. A sample section of this site's routes is shown below:

File location: src/tr/rsywxBundle/Resources/config/routing.yml

home:
pattern:  /
defaults: { _controller: trrsywxBundle:Default:index }

contact:
pattern: /contact
defaults:
_controller: FrameworkBundle:Template:template
template: 'trrsywxBundle:Default:contact.html.twig'

book_list:
pattern: /books/list/{page}/{key}
defaults:
page: 1
key: null
_controller: trrsywxBundle:Book:list

books_search:
pattern: /books/search
defaults: {_controller: trrsywxBundle:Book:search}
requirements:
_method: POST

book_detail:
pattern: /books/{id}.html
defaults: { _controller: trrsywxBundle:Book:detail}

Every app needs an entry point. This is the "home" route. pattern defines the URI pattern the route should match. As this is the entry point, / is used. defaults:_controller defines the action the application will take when this route is matched. Please note the FQN format it used to map the route (and the pattern) for the action to take. In this case, it means whenever we are entering the site with just its domain, the index action in the Default controller under the namespace trrsywxBundle will be triggered.

There are other parameters you can set in the pattern and in the defaults. For example, the book_list route has a pattern with 2 parameters in the URI: page means the current page when there is more than one page for the result to be displayed (I will cover the Pagination in Part 3) and key is the keyword used to search books matching that key (in my current implementation, I only search the beginning of the title of a book). All parameters in the pattern must be within a pair of curly braces.

In book_list:default, I give the default values for the above two parameters: 1 for page and null for key. By doing so, a simple URI like http://localhost/app_dev.php/books/list will list the 1st page of all books, while http://localhost/app_dev.php/books/list/3/Beauty will simply list the 3rd page of all books whose title starts with Beauty (like "Beauty and Beast").

book_list:default ，我为上述两个参数提供了默认值：1表示page ，null表示key 。 这样，一个简单的URI(例如http://localhost/app_dev.php/books/list将列出所有书籍的第一页，而http://localhost/app_dev.php/books/list/3/Beauty将简单地列出列出的，其标题开始与所有书第3页Beauty (如“美女与野兽”)。

You will also notice that the books_search route has requirements:_method set to POST. This restricts the calling method for that pattern to be just POST (in a form submission) as I don't want the users to simply visit /books/search in the browser. In my implementation, this route is meant for internal usage and should redirect to another page with the search result matching the criteria submitted (POSTed)via a form.

The full documentation on routes can be found on Symfony's site.

### 控制器 (Controllers)

The next step is to define controllers. I have a total of 4 controllers located in src/tr/rsywxBundle/Controller. Namely, they are: BookController.php, DefaultController.php, LakersController.php, and ReadingController.php. I group the functions related to various routes based on their functionality.

I will show just two controllers here, matching book_list and books_search.

<?php

class BookController extends Controller
{
// ... Many other functions here, see source

public function listAction($page,$key)
{
$em =$this->getDoctrine()->getManager(); // Get the Entity Manager
$rpp =$this->container->getParameter('books_per_page'); // Get the global parameter for how many books to show on one page

$repo =$em->getRepository('trrsywxBundle:BookBook'); // Get the repository

list($res,$totalcount) = $repo->getResultAndCount($page, $rpp,$key); // Get the result

$paginator = new \tr\rsywxBundle\Utility\Paginator($page, $totalcount,$rpp); // Init the paginator
$pagelist =$paginator->getPagesList(); // Get the pagelist used for navigation

return $this->render('trrsywxBundle:Books:List.html.twig', array('res' =>$res, 'paginator' => $pagelist, 'cur' =>$page, 'total' => $paginator->getTotalPages(), 'key'=>$key)); // Render the template using necessary parameters
}

public function searchAction(Request $req) {$q = $req->request->all(); // Get the posted data$page = 1; // Get which page to display
$key =$q['key']; // Get the search criteria

$em =$this->getDoctrine()->getManager();
$rpp =$this->container->getParameter('books_per_page');

$repo =$em->getRepository('trrsywxBundle:BookBook');

list($res,$totalcount) = $repo->getResultAndCount($page, $rpp,$key);

$paginator = new \tr\rsywxBundle\Utility\Paginator($page, $totalcount,$rpp);
$pagelist =$paginator->getPagesList();

return $this->render('trrsywxBundle:Books:List.html.twig', array('res' =>$res, 'paginator' => $pagelist, 'cur' =>$page, 'total' => $paginator->getTotalPages(), 'key' =>$key));
}
}

In a typical controller fashion, it does three things:

• Get all the preparation work done (input parameters, get the Entity Manager, Repository, etc);

完成所有准备工作(输入参数，获取实体管理器，存储库等)；
• Get the results from a repository;

从存储库中获取结果；
• Display the results (with or without further processing) by rendering a template (Symfony uses Twig as its template engine).

通过渲染模板(Symfony使用Twig作为模板引擎)显示结果(有或没有进一步处理)。

Observe three things carefully:

1. Look at how the parameters we defined in route book_list (page and key) are passed into the function call by name. Symfony does not care about the order of the parameters' appearance but requires a strict name match.

看一下我们在route book_list ( pagekey )中定义的参数如何通过名称传递到函数调用中。 Symfony不在乎参数外观的顺序，但需要严格的名称匹配。

2. Look at how the POSTed parameters in a form are passed into searchAction and how we retrieve the necessary information from the submitted data.

查看如何将表单中的POSTed参数传递到searchAction以及如何从提交的数据中检索必要的信息。

3. getParameter is a function to retrieve global parameters. The global parameters are defined in the file app/config/parameters.yml.dist (autogenerated with composer into .yml) and look like this:

getParameter是用于检索全局参数的函数。 全局参数在app/config/parameters.yml.dist文件中定义(由composer自动生成为.yml)，如下所示：

books_per_page: 10

Normally, it is good practice to leave the application logic in the controller functions and the data provider in a repository. This helps organize the code and keep it re-usable.

A detailed document on Controllers can be found here.

### 实体和存储库 (Entities and Repositories)

In ORM's methodlogy, an entity is the objective reflection of a database table. Instead by issuing native SQL commands in your PHP to manipulate the data, we can use intuitive and straightforward ways to CRUD the data. While the entities generated by Symfony contain simple methods to retrieve a data by ID, in a proper application that is far from enough. Repositories are there to provide more customized ways to manipulate data.

Entities are generated via a console/terminal command: php app\console doctrine:generate:entity (See Part 1 or the official documentation.) The generated PHP entity files are located at src/tr/rsywxBundle/Entity. Take a look at those PHP files to understand more on how the database tables are mapped (via ORM) into a PHP class.

Note: Don't make any changes to these PHP files directly. They are meant to be generated by Symfony.

To create a repository to hold all the customized database manipulation methods, you need to do two things:

• Modify corresponding ORM mapping files (located at src/tr/rsywxBundle/Resources/config/doctrine) to specify a repository class for that database object;

修改相应的ORM映射文件(位于src / tr / rsywxBundle / Resources / config / doctrine)，以指定该数据库对象的存储库类；
• Create all necessary functions to provide required database manipulation capabilities (like retrieving data).

创建所有必需的功能以提供所需的数据库操作功能(例如检索数据)。

Take my books collection table (and its entity for example):

File location: src/tr/rsywxBundle/Resources/config/doctrine/BookBook.orm

tr\rsywxBundle\Entity\BookBook:
type: entity
repositoryClass: tr\rsywxBundle\Entity\BookRepository # Add this line to indicate that we will use BookRepository.php as the repository class for BookBook table
table: book_book
fields:
...

After that, run php app\console doctrine:generate:entity again. You will notice a new BookRepository.php file is created under src/tr/rsywxBundle/Entity. You can now open that file and create your own functions for data manipulation:

public function getResultAndCount($page,$rpp, $key=null) { // Get the book count$em = $this->getEntityManager(); if ($key == 'null')
{
$q1 =$em->createQuery('select count(b.id) bc from trrsywxBundle:BookBook b');
}
else
{
$qstr = sprintf("select count(b.id) bc from trrsywxBundle:BookBook b where b.title like '%s%%'",$key);
$q1 =$em->createQuery($qstr); }$res1 = $q1->getSingleResult();$count = $res1['bc']; // Now get the wanted result specified by page$repo = $em->getRepository('trrsywxBundle:BookBook');$q2 = $repo->createQueryBuilder('r') ->setMaxResults($rpp)
->setFirstResult(($page - 1) *$rpp)
->orderBy('r.id', 'desc');
if ($key <> 'null') {$key = $key . '%';$q2->where('r.title like :key')
->setParameter('key', $key); }$q2 = $q2->getQuery();$res2 = $q2->getResult(); return array($res2, $count); } In the code above, which is a very representative code segment for retrieving data, I have used 2 ways to generate an SQL query and execute it. 在上面的代码中，这是用于检索数据的非常有代表性的代码段，我使用了两种方法来生成SQL查询并执行它。 One is to use $em->createQuery(), which uses a similar grammar as we will use when we issue an SQL command in a database. The only difference is in the from segment. Instead of using a raw table name (book_book), we use the table class name (trrsywxBundle:BookBook).

### 视图和模板 (Views and Templates)

So far we have been doing the "background" work. Nothing is presentable without a view. In Symfony, as in most frameworks, a view is equivalent to a template. As I mentioned earlier, Symfony uses Twig as its template engine. It is simple to learn and quick to display.

I will show you a segment of the Twig template I used to display the Book List page.

(I am not a good web designer, so I slap on Bootstrap 3.0 to quickly bootstrap my page design. You can use your own design or ready-made templates). Note: below is just a fragment of the whole template.

(我不是一个优秀的Web设计师，因此我在Bootstrap 3.0上打了一下掌心，以快速引导我的页面设计。您可以使用自己的设计或现成的模板)。 注意：以下只是整个模板的一部分。

File location: src/tr/rsywxBundle/Resources/views/Books/List.html.twig

{% set active=2 %}
{% extends 'trrsywxBundle:Default:index.html.twig' %}

{% block title %}<title>{{ "RSYWX | Book Collection | Page }}{{cur}}</title>{% endblock %}

{% block content %}
<div class="container">
<div class="row">
<div class="col-md-6">
<h3>My book collection</h3>
</div>
<div class="col-md-6">
<h3>Page {{cur}} of {{total}}</h3>
</div>
<div class="col-md-4">
<form class="form-inline" method="post" action="{{path('books_search')}}" name="searchform" id="searchform">
<div class="form-group">
<input type="text"  class="form-control" required="required" placeholder="Search the beginning of a book title" name="key" id="key" value="{{key}}"/>
<input type="hidden" name="cur" id="cur" value="{{cur}}"/>
</div>
<button type="submit" class="btn btn-primary">Search</button>

</form>
</div>
<br>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
<tbody>
<tr>
<td><strong>ID</strong></td>
<td><strong>Title</strong></td>
<td><strong>Author</strong></td>
<td><strong>Purchase Date</strong></td>
<td><strong>Location</strong></td>
</tr>
{% for book in res %}
{% set author=book.author%}
{%if author ==''%}
{%set author='(anonymous)'%}
{%endif%}
<tr>
<td><a href="{{path('book_detail', {'id':book.id})}}">{{book.id}}</a></td>
<td><a href="{{path('book_detail', {'id':book.id})}}">{{book.title}}</a></td>
<td>{{author}}</td>
<td>{{book.purchdate|date('Y-m-d')}}</td>
<td>{{book.location}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="pager">
<ul>
<li class="previous"><a href="{{path('book_list', {'page':1, 'key':key})}}">First</a></li>
{%if cur==1%}
<li class="previous disabled"><a href="{{path('book_list', {'page':cur-1, 'key':key})}}">Previous</a></li>
{%else%}
<li class="previous"><a href="{{path('book_list', {'page':cur-1, 'key':key})}}">Previous</a></li>
{%endif%}
{%if cur==total%}
<li class="previous disabled"><a href="{{path('book_list', {'page':cur, 'key':key})}}">Next</a></li>
{%else%}
<li class="previous"><a href="{{path('book_list', {'page':cur+1, 'key': key})}}">Next</a></li>
{%endif%}
<li class="previous"><a href="{{path('book_list', {'page':total, 'key':key})}}">Last</a></li>
</ul>
</div>
</div>
</div>

</div>
{% endblock %}

A few things to discuss here:

• To display something, use {{obj.member}} notation; to control the flow or manipulate data, use {% control statement or manipulation statement %}. Actually, these are the two and only two grammar structures in Twig.

要显示内容，请使用{{obj.member}}表示法； 要控制流或操纵数据，请使用{% control statement or manipulation statement %} 。 实际上，这是Twig中仅有的两个语法结构。

• {% extends %} can be used to extend the page layout from a parent (base) layout. It is very useful for page design.

{% extends %}可用于从父(基本)布局扩展页面布局。 这对于页面设计非常有用。

• {% block title %}...{% endblock %} replaces the content in the parent layout with your own content.

{% block title %}...{% endblock %}用您自己的内容替换父版式中的内容。

• {{path(...)}} is used to generate URIs matching that route in the template. It is a very important helper function that everyone will use. Please also note how the parameters of that route are passed.

{{path(...)}}用于在模板中生成与该路由匹配的URI。 这是每个人都会使用的非常重要的帮助程序功能。 另请注意，该路由的参数是如何传递的。

• {% for ... in ... %} is used to iterate the result set. It is also very commonly used.

{% for ... in ... %}用于迭代结果集。 它也是非常常用的。

• {% set ... %} is used to set a local variable.

{% set ... %}用于设置局部变量。

• The rest is regular HTML.

其余的是常规HTML。

Once we run the app, our render should look like something the following figure:

### 结论 (Conclusion)

In this part, I have demonstrated how to link up the basic elements in Symfony and get your application up and running. As you can see, the amount of required PHP code was quite minimal. Also, with the help of Bootstrap, the template code was quite straightforward as well.

In the next and last part of this series, we will cover a few advanced techniques:

• Pagination

分页
• Dynamically add tags associated with a book

动态添加与书关联的标签
• Dynamically create a water mark for the book cover

动态为书的封面创建水印
• …and more

…和更多

symfony开发

• 0
点赞
• 0
评论
• 0
收藏
• 扫一扫，分享海报

07-22 76
09-26 87