在此之前,我們先明確一下我們要實現什么功能,在開始開展具體的代碼工作前先明確自己的目標是一個節省時間的好習慣。我們的需求如下:
- 用戶可以使用新浪微博或者QQ帳號登錄。
- 用戶可以投稿,需要填寫的內容為“標題”+“正文”
- 用戶可以對某篇文章進行評論
在以上的基礎上,我們繼續具體化我們的需求:
- 用戶的內容都將使用markdown格式
- 評論內容不可超過140個字
- 新聞內容不可超過5000個字
- 標題內容不可超過70個字
好,到這里我們的需求已經很明確了,雖然簡單得似乎不可能會有人用的樣子~
修改配置文件
此時項目里還有很多不需要的文件。比如昨天我們看到的Symfony自帶的演示頁面。這些文件都應該被清理。不過清理之前請等一會兒,演示頁面里有 一個“configure”功能我們還可以利用一下。點擊了“configure”按鈕以后我們可以對數據庫、以及網站的密鑰進行配置。其中第二步驟是設 置網站的“密鑰”,先不用管“密鑰”的用途,只管點“下一步”就行。
注意:由於我們此時需要使用Mysql來作為我們的數據存儲方案,所以這里我們需要安裝Mysql,並且在第一步里將driver設置為PDO_Mysql
。由於我們這里只介紹Symfony2,關於Mysql的安裝,不太熟悉的同學可以自行查詢,相關文檔也是非常之多,這里就略過了。當然,如果對存儲層非常熟悉的同學,你們也可以選擇熟悉的驅動,Mysql並不是唯一方案。
當兩個步驟都完成之后,Symfony會提示你,新的配置已經取代了舊的配置。此時就可以把此演示項目從我們的項目代碼里清理掉了。
剛才的步驟,其實只是修改了app/config/parameter.yml
文件而已,直接修改也是可以的。此文件里包含了一些顯而易見的配置,如果你英語還過關的話,應該一看就明白。
注意:如果安裝的是2.7以及以上版本Symfony,只能直接修改app/config/parameter.yml文件
清理不需要的文件和代碼
首先需要清理的是app/AppKernel.php
文件。在這里首先說一個Symfony世界里的“術語”:
什么是Bundle
我認為Bundle並沒有明確的定義,一般來說,如果某幾個功能,相互之間有關聯,並且相互影響,或者都屬於某一類功能,這些功能就可以捆綁在一起 成為一個Bundle。Bundle是一個目錄,里面包含了此Bundle的相關代碼以及配置文件。“Bundle”本來就有“一捆”的意思。比如 Symfony自帶演示項目Acme,就是一個Bundle的例子。在AcmeDemoBundle里的所有代碼,都是為了演示用的,所以大家可以隨意刪 除此Bundle而不用擔心會影響現有的項目。但為了大家的入門速度以及不把大家搞暈,這里先不多說了。
現在打開app/AppKernel.php
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
|
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new AppBundle\AppBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
$bundles[] = new AcmeDemo\Bundle\AcmeDemoBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}
// ...
}
|
仔細觀察registerBundles
(注冊Bundles)方法,可以看到此方法有許多名為XxxBundle
的 實例。所有的Bundle都需要一個類作為此Bundle的“代表”。只有Bundle的代表在此方法里被注冊了,才可以使用此Bundle里的配置以及 服務。大家可以看到此方法里已經注冊了許多Bundle。假如我們不使用twig作為我們的模板引擎,我們是完全可以把TwigBundle
那行直接刪除掉的。支持Symfony框架的第三方庫,都是以Bundle的方式存在。后面我們便會舉例子如何注冊第三方Bundle。
之前已經透露,演示項目的Bundle名叫“AcmeDemoBundle”,我們直接把AcmeDemoBundle
那一行刪掉。注意AcmeDemoBundle
是在if
里的,意思是如果當前環境是dev
或者test
,才注冊AcmeDemoBundle。這個判斷是什么意思?大家打開web
目錄,可以發現有app.php
以及app_dev.php
兩個文件,大家先不要急着看這兩個文件的內容,只用知道這兩個文件都是入口文件就行了。所有的請求,都會首先執行它們中的其中一個,其實看文件名大家也應該明白app_dev.php是開發的時候用的入口文件。昨天使用php app/console server:start localhost:8000
命令開啟web server的時候,此命令已經自動將app_dev.php
作為了入口文件。當我們的項目完成上線,就應該使用Nginx、Apache等Web Server把app.php
作為入口文件。
開發環境不同會導致什么不同?再來看看if
里的代碼:不僅AcmeDemoBundle
,在這個if
里的所有Bundle都是為了開發而存在的,比如DebugBundle是為了在代碼里使用dump()
方法打印格式化好的變量信息,WebProfilerBundle
則是為了收集開發所需要的調試數據,並且有好看的頁面可以顯示這些數據,是我們開發的好幫手。我們看到演示頁面最下面的那條工具欄,就是WebProfilerBundle
里包含的,后面我們將經常使用此工具欄做開發調試分析工作。
從app/AppKernel.php
刪除了AcmeDemoBundle
那行代碼之后,刷新頁面,發現報錯:
FileLoaderLoadException in FileLoader.php line 120: Bundle “AcmeDemoBundle” does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your AppKernel.php file? in @AcmeDemoBundle/Resources/config/routing.yml (which is being imported from “/…/app/config/routing_dev.yml”). Make sure the “AcmeDemoBundle” bundle is correctly registered and loaded in the application kernel class.
意思是說,你是否忘記在AppKernel.php
文件里注冊AcmeDemoBundle
?因為在app/config/routing_dev.yml
里還有AcmeDemoBundle的相關配置。
錯誤信息給我們提供了一個線索:app/config/routing_dev.yml
文件里也有需要刪除的代碼。我們將此文件打開,將一下代碼也刪除掉(代碼里也有注釋說“以下代碼應該被刪除”)
1
2
3
4
|
# AcmeDemoBundle routes (to be removed)
_acme_demo:
resource: "@AcmeDemoBundle/Resources/config/routing.yml"
|
此文件是一個“YAML”格式文件,關於此文件格式這里也不多說,大家也不用有太多壓力,因為YAML格式非常好懂,Symfony2的大多數配置 文件,都是使用的YAML格式。Symfony2支持多種文件格式作為配置文件,常見的還有XML文件,以及直接使用PHP來寫配置文件。對於配置文件的 選擇來說,XML可以使用DTD來做校驗,所以一些風格嚴謹的第三方Bundle內部的配置文件是用的XML,但XML有寫起來繁瑣的問題,而編寫方便正 是YAML的優點。在這里我們遵守Symfony installer幫我們做的選擇:使用YAML。需要注意:YAML和XML不是一個Bundle只能用一種,他們是可以混用的,而且從性能速度來說, 沒有絲毫差別,因為最終都會被編譯成PHP文件。除了以上說的幾種格式外,還有一種叫做“注解”的格式,后面再介紹。
routing***.yml
定義了訪問我們的項目的訪問路徑。Symfony2框架使用的“前端控制器”模式來處理HTTP訪問的需求,即每一種形式的路徑,都將映射到某個類的某個方法來處理。比如說當用戶訪問/hello/{name}
路徑的時候,我們可以將此路徑映射到AppBundle里的HelloController
(一般稱為“控制器類”)的helloAction($name)
(一般稱為控制器方法)方法來處理。routing***.yml
文件就是用來描述這種映射關系的。並且routing***.yml
文件可以有包含關系,我們可以看到routing_dev.yml
包含了routing.yml
文件。另外,我們從文件名也可以看出,routing_dev.yml
是開發環境才加載的配置。
從routing_dev.yml
里刪除掉acme
相關的路由以后,再訪問首頁。我們可以發現錯誤信息變了:
No route found for “GET /”
“route”即“路由映射規則”,出現此錯誤說明已經沒有關於“GET /”路徑相關的配置了。這說明至少從配置上,我們已經把AcmeDemoBundle
刪除掉了。
接下來我們再刪除掉src/Acme
目錄,清理工作便告一段落,說是一個段落的原因是還有一個比較關鍵的配置文件我們沒有提到,但不影響接下來的開發。以后會說。
添加頁面
每次訪問首頁都報錯,不是個好兆頭,怎么着我們得有一個首頁,無論有多簡單。
按我們之前所說,要想首頁能夠被訪問,我們得在routing***.yml
里添加有關首頁的配置。
由於首頁是無論開發環境還是生產環境都需要訪問的,我們就把配置寫在routing.yml
里好了,注意之前說過routing_dev.yml
已經包含了routing.yml
了。
我們打開app/config/routing.yml
文件,可以看到已經有如下內容:
1
2
3
4
|
app:
resource: @AppBundle/Controller/
type: annotation
|
這里的resource
指向的不是一個文件,而是一個路徑:@AppBundle
我想不用多說,一定是指AppBundle所在的目錄,也就是src/AppBundle
。是的,resource
可以不用指定某個具體的文件,目錄也行,意思是:此目錄內的所有配置文件都包含進來,甚至包括子目錄的配置。
然后type
的值為annotation
,這就是之前說的“注解”格式。
我們打開@AppBundle/Controller/
里隨便什么文件,看看注解格式到底是個啥。
@AppBundle/Controller/
目錄里目前只包含一個DefaultController.php
,打開此PHP文件:
1
2
3
4
5
6
7
8
9
10
11
12
|
// ...
class DefaultController extends Controller
{
/**
* @Route("/app/example", name="homepage")
*/
public function indexAction()
{
return $this->render('default/index.html.twig');
}
}
|
仔細觀察,像是配置的代碼,很明顯是@Route("/app/example", name="homepage")
那一行。原來“注解”格式,即是寫在代碼里的注釋。使用此格式的配置有一些其他格式不可比擬的好處,比如方便重構(舉個栗子:控制器名字改了,改1個文件VS改2個文件)。
既然Symfony installer幫我們生成了此文件,我們也繼續沿用。我們要做的只是把路徑改成首頁。然后路由名改短一點,路由名是個什么,后面會說到:
1
2
3
4
5
|
/**
* @Route("/", name="home")
*/
public function indexAction()
|
這個時候我們再訪問首頁,會出現寫着“Homepage.”的頁面。
這個“Homepage.”又是從哪兒來的?看代碼里面給我們提供的線索:indexAction
里返回了一個$this->render('default/index.html.twig')
,如果英語過關,很容易看出來此方法做的事情,就是“渲染”了一個模板文件default/index.html.twig
。
模板文件默認目錄位於app/Resources/views/
,所產我們要找的文件位於app/Resources/views/default/index.html.twig
,打開此文件可以看到里面有一行Homepage.
,以及一些其他的代碼。現在我們不用管其他的代碼。
為了方便開發我們的“新聞”板塊。我們先給首頁加一個鏈接/news
。
我們可以直接將Homepage.
替換成<a href="/news">新聞</a>
。但是這樣做有一個不好的地方:如果想改路徑,比如改成/新聞
,那么每個連接到“新聞”的href="/news"
都需要改一遍。Symfony2以及Laravel或者ROR等現代框架,幾乎都支持“雙向路由”,即在路徑和對應的控制器方法之間,再加一個路由名。此路由名可對應一個路徑,又對應一個控制器方法。前端模板里不再直接寫路徑,而是寫路由名:
1
2
|
<a href="{{ path('news_index') }}">新聞</a>
|
以后想修改路徑,都只用改一下配置文件即可。
修改好以上代碼,再訪問首頁,又會出現以下錯誤:
An exception has been thrown during the rendering of a template (“Unable to generate a URL for the named route “news_index” as such route does not exist.”) in default/index.html.twig at line 4.
意思是,目前還沒有一個叫news
的路由規則。OK我們現在來創建它:
我們把新聞相關的路徑都放在AppBundle\Controller\NewsController
類里:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
// src/AppBundle/Controller/NewsController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* @Route("/news")
*/
class NewsController extends Controller
{
/**
* @Route("/", name="news_index")
*/
public function indexAction()
{
return $this->render('news/index.html.twig');
}
}
|
並且為其創建模板文件app/Resources/views/news/index.html.twig
,目前就是空文件就行。
此時再刷新首頁,錯誤消失。並且“新聞“已經可以點擊。
創建文件時需要大家注意的是,一定要確認代碼的文件的編碼格式是UTF-8,否則會出現不可預料的字符亂碼問題