Laravel Eloquent ORM - fantasy0107/notes GitHub Wiki
用物件的方式操作 DB record
Model
概念
- 設定下面 properties
- relation
- append 當需要再 append
- query 部分丟到 Repository
產生檔案
php artisan make:model User
和 migration + controller + controller resourceful 一起產生
php artisan make:model Company -mcr
-m migration
-c controller
-r resourceful
property
class User extends Model
{
protected $table = 'my_flights'; // 使用的 table
protected $primaryKey = 'id'; // 預設就是 id
public $incrementing = true; // 預設主鍵會自動增加
protected $connection = 'connection-name'; //沒有就用預設的
public $timestamps = true; // 預設會有 created_at 和 updated_at
protected $casts = ['is_admin' => 'boolean']; //轉換資料型態
protected $appends = ['a']; // 每次取時要附加的 accessors
protected $hidden = ['password']; //要隱藏的欄位
protected $visible = ['id']; //要顯示的欄位和 hidden 擇一
protected $fillable = ['name']; //可以存值
protected $guarded = ['id']; //不能存值和 fillable 擇一
protected $with = ['user']; // 要 eager load 的
}
Database Connection
protected $connection = 'connection-name';
跨 DB
protected $table = 'otherDBname.my_flights'; // 使用的 table
切換成用 write connection
Models::onWriteConnection();
append 的時候
要注意有些是需要跑 DB query的不要自動 append 上去
參考資料
Eloquent: Getting Started
[Laravel API] (https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Model.html)
API Resources
轉換 Eloquetn 回傳欄位
方便同一個 model 或 collection
需要再不同地方顯示不同column
產生檔案
命名: modelName + Resource
php artisan make:resource UserResource
檔案位置
App\Http\Resources
使用
model
return new UserResource(User::find(1));
collection
return UserResource::collection(User::all());
結構
$this->column 是取得對應的eloquent欄位
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
參考資料
Laravel API Resources — Under the hood
Frequently Asked Questions About Laravel based APIs
Defining Models
ActiveRecord implementation
database table => Model
config/database.php
產生model + migration
php artisan make:model User --migration
Primary Keys
- $primaryKey
- $incrementing
- $keyType
Timestamps
class Flight extends Model
{
public $timestamps = false;
}
- 設定timestamp格式
class Flight extends Model
{
protected $dateFormat = 'U';
}
- 設定創造和更新時間
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
Database Connection
class Flight extends Model
{
protected $connection = 'connection-name';
}
Retrieving Models
Query Builder
collections
- all + get
- collection helper
- loop like an array
chunking Results
- 當需要處理大量的資料
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//do something
}
});
- 使用cursor - 可以減少記憶體的使用當需要處理大量資料
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
// do something
}
Mutators
- Accessor - Model 自定義屬性
- Mutator - Model 儲存值時轉換輸入進來的值
- Date Mutators - Model 轉換這些欄位為 date
- Attribute Casting - Model property 轉換
Accessor
getPropertyNameAttribute
studly cased name
class User extends Model
{
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
}
Query Scopes
Global Scopes
增加所有對model query的限制
怎麼寫Global Scopes
//範例
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class AgeScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('age', '>', 200);
}
}
使用global scopes
<?php
namespace App;
use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
}
User::all() => select * from `users` where `age` > 200
匿名 Global Scopes
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
}
移除 Global Scopes
// 移除全部 global scopes...
User::withoutGlobalScopes()->get();
// 移除部分 global scopes...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
Local Scopes
定義可以重複使用的限制
定義
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
public function scopeActive($query)
{
return $query->where('active', 1);
}
}
使用
$users = App\User::popular()->active()->orderBy('created_at')->get();
動態Scopes
有的時候需要傳入變數
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
$users = App\User::ofType('admin')->get();
Deleting Models
- 刪除model
$flight = App\Flight::find(1);
$flight->delete();
- 用key刪除存在的model
App\Flight::destroy(1); //單一
App\Flight::destroy([1, 2, 3]); //複數
App\Flight::destroy(1, 2, 3); //複數
- 藉由query刪除model
$deletedRows = App\Flight::where('active', 0)->delete();
Soft Deleting(軟刪除)
- 當不是真的想要從資料庫中刪除這筆資料
- deleted_at => 非null代表已經被軟刪除
- 要使用軟刪除 => Illuminate\Database\Eloquent\SoftDeletes + deleted_at必須在資料庫中
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;//軟刪除
class Flight extends Model
{
use SoftDeletes;
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
}
- Schema builder helper
Schema::table('flights', function ($table) {
$table->softDeletes();
});
- 判斷model是否被軟刪除
if ($flight->trashed()) {
//
}
Querying Soft Deleted Models
- 包括Soft Deleted Models
$flights = App\Flight::withTrashed()
->where('account_id', 1)
->get();
- withTrashed 可以用在relationship Query
$flight->history()->withTrashed()->get();
- 只擷取軟刪除的model
$flights = App\Flight::onlyTrashed()
->where('airline_id', 1)
->get();
- 回復軟刪除的model
$flight->restore(); //單一
App\Flight::withTrashed()
->where('airline_id', 1)
->restore();// 複數
$flight->history()->restore(); //關聯
- 永久的刪除model
// Force deleting a single model instance...
$flight->forceDelete();//單一
// Force deleting all related models...
$flight->history()->forceDelete(); //關聯
Mutator
class User extends Model
{
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
}
Date Mutators
取得 $dates 內設定的 property 回傳都會是 Carbon instances
當儲存以下的值時會自動轉成正確的格式儲存進DB
- UNIX timestamp
- date string (Y-m-d)
- date-time string
- DateTime / Carbon instance
class User extends Model
{
protected $dates = [
'created_at',
'updated_at',
'deleted_at'
];
}
Attribute Casting
支援的轉換格式
- integer
- real
- float
- double
- string
- boolean
- object
- array
- collection
- date
- datetime
- timestamp
class User extends Model
{
protected $casts = [
'is_admin' => 'boolean',
];
}
轉換 is_admin 為 boolean value, 如果原本是 DB 中是 0 或 1 就會轉換成相對應的 Boolean value
注意
轉換 DB 欄位中的 serialized JSON
當serialize json 被轉換成 array 那麼重新儲存值後更新會被轉換回 serialize json
$user = App\User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save();
概念
- 不要先將 Accessors append 要使用的時候再append
Events
Model lifecycle
- retrieved - 當存在的model被取得
- creating/created - 在資料庫中存在的model且save被呼叫
- updating/updated -同 creating/created
- saving
- saved
- deleting
- deleted
- restoring
- restored
//範例
<?php
namespace App;
use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
Observers
當你需要在MODEL上監聽很多事件
產生Observer
//範例
<?php
namespace App\Observers;
use App\User;
class UserObserver
{
/**
* Listen to the User created event.
*
* @param \App\User $user
* @return void
*/
public function created(User $user)
{
//
}
/**
* Listen to the User deleting event.
*
* @param \App\User $user
* @return void
*/
public function deleting(User $user)
{
//
}
}
註冊Observer
//範例
<?php
namespace App\Providers;
use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}
Inserting & Updating Models
inserts
- new出model
- 給予model所需要的值
- save
$flight = new Flight;//new出flight model
$flight->name = $request->name;
$flight->save();//存入DB + created_at和updated_at會被更新
updates
- 取得需要的model
- 對欄位進行更新
- save
$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();
mass updates
- 對大量的model進行更新
App\Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);//是個array columneName => value
mass assignment
- fillable(可以儲存值) / guardered(不能儲存值)
class Flight extends Model
{
//The attributes that are mass assignable.
protected $fillable = ['name'];
}
- 當有model instance可以用fill來填充陣列的屬性
$flight->fill(['name' => 'Flight 22']);
Guarding Attributes
- 不能被儲存值
class Flight extends Model
{
//The attributes that aren't mass assignable.
protected $guarded = ['price'];
}
- 所有屬性都不能被儲存值
protected $guarded = [];
other creation methods
firstOrCreate/ firstOrNew
- 找出或創造model
- 差別在firstOrNew需要save
// Retrieve flight by name, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// Retrieve flight by name, or create it with the name and delayed attributes...
$flight = App\Flight::firstOrCreate(
['name' => 'Flight 10'], ['delayed' => 1]
);
// Retrieve by name, or instantiate...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
// Retrieve by name, or instantiate with the name and delayed attributes...
$flight = App\Flight::firstOrNew(
['name' => 'Flight 10'], ['delayed' => 1]
);
updateOrCreate
- 更新或創造model
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
// 不需要save() 就可以儲存進DB
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
Serialization
Appending Values To JSON
\\Mode
...
public function getIsAdminAttribute()
{
return $this->attributes['admin'] == 'yes';
}
...
\\Controller
return $user->append('is_admin')->toArray();
Relationships
問題 hasManyThrought 的參數
// 拿到 countries 的 posts
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
public function posts()
{
return $this->hasManyThrough(
'App\Post', // 目標的model
'App\User', // 中介的model
'country_id', // Foreign key on users table... // 中介model 參考的 foregin key
'user_id', // Foreign key on posts table... // 目標model 參考的 foregin key
'id', // Local key on countries table... // 主要model 的 primary key
'id' // Local key on users table... // 中介model 的 primary key
);
}