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


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.

第1部分中 ,我向您展示了如何设置Symfony 2和链接数据库。 我们还介绍了该框架的一些基本概念。 在这一部分中,我们将通过创建路由,控制器,实体/存储库和视图来链接这些东西,以使站点正常运行。

路线 (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:

我们的Symfony实例使用YAML格式来配置应用程序的路由。 该站点路线的示例部分如下所示:

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

文件位置: src/tr/rsywxBundle/Resources/config/routing.yml

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

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

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

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

      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.

每个应用都需要一个切入点。 这是“回家”路线。 pattern定义路由应匹配的URI模式。 因为这是入口点,所以使用/defaults:_controller定义匹配此路由时应用程序将执行的操作。 请注意, FQN格式用于映射要执行操作的路线(和模式)。 在这种情况下,这意味着只要我们进入仅包含其域的站点,就会在名称空间trrsywxBundle下的Default控制器中触发index操作。

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.

您还可以在模式和默认值中设置其他参数。 例如, book_list路由在URI中具有带有2个参数的模式: page表示当前页面,如果要显示的结果超过一个页面(我将在第3部分中进行分页),并且key是所使用的关键字搜索与该key匹配的书籍(在我当前的实现中,我仅搜索书籍标题的开头)。 pattern中的所有参数都必须在一对花括号内。

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.

您还将注意到, books_search路由的books_search requirements:_method设置为POST 。 这限制了该模式的调用方法为POST(在表单提交中),因为我不希望用户在浏览器中简单地访问/books/search 。 在我的实现中,此路由仅供内部使用,应重定向到另一页,其搜索结果与通过表单提交(发布)的条件匹配。

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.

下一步是定义控制器。 我在src/tr/rsywxBundle/Controller总共有4个src/tr/rsywxBundle/Controller 。 即,它们是:BookController.php,DefaultController.php,LakersController.php和ReadingController.php。 我根据其功能将与各种路由相关的功能分组。

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



    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).


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.


  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.

在ORM的方法中,实体是数据库表的客观反映。 取而代之的是,通过在PHP中发出本机SQL命令来处理数据,我们可以使用直观直接的方法对数据进行CRUD。 尽管Symfony生成的实体包含简单的方法来按ID检索数据,但是在适当的应用程序中这还远远不够。 存储库在那里提供了更多定制的方式来处理数据。

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.

实体是通过控制台/终端命令生成的: php app\console doctrine:generate:entity (请参阅第1部分官方文档 。)生成PHP实体文件位于src/tr/rsywxBundle/Entity 。 查看这些PHP文件,以了解有关如何将数据库表(通过ORM)映射到PHP类的更多信息。

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

注意:请勿直接对这些PHP文件进行任何更改。 它们是由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

文件位置: src/tr/rsywxBundle/Resources/config/doctrine/BookBook.orm

        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

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:

之后,再次运行php app\console doctrine:generate:entity 。 您会注意到在src/tr/rsywxBundle/Entity下创建了一个新的BookRepository.php文件。 现在,您可以打开该文件并创建自己的函数以进行数据处理:

public function getResultAndCount($page, $rpp, $key=null)
            // Get the book count
            $em = $this->getEntityManager();
            if ($key == 'null')
                $q1 = $em->createQuery('select count( bc from trrsywxBundle:BookBook b');
                $qstr = sprintf("select count( 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')
                    ->setFirstResult(($page - 1) * $rpp)
                    ->orderBy('', '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.


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).

一种是使用$em->createQuery() ,它使用与在数据库中发出SQL命令时使用的语法类似的语法。 唯一的区别是from段。 代替使用原始表名( book_book ),我们使用表类名( trrsywxBundle:BookBook )。

getSingleResult is used in my first query as I am expecting there will be only one result returned from this query (the count of books matching a certain criteria or ALL books).

在我的第一个查询中使用了getSingleResult ,因为我希望此查询仅返回一个结果(符合特定条件的图书数量或所有图书的数量)。

In the 2nd query, I used createQueryBuilder and chained all the methods to set necessary SQL parameters (the starting record, the record count, the order, and a where condition to filter).


getResult is used since we will be expecting more than one record.

getResult ,因为我们期望有多个记录。

Finally, we pack the results into an array: $res2 as the resulted dataset and $count as the count of the resulted dataset. They will be further processed in the calling controller function (in this case, create a Paginator object to facilitate the navigation).

最后,我们将结果打包到一个数组中: $res2作为结果数据集, $count作为结果数据集的计数。 它们将在调用控制器函数中进一步处理(在这种情况下,创建Paginator对象以方便导航)。

视图和模板 (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.

到目前为止,我们一直在做“背景”工作。 没有视图,什么都无法呈现。 与大多数框架一样,在Symfony中,视图等效于模板。 如前所述,Symfony使用Twig作为其模板引擎。 它简单易学,显示Swift。

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

文件位置: 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 class="col-md-6">
            <h3>Page {{cur}} of {{total}}</h3>
        <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}}"/>
                <button type="submit" class="btn btn-primary">Search</button>

    <div class="row">
        <div class="col-md-12">
            <table class="table table-striped">
                        <td><strong>Purchase Date</strong></td>
            {% for book in res %}
                 {% set}
            {%if author ==''%}
            {%set author='(anonymous)'%}
                        <td><a href="{{path('book_detail', {'id'})}}">{{}}</a></td>
                        <td><a href="{{path('book_detail', {'id'})}}">{{book.title}}</a></td>
            {% endfor %}
    <div class="row">
        <div class="col-md-6">
            <div class="pager">
                    <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>
                            <li class="previous"><a href="{{path('book_list', {'page':cur-1, 'key':key})}}">Previous</a></li>
                {%if cur==total%}
                                <li class="previous disabled"><a href="{{path('book_list', {'page':cur, 'key':key})}}">Next</a></li>
                                    <li class="previous"><a href="{{path('book_list', {'page':cur+1, 'key': key})}}">Next</a></li>
                                        <li class="previous"><a href="{{path('book_list', {'page':total, 'key':key})}}">Last</a></li>

{% 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.


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.

在这一部分中,我演示了如何链接Symfony中的基本元素以及如何启动和运行您的应用程序。 如您所见,所需PHP代码非常少。 同样,在Bootstrap的帮助下,模板代码也非常简单。

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


Stay tuned, and download the source code to experiment on your own!




  • 0
  • 0
  • 0
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
钱包余额 0