Symfony2中建立數據模型

Symfony是一款優秀的基於MVC架構的PHP框架。今天我在這裏給你們分享一下在Symfony中如何建立數據模型和基於RESTful api的搭建。重點是如何建立數據模型哦!php

  • 本教程使用的當前Symfony的LTS版本(Symfony 2.8)html

  • 本教程開始學作日期爲2016年11月前端

如何安裝Symfony?

同窗們本身去官網看啦 -- Symfony官方網站。不管是使用Symfony installer仍是Composer,在中國大陸地區下載速度會比較慢,須要使用科學的上網方法。程序員

何爲MVC?

MVC這一點我得重點說一下。去任何一家企業面試PHP,90%的同窗會被問到什麼是MVC。我相信你們已經背得倒背如流,能夠自信滿滿的回答道:MVC是三個單詞的首字母縮寫,它們是Model(模型)、View(視圖)和Controller(控制器)。Ok,面試官滿意地點點頭,進入下一個問題。可是,可是!在實際工做中,我發現不少同窗在MVC框架下依然喜歡使用手寫sql語句進行數據庫操做。從這點來看,我我的認爲這樣作的同窗實際上是沒有理解到MVC的,下面我就講一講我對MVC的理解。web

請你們想象下圖所描繪的場景:Peter和John相隔千里,但他們能夠經過互聯網進行溝通。抽象一點理解,Peter和John是在電腦的世界裏面進行溝通。在電腦的世界裏面,再也不有現實中距離的限制,千里以外瞬息即到!模型就是現實對象在計算機世界裏面的抽象,咱們將Peter和John進行了建模,讓他們可以進入計算機的世界。經過controller咱們能夠操做計算機世界裏的數據模型,經過view咱們能夠接收計算機世界裏數據模型傳達的信息。重點:模型是計算機世界裏實實在在的一個對象(Object),模型是現實世界裏一個對象在計算機世界裏的抽象。面試

圖片描述

對象與數據庫的關係

一般在電腦的世界裏建立的數據模型會轉換爲數據庫裏的一條條記錄。例如,咱們建立了Peter和John這兩個數據模型,那麼在數據庫的User表裏面就會多出下面這兩條記錄:sql

| id | name  | gender | age |
| 1  | Peter | male   | 30  |
| 2  | John  | male   | 25  |

那麼在MVC構架的框架裏,這兩個數據模型寫入數據庫的過程一般是這樣的:數據庫

  • 首先,定義個一個User類(如下代碼爲僞代碼)編程

Class User {
    public $name;
    public $gender;
    public $age;
    
    # 下面會有一堆getter和setter
}
  • 而後實例化兩個User類後端

$peter = new User();
$peter->name = 'Peter';
$peter->gender = 'male';
$peter->age = 30;
$peter->save(); // 將Peter存入數據庫

$john = new User();
$john->name = 'John';
$john->gender = 'male';
$john->age = 25;
$john->save(); // 將John存入數據庫

重點來了,有些同窗會說:你這樣太麻煩了,兩句sql不就搞定了嗎?!具體sql語句以下:

INSERT INTO User VALUES ('Peter', 'male', 30)
INSERT INTO User VALUES ('John', 'male', 25)

然而,在實際項目當中,數據模型不可能像我剛纔說的例子那麼簡單,若使用手寫sql的話,就必需要再寫代碼實現記錄到邏輯的轉換,邏輯複雜了,工做量就會很大!可是,若是讓你手動建立數據模型,每每你又會以爲也是一件麻煩的事情。在有了Symfony以後,數據模型的建立就變得無比簡單,接下來我會用實際的例子告訴你怎麼在Symfony裏面快捷高效的建立數據模型!

設計數據模型

好了,假設咱們如今要爲一所學校作一個系統,具體需求以下圖所示:
圖片描述

老師A有兩門課 - 課程A和課程B,學生A和學生B選修了課程A,學生B和學生C選修了課程B。拿到這個需求的時候,千萬不要急急忙忙的開始寫代碼,首先要作的是理清楚老師,課程還有學生之間的邏輯關係,建好數據模型。由於在邏輯都尚未理清的狀況下寫出來的代碼你以爲會沒有問題嗎?!到了那時,你會不停的進行結構大改,把本身搞得筋疲力盡並且代碼寫的亂七八糟!

好,在瞭解需求以後,咱們總結出如下兩點:

  • 一個老師能夠教授多門課程,但一門課程只能有一個授課老師,即老師和課程是一對多的關係,老師須要保存的字段爲:姓名,性別,年齡,院系,職位,發表論文數量,課程須要保存的字段爲:課程代碼,課程名稱,課程開始時間,課程結束時間。

  • 一門課程能夠被多個學生選修,一個學生也能夠選修多門課程,即學生和課程是多對多的關係。學生須要保存的字段爲:姓名,性別,年齡,學院,年級。

邏輯關係理清楚了,就能夠開始數據建模了。我所謂的數據模型其實很簡單,就是類和類與類之間的關係的圖表,根據以上需求,咱們能夠建立以下對應的數據模型:
圖片描述

上面設計的數據模型是可用的,可是咱們發現一個問題 - 老師和學生有相同的字段,例如姓名,性別等,由於這些都是一我的基本的屬性。那麼運用OOP的思想,咱們眼前豁然開朗 - 老師和學生其實都是繼承了人這個對象。基於這點,咱們能夠優化以前的數據模型,讓邏輯更加清晰,數據庫表裏的字段再也不有冗餘!優化後的數據模型以下圖所示:
圖片描述

到這裏,數據模型已經搭建完成,業務邏輯已經整理完畢。能夠說已經完成了大半了工做。有的同窗會說:你搞了那麼半天,一點代碼都沒寫,沒有一點產量。那我只能回答:呵呵。下面我就實際演示一下什麼是基於框架下的快速應用開發(RAD),但願同窗們能夠明白我們程序員必定要取巧,想比作更加劇要,否則你一生都是碼農,幹十年等於積累一年經驗!

建立數據模型

進入symfony2的根目錄,執行以下命令:

php app/console

而後就會列出Symfony2全部的可用命令,咱們重點關注如下兩條命令:

  • generate:bundle: 建立一個bunlde,你能夠把Bundle理解成一個模塊 - 處理同一個問題的程序和資源的一個集合。Bundle和Bundle之間應該是低耦合(最好是零耦合),這一點應該很容易理解吧。例如一個是前端Bundle一個是後端Bundle,你要是在前端Bunlde裏寫了一堆處理後端邏輯的程序,在後端Bundle里加了一些處理前端邏輯的程序。Ok,我建議你應該先去從新理解一下編程的思想。爲何說能夠作到零耦合呢?由於在Symfony2裏面一些多個Bundle都會調用的方法能夠註冊成Service,而不須要直接到方法所在的Bundle裏去調用。

  • doctrine:generate:entity: 生成一個模型,entity就是實體模型的意思。

首先使用 php app/console generate:bundle 生成一個名爲Backend的Bundle,生成步驟以下圖:
圖片描述

接着,使用 php app/console doctrine:generate:entity 生成數據模型,根據提示來一步一步來,很簡單的!例以下圖演示瞭如何生成學生數據模型:

圖片描述

進行如上操做以後,能夠發如今項目目錄結構下已經多了一個叫BackendBundle的文件夾,而且4個數據模型已經建好了,以下圖所示:
圖片描述

接着,咱們須要手動調整一下Symfony2幫咱們自動生成的這4個數據模型。

數據表屬性命名規範

在Symfony2 entity類中,屬性名若由多個單詞組成,通常使用大寫字母分隔,例如:finishAt。而在數據表中,屬性名若由多個單詞組成,通常使用下劃線分隔,例如:finish_at。查看自動生成的數據模型的屬性,具體格式以下:

/**
 * @var \DateTime
 *
 * @ORM\Column(name="startFrom", type="datetime")
 */
 private $startFrom;

能夠發現,每個屬性上面都有一堆註釋,但這一堆註釋不一樣於普通的註釋,由於它其實就是一段程序,由於咱們在生成BackendBundle的時候Configuration format選擇的是annotation。在Symfony2中,路由註冊,變量類型定義,數據庫屬性定義等均可以經過annotation的方式。特別是在路由註冊的時候,我特別偏好於annotation的方式,由於它是離散型的,就像一個標籤同樣貼在了所做用屬性或控制器的上面。有其餘框架開發經驗的同窗們應該知道在其餘框架中,註冊的路由一般會被寫在一個文件裏面,好比在Yii2 basic中,全部註冊路由都在config/web.php的urlManager中。我曾經接手了一個基於Yii2 basic的項目,一打開路由配置文件,發現裏面有一千多行註冊路由的代碼,並且以前註冊這些路由的程序員根本就沒正確理解路由的註冊,搞得配置文件亂七八糟,每次打開這個文件內心就會有一種很不爽的感受,程序員的潔癖呀!還有一點就是,在團隊合做中,這種中心配置文件確定會被不一樣的程序員編輯,合併時發生衝突的可能大大增長,若是是Git還好一點,但若是你還用SVN,你就等着哭吧。好了,不吐槽了,我簡單講解一下上面的那一段代碼,@var定義了$startFrom爲DateTime類型,@ORM\\Column定義了數據表中,屬性名爲startFrom,類型爲datetime。咱們只須要修改一下數據表中的屬性名就能夠統一數據庫表屬性命名規範:

/**
 * @var \DateTime
 *
 * @ORM\Column(name="start_from", type="datetime")
 */
 private $startFrom;

數據模型驗證

驗證就是指檢查傳遞過來的數據是否正確,例如以前的startFrom是一個DateTime類型,若是傳遞一個string類型過來,確定是不正確的。在Symfony2中,在每一個屬性的annotation就能夠定義驗證條件,詳情可見 Symfony Validation。下面舉兩個例子說明一下:

/**
 * @var string
 *
 * 不能爲空
 * @Assert\NotBlank()
 * 值必須爲Male或Female
 * @Assert\Choice({"Male", "Female"})
 *
 * @ORM\Column(name="gender", type="string", length=255)
 */
 private $gender;
/**
 * @var string
 *
 * 不能爲空
 * @Assert\NotBlank()
 * 值類型需爲string型
 * @Assert\Type("string")
 *
 * @ORM\Column(name="name", type="string", length=255)
 */
 private $name;

自動時間戳屬性

在數據模型中,通常咱們會默認加入兩個時間戳屬性 - createdAt(建立於)和updatedAt(更新於),這兩個屬性顯然不該該是由咱們傳值的,而是在記錄生成和更新的時候自動將當時的時間存下來。對於這個問題,StofDoctrineExtensionsBundle這個第三方Bundle已經給咱們提供了完美的解決方案。

  • 提示:爲何咱們喜歡用流行的第三方框架,應爲流行的第三方框架有本身的社區,有一大幫程序員不停的在爲社區的壯大獻出本身的一份力。不少時候,不少問題其實都是別人已經解決過了的。因此,咱們在遇到一個問題的時候,要多去社區裏面找找成熟的解決方案,而不是關上門重複的造輪子。

  • Symfony官方幫咱們總結出了最火的30個第三方Bundle,有興趣的能夠看一下,說不定會有驚喜哦!

首先,用Composer下載這個第三方Bundle:

composer require stof/doctrine-extensions-bundle

而後,啓用和配置Bundle:

// app/AppKernel.php

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...

            new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
        );

        // ...
    }

    // ...
}
// app/config/config.yml
stof_doctrine_extensions:
    default_locale: en_US
    orm:
        default:
            timestampable: true

最後,在createdAt和updatedAt屬性上面用Annotation的方法進行配置便可:

use Gedmo\Mapping\Annotation as Gedmo;

// ...

/**
 * @var \DateTime
 *
 * @Gedmo\Timestampable(on="create")
 * @ORM\Column(name="created_at", type="datetime")
 */
 private $createdAt;

/**
 * @var \DateTime
 *
 * @Gedmo\Timestampable(on="update")
 * @ORM\Column(name="updated_at", type="datetime")
 */
 private $updatedAt;

創建模型與模型之間的關係

以前咱們已經總結出了數據模型之間的關係,如今讓咱們再回顧一下:

  • Teacher與Course的關係爲一對多

  • Student與Course的關係爲多對多

  • Teacher和Student都繼承Person類

具體實現以下:

// src/BackendBundle/Entity/Course.php

use Doctrine\Common\Collections\ArrayCollection;

// ...
class Course {
  // ...

  /**
   * @var Teacher
   *
   * @ORM\ManyToOne(targetEntity="Teacher", inversedBy="courses")
   * @ORM\JoinColumn(name="teacher_id", referencedColumnName="id")
   */
  private $teacher;

  /**
   * @ORM\ManyToMany(targetEntity="Student", inversedBy="courses")
   * @ORM\JoinTable(name="course_student")
   */
  private $students;

  public function __construct()
  {
    $this->students = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Teacher.php

use Doctrine\Common\Collections\ArrayCollection;

// ...
class Teacher extends Person {

  // ...
  
  /**
   * @ORM\OneToMany(targetEntity="Course", mappedBy="teacher")
   */
  private $courses;

  public function __construct()
  {
    $this->courses = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Student.php

use Doctrine\Common\Collections\ArrayCollection;

// ...
class Student extends Person {

  // ...
  
  /**
   * @ORM\ManyToMany(targetEntity="Course", mappedBy="students")
   */
  private $courses;

  public function __construct()
  {
    $this->courses = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Person.php

/**
 * Person
 *
 * @ORM\Table(name="person")
 * @ORM\Entity(repositoryClass="BackendBundle\Repository\PersonRepository")
 * @ORM\InheritanceType("JOINED")
 */
abstract class Person
{
  // ...
}

而後運行php app/console doctrine:generate:entities BackendBundle生成剛纔咱們加入的自定義屬性的getter和setter。以後,運行php app/console doctrine:schema:update --force,數據表就已經成功生成了,趕快去數據庫裏面看看吧。

相關文章
相關標籤/搜索