# 樣本數據
應用開發,其實有很大一部分是在樣本數據的創建上。我們為什么要用樣本數據?
首先,有了樣本數據,我們的頁面顯示不會那么“空白”,有了比較實在的內容。
其次,樣本數據一般都是非常有規則的,因此便于我們進行單元測試和功能測試。
第三,有一些應用上的邏輯問題,需要有足夠的樣本數據才會體現。比如我們在測試分頁數據的時候。
如果我們只能人工或者半自動地往數據庫中添加數據,那么效率就太低了。SF考慮到了這個問題,因此提供了一個專門的部件幫助我們來完成這個工作。這個部件就是`DoctrineFixturesBundle`。
## 安裝`DoctrineFixturesBundle`
按照SF官方文檔的[說明](http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html),該部件的分裝有兩步。
第一步,下載部件包。而這是通過運行如下命令完成的:
`composer require --dev doctrine/doctrine-fixtures-bundle`
下載安裝完畢后,項目根目錄下`composer.json`文件中會增加一行:
~~~
"require-dev": {
...
"doctrine/doctrine-fixtures-bundle": "^2.3" //這是增加的一行
},
~~~
第二步,注冊并激活該部件包。
找到`app/AppKernel.php`文件,并作如下修改:
~~~
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
// ...
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
}
return $bundles
}
// ...
}
~~~
從這段代碼我們也可以看出,這個部件包只能使用在開發和測試環境中。這當然是很明顯的。
## 編寫樣本數據文件
首先我們要確定這些樣本數據文件所在的文件夾。一般而言,如果我們用Doctrine的話,樣本文件應該存放在`src/AppBundle/DataFixtures/ORM`之下。每個需要樣本數據填充的表格都要有一個對應的樣本文件。
我們先看一個比較簡單的樣本文件。該文件用來為`book_publisher`表格填充樣本數據:
~~~
<?php
namespace AppBundle\DataFixtures\ORM;
use \Doctrine\Common\DataFixtures\AbstractFixture;
use \Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use \Doctrine\Common\Persistence\ObjectManager;
use AppBundle\Entity\BookPublisher as BookPublisher;
class LoadPublisherData extends AbstractFixture implements OrderedFixtureInterface
{
/**
*
* {@inheritDoc}
*/
public function load(ObjectManager $manager)
{
//Create a common publisher
$pub1=new BookPublisher();
$pub1->setName('Common');
$this->addReference('commonPub', $pub1);
//Create a special publisher
$pub2=new BookPublisher();
$pub2->setName('Special');
$this->addReference('specialPub', $pub2);
$manager->persist($pub1);
$manager->persist($pub2);
$manager->flush();
}
/**
*
* {@inheritDoc}
*/
public function getOrder()
{
return 1;
}
}
~~~
我們首先約定本文件的命名空間。其次是四個`use`語句。這四個語句中,前三個是標準的,也是所有樣本文件需要用到的。第四個(`use AppBundle\Entity\BookPublisher as BookPublisher;`)用到的命名空間是本樣本文件需要操作的表格所對應的實體類。我們不對該實體類加以引用也是可以的,只是這樣做的話,后面的代碼中將要用到這個類的FQN(Fully Qualified Name)。
所有樣本數據類都派生自`AbstractFixture`并實現了`OrderedFixtureInterface`接口。從這點我們可以看出,樣本數據文件是有順序的。這是因為,在數據庫中,由于存在表格之間的依賴關系和引用一致性檢查,有些表格的數據必須在另外一些表格的數據能得以填充之前先得到填充。
比如,`book_book`表格中的`publisher`字段是`book_publisher`的外鍵。基于引用一致性的要求,我們不能為`book_book.publisher`賦一個并不存在于`book_publiser.id`中的值。所以,我們必須先填充`book_publisher`表格才能再填充`book_book`表格中的數據。
類的實現中,至少必須有兩個函數:一個是`load()`,一個是`getOrder()`。
`getOrder()`用來制定本樣本類中的數據需要在第幾位被填充。從上面的代碼中可以看到,該樣本數據是在第一位被填充,也就是我們首先在`book_publisher`中填充數據。
`load()`完成真正的數據填充。在上述代碼中,我們創建了兩個出版商的信息:一個被我們命名為"Common",一個被命名為"Special"。
另外,由于我們已經知道這兩個出版社一定會被`book_book`引用,所以我們用"`addReference()`"方法添加了各自的引用,以便我們在后續的樣本文件中引用這兩個對象。
最后我們持續化這兩個新創建的出版社對象并保存到物理數據庫中。
## 數據填充
有了這個樣本文件,我們就已經可以開始數據填充了。我們輸入如下的命令:
`php bin/console doctrine:fixtures:load`
注意:每一次我們進行樣本數據填充的工作,原來在數據庫表格中的數據都會被清除。這是為了保證在開發和測試的時候所有原始的數據都是一致的。
數據填充完畢后,我們使用的數據庫中就有了對應的數據。即以`book_publisher`為例,此時應該有兩個數據:

## 引用其它對象的樣本文件
如前所述,`book_book`這個表格依賴其它表格數據,所以它不能第一個被填充,而且在填充的時候需要引用其它表格中的數據。
我們已經在上面看到,`book_publisher`表格的填充文件中創建了兩個對象引用。類似的創建在`book_place`中也一樣存在。
一旦我們完成了其它表格的填充和相應依賴對象的創建,我們就可以在`book_book`對應的樣本文件中加以必要的引用而進一步創建`book_book`中的記錄。
~~~
<?php
namespace AppBundle\DataFixtures\ORM;
use \Doctrine\Common\DataFixtures\AbstractFixture;
use \Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use \Doctrine\Common\Persistence\ObjectManager;
use AppBundle\Entity\BookBook as BookBook;
class LoadBookData extends AbstractFixture implements OrderedFixtureInterface
{
/**
*
* {@inheritDoc}
*/
public function load(ObjectManager $manager)
{
//Now we create a 100 general book
for ($i = 10000; $i <= 10099; $i++)
{
$p = new BookBook();
$p->setAuthor('Normal');
$p->setCategory('Normal');
$p->setCopyrighter('');
$p->setDeco('Normal');
$p->setInstock(1);
$p->setTitle('Normal Book Title');
$p->setRegion('Normal');
$p->setTranslated(0);
$p->setPurchdate(new \DateTime());
$p->setPubdate(new \DateTime());
$p->setPrintdate(new \DateTime());
$p->setVer('1.1');
$p->setKword($i);
$p->setPage($i);
$p->setIsbn("$i");
$p->setPrice("$i.99");
$p->setLocation('a1');
$p->setIntro('This is a normal book.');
$p->setPublisher($this->getReference('commonPub'));
$p->setPlace($this->getReference('commonPlace'));
$p->setBookid("$i");
$manager->persist($p);
}
//Create a special book
$s = new BookBook();
$s->setAuthor('Special');
$s->setCategory('Special');
$s->setCopyrighter('');
$s->setDeco('Special');
$s->setInstock(1);
$s->setTitle('Special Book Title');
$s->setRegion('Somewhere On Earth');
$s->setTranslated(0);
$s->setPurchdate(new \DateTime('1970-1-1'));
$s->setPubdate(new \DateTime('1970-1-1'));
$s->setPrintdate(new \DateTime('1970-1-1'));
$s->setVer('1.1');
$s->setKword(999);
$s->setPage(999);
$s->setIsbn('123456789');
$s->setPrice('9999.99');
$s->setLocation('x1');
$s->setIntro('This is a very special book.\nIt is special because it is purchased at EPOCH.');
$s->setPublisher($this->getReference('specialPub'));
$s->setPlace($this->getReference('specialPlace'));
$s->setBookid('99999');
$this->addReference('aBook', $s);
$manager->persist($s);
$manager->flush();
}
/**
*
* {@inheritDoc}
*/
public function getOrder()
{
return 3;
}
}
~~~
在該樣本文件中,我們一共創建了101本書。其中有100本是常規的,有1本是特殊的。
對于常規的圖書,我們設置其相應的日期都是樣本數據創建當日:
~~~
$p->setPurchdate(new \DateTime());
$p->setPubdate(new \DateTime());
$p->setPrintdate(new \DateTime());
~~~
我們在設置其購買地點和出版商時,用到了之前樣本文件中創建的對象引用:
~~~
$p->setPublisher($this->getReference('commonPub'));
$p->setPlace($this->getReference('commonPlace'));
~~~
對于那本特殊的書,我們設置其相應的日期是一個很特別的日期,1970年1月1日,也就是計算機元年。其購買地點和出版商的設置也用到了之前樣本文件中創建的對象引用:
~~~
$s->setPublisher($this->getReference('specialPub'));
$s->setPlace($this->getReference('specialPlace'));
~~~
最后,我們為這本特殊的書創建了一個對象引用。因為這本書的對象在后續的樣本文件中還會用到。
## 小結
樣本數據填充部件是功能很強大的一個部件。它能幫助我們系統、高效地創建大量有組織、有規律的數據,方便今后的開發和調試。
樣本填充文件采用PHP寫成,所以不必麻煩開發人員再去熟悉一種新的方式或者語法。
筆者個人認為,開發初期使用樣本數據填充絕對是事半功倍的。
- 引言
- 1 LAMP
- 1.1 安裝虛擬機
- 1.2 安裝Vagrant
- 1.3 安裝Ubuntu
- 1.4 安裝Apache 2
- 1.5 安裝PHP
- 1.6 安裝MySQL服務器
- 1.7 最后的微調
- 1.8 設置一個虛擬主機
- 1.9 一個趁手的IDE
- 2 Symfony 3和重要構件
- 2.1 Symfony 3
- 2.2 Doctrine
- 2.3 Twig
- 2.4 Composer
- 3 Symfony重要概念
- 3.1 MVC
- 3.2 Bundle/包
- 3.3 Route/路由
- 3.4 Controller/控制器
- 3.5 Entity/實體
- 3.6 Repository/倉庫
- 3.7 Template/模板
- 3.8 Test/測試
- 4 藏書管理程序的結構
- 5 創建應用
- 5.1 建立版本管理
- 5.2 建立數據庫
- 5.3 應用結構
- 5.4 建立數據庫實體
- 5.5 樣本數據
- 5.6 路由
- 5.7 模板
- 5.8 開始編寫首頁
- 5.9 書籍詳情頁面
- 5.10 書籍列表頁面
- 5.11 書籍搜索
- 6 用戶和后臺
- 7 結語