Laravel Migration、Model、DB物件 相關操作 (濃縮)
官方文件非常詳細,而這篇主要將用法濃縮來「速查」,且由另外一個角度來了解 DB 相關操作。
指令相關
建立/更新 tables$ php artisan migrate
清空
$ php artisan migrate:reset
刪除最後一個 migration
$ php artisan migrate:rollback --step=1
自動化(首次安裝,下 composer install 會連帶執行)
{
...
"scripts": {
"init" : [
"mysql -P 3306 -h 127.0.0.1 --user=root --password=1234 --execute=\"CREATE DATABASE IF NOT EXISTS mydb\"",
"php artisan migrate",
"php artisan db:seed"
],
}
}
DB 相關物件
//原始 SQL
$users = DB::select('select * from users where id = :id', ['id' => 1]);
//使用 Queries(ActiveRecord) 簡易 CURD 等..
DB::table('users')->select('name')->get();
//使用 ORM,需先建立 Model
User::where('name', 'wild')->get();
//將 Model 彼此建立關聯後,可操作 A Model 去撈取關聯的 B Model 資料
User::find(1)->posts()->where('title','test')->get();
//在 migrate file 使用,主要在操作資料庫架構
Schema::create('users', function (Blueprint $table) {
$table->string('name', 20)->comment('註解');
});
命名規則
"Snake-case" 蛇底式,ex. user_detail
"Camel-Case" 駝峰式,ex. UserDetail
DB相關命名都使用 snake-case
table name 命名,通常 +'s' 複數
table index 命名,通常為 id,關聯的 table index,本地欄位名稱通常為 table_name(單數) + '_id'
FK 名稱通常為 table_name + '_' + local欄位名稱 + '_foreign'
多對多樞紐表,名稱為 A表_B表,依字母排序
Model 檔案名稱用 camel-case,單數
Model 中的 method 名稱,依照 relation 做單數 or 複數命名
Model 在自動帶入 FK 時,預設 classname + '_id'
Migration 檔名通常為 時間戳記_create_xxxx_table
Migration 一個 Class 也可操作多張 table
"Snake-case" 蛇底式,ex. user_detail
"Camel-Case" 駝峰式,ex. UserDetail
DB相關命名都使用 snake-case
table name 命名,通常 +'s' 複數
table index 命名,通常為 id,關聯的 table index,本地欄位名稱通常為 table_name(單數) + '_id'
FK 名稱通常為 table_name + '_' + local欄位名稱 + '_foreign'
多對多樞紐表,名稱為 A表_B表,依字母排序
Model 檔案名稱用 camel-case,單數
Model 中的 method 名稱,依照 relation 做單數 or 複數命名
Model 在自動帶入 FK 時,預設 classname + '_id'
Migration 檔名通常為 時間戳記_create_xxxx_table
Migration 一個 Class 也可操作多張 table
Relationships
Laravel沒有規定一定要建 Model,可以直接用 DB::來操作。若想用ORM操作,才需建立 Model,Model 內的 relation "有用到才建立"
One To One 一對一
- 範例:users vs details
//User model
public function detail(){
return $this->hasOne('App\Detail', 'foreign_key', 'local_key');
}
//Detail model
public function user(){
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}
One To Many 一對多
- 範例:users vs comments
//User model
public function comments(){
return $this->hasMany('App\Comment', 'FK', 'local_key');
}
//Comment Model
public function user(){
return $this->belongsTo('App\User', 'FK', 'other_key');
}
Many To Many 多對多
- 範例:users vs user_app vs app
- 判別:A表 - 樞紐表 - B表,樞紐表欄位一定有 A_id, B_id
//User model
public function apps(){
return $this->belongsToMany('App\App', 'user_app', 'user_id', 'app_id');
}
//app Model
public function users(){
return $this->belongsToMany('App\User', 'user_app', 'app_id', 'user_id');
}
Has Many Through 遠層一對多
- 範例:countries vs users vs comments,要取得某個 country 的 Comment,須先查該國哪些 User,在查這些 User 有哪些 Comment
- 判別:A表(1) - B表(n) - C表(n),需查B才能查C
//Country model
public function comments(){
return $this->hasManyThrough('App\Comment', 'App\User', 'country_id', 'user_id');
}
Polymorphic Relations 多型關聯
- 範例:users vs products vs photos,Photos 存放 User & Product 的圖片,用 type 分開,User 與 Photo 是 1對1關係
- 判別:A表 - B表 - 多型表,多型表含有 id + type 欄位
//User Model
public function photos(){
return $this->morphMany('App\Photo', 'imageable');
}
//Product Model
public function photos(){
return $this->morphMany('App\Photo', 'imageable');
}
//Photo Model
public function imageable(){
return $this->morphTo(); //取得所有所屬的可擁有圖片的Model
}
//photos Migration
//up function
Schema::create('photos', function (Blueprint $table) {
$table->increments('id');
$table->string('path');
$table->morphs('imageable'); //會建立2欄位,imageable_id 和 imageable_type
});
Many To Many Polymorphic Relations 多型多對多
- 範例:posts vs videos vs taggables vs tags,Post 有 N 個 Tag、Video 也有 N 個 Tag
- 判別:A表 - B表 - C多型表 - C表,多型表欄位為 Target_id, Target_type, C_id ,Target_type 為 A or B
//Post Model
public function tags(){
return $this->morphToMany('App\Tag', 'taggable');
}
//Video Model
public function tags(){
return $this->morphToMany('App\Tag', 'taggable');
}
//Tag Model
public function posts(){ //type1
return $this->morphedByMany('App\Post', 'taggable');
}
public function videos(){ //type2
return $this->morphedByMany('App\Video', 'taggable');
}
//taggables Migration
//up function
Schema::create('taggables', function (Blueprint $table) {
$table->integer('tag_id'); //C表的索引
$table->morphs('taggable'); //會建立2欄位,taggable_id 和 taggable_type
$table->foreign('tag_id')->references('id')->on('tags'); //如有需要可加
});
預載 N+1
- with
延遲預載 load
Pivot 樞紐表操作(多對多)
- Model取值 - 在樞紐表中,除了 A_id, B_id 欄位之外,也可能有其他資訊欄位
//user Model
public function apps(){
return $this->belongsToMany('App\App')->withPivot(col1, col2,..);
return $this->belongsToMany('App\App')->withTimstamps();
}
//Controller
$user = App\User::find(1);
$user->apps()->attach($appId, ['col1' => false,...]); //參數2可略
$user->apps()->detach($appId); //刪除單一個關係
$user->roles()->detach(); //刪除全部關係
$user->apps()->updateExistingPivot($appId, [col1 => false, col2 => 100, ..]);
$user->apps()->sync([1 => ['col1' => false], 2, 3]);
$user->apps()->syncWithoutDetaching([1, 2, 3]);
Model 屬性
class Xxxx extends Model
{
//使用軟刪除(註記刪除),需指定 deleted_at,Migration 需加上 $table->softDeletes();
use SoftDeletes;
//指定與該 Model 關聯的資料表
protected $table = 'my_table';
//告知該 table 的 PK 欄位(預設為 id 欄位)
protected $primaryKey = 'SongID';
//讓 Model 連接指定的 connection
protected $connection = 'connection-name';
//是否讓 Eloquent 來自動維護 created_at 和 updated_at 欄位
public $timestamps = false;
//日期欄位的儲存格式。'Y-m-d' or 'U' or ...
protected $dateFormat = 'U';
//需要被轉換成日期的屬性。(通常輔助 softdeletes 使用)
protected $dates = ['deleted_at'];
//「連動」,資料更新時,可連帶更新上層 updated_at 欄位
//此 Model 為 belongsTo 或 belongsToMany
protected $touches = ['post', 'model2', ...];
//可以被大量賦值的屬性。
protected $fillable = ['name'];
//不可以被大量賦值的屬性。
protected $guarded = ['price'];
//被大量撈出時,不會顯示在物件中,要暫時可見需使用 makeVisible()
protected $hidden = ['password'];
//應該在陣列中可見的屬性。(白名單)
protected $visible = ['first_name', 'last_name'];
//把指定值附加到 Model 資料陣列中。需實作 get{fieldname}Attribute()
//可參考 eloquent-serialization 官方文件
protected $appends = ['is_admin'];
//輸出時,轉化格式
//可參考 Eloquent: Mutators 官方文件
protected $casts = ['is_admin' => 'boolean','raw_json' => 'array',];
}
Queries 操作
預載入
最重要的部份,沒處理好會造成效能低落//撈取
$user->books()->get(); //預載入,在呼叫 get() 當下,就將資料取回轉成物件
$user->books; //延遲載入 (動態屬性用法),在使用到該 table、欄位 才會去 Query
//以上兩種結果相同
N + 1 問題,範例為 1對1 的 Book、Author Model,已經做好關聯
$books = App\Book::all(); //select * from books
//撈取每一本書的個別作者
foreach ($books as $book) {
echo $book->author->name;
//select * from authors where id = n
}
//依「延遲載入」的特性,使用前才 Query,所以上面 Query 了「N次」
//簡單說,就是沒在迴圈外先撈完資料,才在裡面,每缺一次資料就抓一次
解決方式:
//預載關聯 table
$books = App\Book::with('author')->get();
//預載 多個 關聯 table,A->B()、A->C()
$books = App\Book::with('author', '發行商',...)->get();
//巢狀預載入,A->B()->C()
$books = App\Book::with('author.多個聯繫方式')->get();
//預載入條件限制
//範例:多對多,N個user->N個post
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
//延遲預載入
$books = App\Book::all();
if ($someCondition) { //判斷後,再決定是否預載入關聯 table
$books->load('author', 'publisher');
}
//延遲預載入的額外條件
$books->load(['author' => function ($query) {
$query->orderBy('published_date', 'asc');
}]);
其他操作
//至少有一篇"評論"的"文章"
$posts = Post::has('comments')->get();
$posts = Post::has('comments', '>=', 3)->get(); //大於3篇
$posts = Post::has('comments.votes')->get(); //是少有一篇評論 + 被評分。(巢狀關係,votes 是 table)
//has進階用法,裡面設定 where 條件,用 whereHas、orWhereHas
$posts = Post::whereHas('comments', function ($query) { // 取得所有至少有一篇評論
$query->where('content', 'like', 'foo%'); //內容包含 foo 的文章
})->get();
DB:: 物件操作
transactions 交易模式
#方法一
DB::transaction(function () use($nickname) {
DB::table('users')->update(['nickname' => $nickname]);
DB::table('posts')->delete();
}, 重試次數);
#方法二
DB::beginTransaction();
try {
DB::...
DB::commit();
} catch (\Exception $e) {
DB::rollback();
}
問題
- 因 Migrate class name 相同,導致 rollback 出錯
Cannot declare class UpdateUsersTable, because the name is already in use
Laravel Migration、Model、DB物件 相關操作 (濃縮)
Reviewed by Wild
on
2/09/2017 03:17:00 上午
Rating:
沒有留言:
沒有Google帳號也可發表意見唷!