# 建立數據庫實體
ORM的本質在于將一個數據庫(更確切的說是其中的表格)“轉換”到一個PHP對象。于是我們不用類似“`select * from ...`”或者“`insert into ...`”這樣的SQL語句來操作表格中的數據,而是改用更直觀、也更不容易出錯的方式。比如下面這段代碼的最終運行效果是在`rsywx`數據庫的`book_place`中插入了一個新的紀錄。
~~~
$place = new BookPlace();
$place->setName('Common');
$manager->persist($place);
$manager->flush();
~~~
這樣的過程也許比`mysqli_query($connection, 'insert into ...')`多了幾行代碼的輸入,但是出錯機會少,而且也更安全。
## 導入MySQL數據庫
我們的應用開發里程中,數據庫已經建立完成(見[建立數據庫](https://taylorr.gitbooks.io/building-a-web-site-with-symfony/content/05.02%20database.html)一節)。所以,我們需要將數據庫轉換到ORM中可以操作的類。
在嚴格的MVC框架下,這樣形成的類是M(odel)層。但由于在本應用中,我們將提供數據這一任務全部放置到API中完成,所以我們導入MySQL數據庫形成M層并不是必須的。我們在本教程中還是這么做是為了后面一節[樣本數據](https://taylorr.gitbooks.io/building-a-web-site-with-symfony/content/05.05%20fixture.html)的需要。
進入虛擬機中項目的根目錄(`/vagrant/symfony`),輸入如下命令:
`php bin/console doctrine:mapping:import AppBundle yml`
其中:
* `bin/console`是SF的命令行接口,對SF框架應用的操作都要通過這個接口。
* `doctrine:mapping:import`表明我們要導入一個數據庫。
* `AppBundle`表明導入的數據庫要為`AppBundle`這個包所用。
* `yml`表明我們要用[YAML](http://yaml.org/)格式保存導出的數據庫信息。
命令順利執行后,在`symfony\src\AppBundle\Resources\config\doctrine\`目錄下會多出幾個文件,這幾個文件一一與數據庫中的表格對應。比如`BookPlace.orm.yml`對應的就是`book_place`數據庫。而它的內容也是如此:
~~~
AppBundle\Entity\BookPlace:
type: entity
table: book_place
id:
id:
type: integer
nullable: false
options:
unsigned: false
id: true
generator:
strategy: IDENTITY
fields:
name:
type: string
nullable: false
length: 255
options:
fixed: false
lifecycleCallbacks: { }
~~~
如果我們回憶一下`book_place`的結構,會看到這個yml文件對表格的描述是與該表格的定義完全一致的。
我們暫時不會深入討論各個字段定義中各個選項的意義。而且在一般情況下,我更喜歡從數據庫到類的映射方式。
## 生成實體類
導入了數據庫后,我們執行如下命令來生成實體類:
`php bin/console doctrine:generate:entities AppBundle`
該命令會在`AppBundle`目錄下生成一個新目錄`Entity`,其中會有若干個PHP文件。這些文件一一與上一步生成的YML文件對應,也因此一一與數據庫中的表格對應。比如`BookPlace.php`文件對應的是`BookPlace.orm.yml`文件,并進而對應的是`book_place`這個表格。它的內容如下:
~~~
<?php
namespace AppBundle\Entity;
/**
* BookPlace
*/
class BookPlace
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return BookPlace
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
~~~
這個文件中包含一個命名空間(`AppBundle\Entity`)的聲明和一個類(`BookPlace`)的聲明。而在類的聲明中,包括了兩部分:
* 第一部分是成員聲明。在`BookPlace`類中,只有兩個成員:一個是`$id`,一個是`$name`。它們分別于`book_place`表格中的兩個字段`id`和`name`對應。
* 第二部分是方法聲明。一般情況下,對于每個成員,都有兩個方法,一個是setter,一個是getter。對于只讀字段或者應該由數據庫引擎自動生成的字段(如本類中表明記錄唯一性的`id`)就只有一個getter。
從數據庫(表格)到ORM映射,再到PHP類聲明,我們完成了將數據庫加以對象化的步驟。
注意:上面產生的PHP文件是自動生成的。我們對這個文件和其中的類聲明不應該做任何的改動。
我們只能修改YML文件然后用`doctrine:generate:entities`來生成PHP文件,用`doctrine:schema:update`命令更新數據庫;或者直接操作數據庫,并在此通過上面講到的兩個步驟來更新ORM和Entity。
## ORM表述和Entity類中對表間關系的描述
在結束本小節之前,我們有必要看看ORM表述和Entity類中是怎樣描述表之間的關系的。
我們的`book_book`表格與若干表格有“1對多”的關系。比如一本書的出版社和`book_publisher`,它的購買地點和`book_place`都有1對多的關系。
在`BookBook.orm.yml`中,我們可以找到這樣一段:
~~~
AppBundle\Entity\BookBook:
... ...
manyToOne:
place:
targetEntity: BookPlace
cascade: { }
fetch: LAZY
mappedBy: null
inversedBy: null
joinColumns:
place:
referencedColumnName: id
orphanRemoval: false
publisher:
targetEntity: BookPublisher
cascade: { }
fetch: LAZY
mappedBy: null
inversedBy: null
joinColumns:
publisher:
referencedColumnName: id
orphanRemoval: false
lifecycleCallbacks: { }
~~~
在這里我們可以清楚看到,`BookBook`是多端,它與兩個1端對應。
而在`BookBook.php`中,我們可以找到這樣的代碼:
~~~
<?php
namespace AppBundle\Entity;
/**
* BookBook
*/
class BookBook
{
... ...
/**
* @var \AppBundle\Entity\BookPlace
*/
private $place;
/**
* @var \AppBundle\Entity\BookPublisher
*/
private $publisher;
... ...
/**
* Set place
*
* @param \AppBundle\Entity\BookPlace $place
*
* @return BookBook
*/
public function setPlace(\AppBundle\Entity\BookPlace $place = null)
{
$this->place = $place;
return $this;
}
/**
* Get place
*
* @return \AppBundle\Entity\BookPlace
*/
public function getPlace()
{
return $this->place;
}
/**
* Set publisher
*
* @param \AppBundle\Entity\BookPublisher $publisher
*
* @return BookBook
*/
public function setPublisher(\AppBundle\Entity\BookPublisher $publisher = null)
{
$this->publisher = $publisher;
return $this;
}
/**
* Get publisher
*
* @return \AppBundle\Entity\BookPublisher
*/
public function getPublisher()
{
return $this->publisher;
}
}
~~~
針對`place`和`publisher`這兩個字段,在`book_book`中存放的只是一個ID(一個整數),但是在根據ORM生成的PHP類中,它們以各自PHP類出現(`\AppBundle\Entity\BookPlace`和`\AppBundle\Entity\BookPublisher`)。對應的,它們的setter和getter也是對這兩個類的操作,而不是對`id`這個字段本身的操作。
在本教程中,我們不再對此進行進一步的展開。
- 引言
- 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 結語