使用 Migration 和 Model

這章節會分兩個部份。因為 Model 必須和資料庫互動,所以在開始使用 Model 之前,必須先建立資料庫。我們將使用 Migration 來建立資料庫中的資料表,完成後再開始使用 Model 來和資料庫互動。

Migrateion

在傳統的開發上,我們會先使用資料庫管理軟體來建立資料庫、資料表及各個欄位等工作,而且你可能要在本機上建一次,遠端伺服器上又要建一次,而且在測試完成後,又必須清空資料庫的假資料等等,這些鎖碎的事其實會消耗掉許多時間。Laravel 提供 Migration 遷移功能,不只讓這些工作簡化,並且還可以重覆使用。

1.建立資料庫

MAMP[1] 中使用的是 MySQL 資料庫。

有幾個方法可以建立資料庫,使用終端機指令或 phpMyAdmin 網頁管理工具,在 Mac 上則推薦使用 Sequel Pro[2]。

使用資料庫管理工具要做的工作就只有建立一個資料庫,這裡我們把資料庫命名為 blog。使用者暫時就使用 MAMP 預設的 root 即可。

2.設定資料庫配置檔

回到 Laravel,我們要設定配置檔,讓它知道我們要連到哪個資料庫。

你可以參考 3.3 節的說明,依環境設定不同的配置,這裡為了簡化動作,請直接開啟 app/config/database.php 檔。

確定 default 的值為 mysql,在之後的 connections 項目中,在 mysql 項,修改 database 的名稱為 blog,username 如果是使用 MAMP 的話,預設是 root、password 則是空白。

3.建立資料庫遷移檔

假定資料庫配置設定都是正確的,那接下來就可以使用 Migration 來建立資料表及欄位等綱要了。

你可以手動在 app/database/migrations 建立遷移檔,不過這樣不只麻煩也太沒效率,這種事就交給 artisan 去做。請開啟終端機,並切換到網站根目錄,輸入:

php artisan migrate:make create_posts_table

如果成功,會產生一個類似 2014_12_11_081527_create_posts_table.php 的遷移檔,並且自動放在 app/database/migrations 目錄下。

註:可能發生的錯誤,請確定執行指令的所在位置是在網站根目錄,因為 artisan 在根目錄底下;另外就是 php 指令是否有加入 PATH 中,若沒有,則會出現找不到指令。

artisan 的 migrate:make 參數,會要求產生一個遷移檔,create_posts_table 則是這個檔案的名稱。那為什麼真正的檔案前面多了一堆數字呢?這堆數字其實是產生這個檔案的時間,這樣你就可以很方便得知哪個檔案是在什麼時候建立的,而且這個時間很重要,它會做為眾多遷移檔執行先後順序的依據。

假設你手動將建立資料表及新增資料到該資料表的兩個遷移檔時間改成相反,在執行遷移動作時,就會出現新增資料到尚未建立的資料表的情況而發生錯誤,所以這個時間是有意義的,不能隨意更改。

現在,可以來修改這個遷移檔的內容了。開啟它,你會看到它是一個繼承 Migration 的類別,其中只有兩個方法:up() 及 down()。我們會將新增資料表的任務放在 up() 方法裡,將移除資料表的任務放在 down() 方法裡。

當你在終端機輸入

php artisan migrate

會執行 up() 方法來建立資料表。而輸入

php artisan migrate:rollback

時,則會執行 down() 來移除這個資料表。

我們會使用 Schema 類別來建立資料表,程式碼如下:

public function up()
{
Schema::create('posts', function($table){
$table->increments('id');
$table->string('title');
$table->string('content');
$table->timestamps();
});
}
public function down()
{
Schema::drop('posts');
}

Schema 類別可以操控資料表,使用 create() 方法建立,使用 drop() 方法移除。

create 的第一個參數是資料表的名稱 posts。第二個參數則是要建立的欄位內容。

  • increments 是唯一值的遞增數字,就是主鍵,名稱叫 id。

  • string 在資料表中會被轉換為 VARCHAR 型態,title 及 content 都是欄位名稱。

  • timestamps 是時間戳記,在 Laravel 中預設是開啟的,可以在 Model 中關閉。

整個動作以相同的 SQL 語言來表示大概如下:

CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`content` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
);

是不是頭很痛呢?你只是寫了幾行程式外加一行指令就完成了這見鬼的 SQL 指令,是不是有效率多了。

4.使用 migrate 指令

當你編輯完遷移檔後,就可以使用指令來建立資料表

php artisan migrate

查看一下資料庫管理程式(記得重新整理),你應該可以看到 posts 資料表被建立了。

接著來把它移除吧

php artisan migrate:rollback

資料表就消失了。日後不管測試時加了多少假資料進來,只要執行兩行指令,就能讓資料庫跟新的一樣,是不是很方便呢。

最後記得再把資料表建立回來,後面需要用到。

Model

現在我們在資料庫中已經有一個資料表叫做 posts 了,所以可以建立一個和它相對應的 Model 。在 Laravel 中,資料庫中的每個資料表都會有一個對應的 Model。

Post 模型

接下來在 app/models 下新增一個 Post.php 檔,內容如下:

<?php
class Post extends Eloquent{
}

如果你有安裝前面提過的 JeffreyWay/Laravel-4-Generators[3] 套件,上面新增 Model 的方法可以改成用一行指令:

php artisan generate:model Post

Post 類別會繼承 Eloquent,然後你什麼都不用寫就完成了!這裡有個要注意的細節。預設你不用告訴 Eloquent 這個 Model 代表哪個資料表,它會自動將類別名稱以"字母小寫、複數"的名稱來取得對應的資料表,所以 Post 類別對應的資料表會是 posts,這也是為什麼前面我們所建立的資料表會命名為 posts 的原因。

設定模型

當然,你也可以使用 $table 屬性,特別去指定它的資料表名稱。前面提到的時間戳記也可以在使用 $timestamps 屬性取消。

class Post extends Eloquent{
protected $table = "post";//可以指定你想要的名稱
public $timestamps = false;//不使用,必須將 Migration 檔中的 $table->timestamps(); 此行移除
}

如果把 $timestamps 設為 false,記得要把遷移檔中的

$table->timestamps();

刪掉,否則會出現找不到欄位的錯誤。

Model 己經建立完成。我們先來看看怎麼用它。

新增資料

如果要新增一筆文章到 posts 資料表:

$post = new Post;
$post->title = 'Laravel 入門';
$post->content = 'Laravel 是一個 PHP Web 開發框架。';
$post->save();

以上程式碼的等效SQL:

insert into `posts` (title, content) values('Laravel 入門', 'Laravel 是一個 PHP Web 開發框架。')

在 PHP 中,變數名稱由 $ 字號開始,所以 $post 就是用來儲存 Post Model 物件的變數。

資料表中的每一個欄位都會有相對應的屬性,因此我們設定屬性。最後的 save() 方法會正式執行新增的動作。

刪除資料

$post = Post::find(1);
$post->delete();

等效SQL:

delete from `posts` where id=1;

find() 方法的參數是 id 的值,通常會由前端送來一個準備要刪除的資料的 id,以這個 id 去找到這列 post,然後 delete() 刪除它。

更新資料

$post = Post::find(1);
$post->content = '修改後的內容';
$post->save();

等效SQL:

update `posts` set content='修改後的內容' where id=1;

更新的動作和新增差不多,只是從 new 一個新的 Post 改為以 id 去找到已存在的資料列,針對需要修改的欄位,去更新相對應的屬性,最後 save() 就完成了。

這裡的 find() 方法是查詢 id 的快速方法,後面會介紹使用 where() 方法做條件查詢。

查詢資料

查詢可依不同需求會有不同的表達方法。

取得全部資料

$posts = Post::all();

等效SQL:

select * from `posts`;

all() 會傳回整個 posts 資料表的資料,所以 $posts 變數儲存的會是一個陣列集合,而非單一物件。

條件查詢

Post::where('id','=',1)->get();

如果是相等(=)條件,可以簡寫成,

Post::where('id',1)->get();

等效SQL:

select * from `posts` where id=1;

這行程式碼和 Post::find(1) 類似但它回傳的是集合(陣列),因為後面用的是 get() 方法,如果要和 find(1) 相同結果,可以這樣寫:

Post::where('id',1)->first();

這行只會取第一筆資料,行為如同 find(1)。實際上這就是 find() 的原始碼。

以上就是操作資料庫最常使用的4大行為:Create、Read、Update、Delete (CRUD)。在資料庫的章節會做更全面的說明。