新聞數據算是咱們業務模型裏必不可少的模型之一。根據咱們以前對需求的分析,咱們能夠很容易想到,新聞模型News
須要的屬性:php
接下來,咱們要在AppBundle裏建立它,可是這些數據還須要一個持久層來保存數據,例如以前配置的Mysql。目前流行的開發方式,不管是Java仍是ROR,都會使用ORM將數據庫字段和類屬性關聯起來。html
Symfony2框架自己並不包含ORM工具(嚴格意義上來講,Symfony2框架,即FrameworkBundle,不包含ORM,安全組 件,模板引擎,日誌工具,郵件組件等一系列工具),只不過Symfony installer將一些推薦的,Web開發經常使用的工具,都默認安裝了。若是你已經知道如何用Mysql來存儲/獲取數據,也不必定非要用ORM。這裏我 們爲了快速開發,也爲了省一些精力,就使用默認提供的Doctrine2 ORM,它會給咱們的開發帶來許多便利。git
決定了使用Doctrine2,咱們除了定義好News
類,還須要寫配置文件,讓Doctrine2 ORM將News
類同數據庫某個表關聯起來。聽起來要作的工做很多,不過且慢,DoctrineBundle裏自帶的代碼生成工具能讓咱們的開發再快一點點:github
1
2
|
$ php app/console doctrine:generate:entity
|
此時會有歡迎提示出現,而且讓你輸入一個模型的」短名字「,爲何說是要輸入短名字?News
並非全名,全名應該是包含命名空間的,好比咱們的News
全名應該是AppBundle\Entity\News
,Entity是什麼?Entity只是一個習慣性叫法。在Doctrine2的世界裏,只要是ORM過的模型,都叫Entity(除此以外還有用MongoDB做爲存儲方案的ODM,ODM過的模型習慣稱之爲Document)。算法
後面咱們就按照他們的提示,分別輸入:sql
1
2
3
4
5
6
7
8
9
10
11
|
The Entity shortcut name: AppBundle:News
Configuration format (yml, xml, php, or annotation) [annotation]: #這裏直接回車確認就好
New field name (press <return> to stop adding fields): title
Field type [string]: #直接回車
Field length [255]: 70 #這裏咱們很明確只須要70的長度
New field name (press <return> to stop adding fields): body
Field type [string]: text
New field name (press <return> to stop adding fields): #直接回車中止添加字段
Do you want to generate an empty repository class [no]? yes 是否建立新聞倉庫類,這裏咱們先yes,以後解釋
Do you confirm generation [yes]? #回車完成代碼生成
|
刷新src/AppBundle
目錄,多了一個Entity目錄,此目錄包含了兩個文件:News.php
和NewsRepository.php
。數據庫
打開News.php
咱們能夠看到,News
類已經生成好了,而且還有用註解格式寫的ORM配置。個人建議是能夠用生成代碼工具儘可能用,一是快,二是不容易寫錯字。安全
此時,咱們就能夠用DoctrineBundle
的數據庫操做工具來生成數據的數據庫和表了:app
1
2
3
|
$ php app/console doctrine:database:create
$ php app/console doctrine:schema:create
|
數據庫建立好以後,咱們應該建立」新建新聞「頁和」新聞詳情「頁、以及更新咱們以前寫的」新聞首頁「。不過且慢,DoctrineBundle不只僅能生成Entity,它還能根據Entity直接生成相關的Controller!這裏咱們先把以前的NewsController
類文件刪掉。而後使用下面命令:composer
1
2
|
$ php app/console doctrine:generate:crud
|
如同以前的命令,咱們只用回答它的問題就好了:
1
2
3
4
5
6
|
The Entity shortcut name: AppBundle:News
Do you want to generate the "write" actions [no]? yes
Configuration format (yml, xml, php, or annotation) [annotation]:
Routes prefix [/news]:
Do you confirm generation [yes]?
|
咱們會發現,被刪除的NewsController
又被從新生成,而且多了好多代碼。先無論每一個控制器方法裏寫了啥,咱們先檢查路由配置。咱們會發現,一切都很好,除了首頁的路由名字從之前的news_index
變成了news
,此時咱們能夠將news
改回news_index
,也能夠考慮將以前的news_index
定爲news
。咱們這裏選擇後者,畢竟已經爲咱們定義了一套規則,而且也不賴,何須再去折騰別的命名方式?還好代碼也很少,目前咱們只用把首頁模板default/index.html.twig
裏的news_index
改爲news
就好了。
doctrine:genearte:crud
作的事情就比較多了,它不只生成了控制器,全部的模板文件也都生成了,而且還生成了表單類。咱們先無論表單類,先訪問新聞首頁/news/
試試,沒有意外的話,大家能夠看到一個重新建、顯示、編輯、刪除都徹底可用的新聞功能。
須要注意的是:從Symfony2.6開始,模板文件推薦是放在app/Resources
下的,可是doctrine:generate:crud
命令仍是將模板文件放在了AppBundle
的Resources
目錄。不只如此,也不推薦使用@Template
註解來猜模板路徑(官方說法:主要由於性能問題),因此這裏咱們得把生成的src/AppBundle/Resources
目錄移到app
目錄,而且去掉控制器類裏的全部@Template
註解,而直接使用$this->render
方法。
有一些咱們暫時用不着的方法,能夠先去掉。好比刪除相關的方法deleteAction
以及createDeleteForm
,以及相關的調用所有去掉。模板裏面有用到delete_form的地方,也都刪掉。爲了節省代碼,咱們把newAction/createAction,以及editAction/updateAction合併爲一個方法,這也是Symfony官方所推薦的最佳實踐:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
/**
* @Route("/new", methods={"POST", "GET"}, name="news_new")
*/
public function newAction(Request $request)
{
$entity = new News();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) { // 注意若是不是POST請求,isValid方法會返回false
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('news_show', ['id' => $entity->getId()]));
}
return $this->render('news/new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
}
/**
* @Route("/{id}/edit", methods={"GET", "PUT"}, name="news_edit")
*/
public function editAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:News')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find News entity.');
}
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('news_edit', ['id' => $id]));
}
return $this->render('news/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
}
|
注意合併以後,以前的兩個路由news_create
和news_update
已經沒有了,因此NewsController
裏相關的代碼,都須要更新。
另外細心的同窗會發現註解@Method
也被我刪除了,由於@Route
實際上是包含@Method
的功能的。這裏看我的愛好。
若是你已經清理乾淨了@Method
和@Template
註解,那麼如下的代碼就能夠刪除了:
1
2
3
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
反過來也說明一個須要當心的點:若是你要使用某個註解功能,是須要use
到當前代碼文件的
如今有一個小問題,列表老是須要翻頁的。此時你們若是內心在琢磨」翻頁算法「,那說明你的開發方式還不夠Symfony,或者說還不夠敏捷。像翻頁 這種常見的需求,現成的庫必定是不少並且已經很是成熟,一我的花時間作設計和開發,讓成千上萬的其餘人節省時間,正是開源軟件偉大之處之一,除非是本身個 人愛好,省時間的事情是必定要作的,畢竟工程師的主要職責是實現而不是鑽研。目前支持Doctrine2的翻頁庫也有二三,並且大都已經Bundle化 了。這裏我推薦一款用得比較多的:KnpPaginatorBundle
安裝第三方Bundle須要使用Composer。安裝Composer不在本篇範圍內,你們能夠上網搜搜,也能夠看看我這篇博客。這裏我只用提醒你們一點:由於某種衆所周知的緣由,Composer的速度不是很快,你們能夠試試鏡像。
準備好Composer之後,執行如下命令(假設已經把composer.phar
更名爲composer
)
1
2
|
$ composer require "knplabs/knp-paginator-bundle"
|
昨天說過,以後將Bundle的」表明類「註冊到AppKernel
裏,才能使用Bundle所提供的配置文件以及」服務「,什麼是服務,稍後會說。
咱們先完成KnpPaginatorBundle的註冊:
1
2
3
4
5
6
7
8
9
10
|
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
// ...
);
}
|
而後新聞首頁的代碼作一點更新:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* @Route("/", name="news")
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$qb = $em->getRepository('AppBundle:News')->createQueryBuilder('n');
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($qb, $request->query->getInt('page', 1));
return $this->render('news/index.html.twig', [
'pagination' => $pagination,
]);
}
|
其中$this->get('knp_paginator')
獲得的就是一個服務。目前不用深究服務是什麼,只用 知道服務也是一個對象,此對像會依賴一些其餘的對象。若是手工初始化,須要寫不少代碼(依賴的對像的初始化,依賴的對象的依賴對象的初始化……),而 Symfony2的Service Container組件,能夠經過配置文件來描述這種對象之間的依賴關係,進一步在實際使用的時候,只需用$this->get
方法經過一個設置好的惟一名字去獲取組件就好了($this->get()
實際上是$this->container->get()
的快捷方式)。依賴注入和服務容器是Symfony2框架中運用得不少的概念之一,目前有個感受就行,之後還會提到。
相應的模板文件app/Resources/views/news/index.html.twig
也作一點調整:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<tbody>
{% for entity in pagination %}
<tr>
...
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="4">
{{ knp_pagination_render(pagination) }}
</td>
</tr>
</tfoot>
|
分頁咱們就實現了。
這裏我給你們介紹一下createQueryBuilder
方法。首先你們應該知道的是,從數據庫裏獲取數據,須要向數據庫發起數據請求,通常來講是一段字符串,好比你們最熟悉不過的SELECT * FROM xxx
這樣的SQL語句,這個請求就叫「Query」,而「Query Builder」就是指建立Query的東西。其實有了QueryBuilder這個東西,在拼裝SQL語句的時候,會方便許多,也不容易出錯。你們能夠對比一下如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$sql = "SELECT * FROM user WHERE is_active = 1";
if ($isAdmin) {
$sql .= " AND user_group = 'admin'"; // AND前面的空格很容易忘
}
if ($sortByUsername) {
$sql .= " ORDER BY username"; // 順序還不能錯,order必須在where以後判斷
}
// 若是使用QueryBuilder
$qb = $doctrine->createQueryBuilder('u')->where('u.isActive = 1');
if ($sortByUsername) {
$qb->orderBy('u.username');
}
if ($isAdmin) {
$qb->addWhere('u.userGroup = 'admin'');
}
|
須要各位讀者注意的是,若是記錄條數不滿每頁記錄數,分頁控件是不會出現的(KnpPaginator默認是10條,您也能夠本身設置每頁記錄數,至於怎麼調,留給你們看成業吧)