模型是 MVC 架構中代表業務資料、規則與邏輯的核心元件。Yii 透過 yii\base\Model 基類提供豐富功能,支援表單模型與 Active Record 兩大類型。本文深入探討模型屬性管理、驗證機制、場景應用、區塊賦值、資料匯出等核心概念。
| 類型 | 基類 | 用途 | 特點 |
|---|---|---|---|
| 表單模型 | yii\base\Model | 使用者輸入資料處理 | 資料暫時性、經常獲取使用後丟棄 |
| Active Record | yii\db\ActiveRecord | 資料庫資料物件映射 | 對應資料表列、支援關聯查詢、資料持久化 |
方式一:公開成員變數(預設)
namespace app\models;
use yii\base\Model;
class ContactForm extends Model {
public $name;
public $email;
public $subject;
public $body;
}
方式二:覆寫 attributes() 方法
// ActiveRecord 自動對應資料表欄位
public function attributes() {
return ['id', 'username', 'email', 'created_at', 'updated_at'];
}
$model = new ContactForm();
// 物件屬性存取
$model->name = 'example';
echo $model->name;
// 陣列存取(支援 ArrayAccess、ArrayIterator)
$model['name'] = 'example';
echo $model['name'];
// 迭代器遍歷
foreach ($model as $name => $value) {
echo "$name: $value\n";
}
駝峰式命名自動轉換:firstName → First Name,username → Username
public function attributeLabels() {
return [
'name' => 'Your name',
'email' => 'Your email address',
'subject' => 'Subject',
'body' => 'Content',
];
}
public function attributeLabels() {
return [
'name' => \Yii::t('app', 'Your name'),
'email' => \Yii::t('app', 'Your email address'),
];
}
public function attributeLabels() {
return [
'email' => $this->scenario === 'register' ? 'Register Email' : 'Login Email',
];
}
// 方式一:屬性指派 $model = new User; $model->scenario = 'login'; // 方式二:建構子配置 $model = new User(['scenario' => 'login']);
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord {
public function scenarios() {
return [
'login' => ['username', 'password'],
'register' => ['username', 'email', 'password'],
'profile' => ['username', 'email', 'avatar', 'bio'],
];
}
}
public function scenarios() {
$scenarios = parent::scenarios();
$scenarios['login'] = ['username', 'password'];
$scenarios['register'] = ['username', 'email', 'password'];
return $scenarios;
}
$model = new ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) {
// 驗證通過
} else {
$errors = $model->errors; // 錯誤訊息陣列
}
public function rules() {
return [
// 必填驗證
[['name', 'email', 'subject', 'body'], 'required'],
// Email 格式驗證
['email', 'email'],
// 字串長度
[['name', 'subject'], 'string', 'max' => 100],
// 數值範圍
['age', 'integer', 'min' => 18, 'max' => 100],
// 正則表達式
['username', 'match', 'pattern' => '/^[a-zA-Z0-9_]+$/'],
// 自訂驗證方法
['password', 'validatePassword'],
// 條件式驗證(特定場景)
[['username', 'email', 'password'], 'required', 'on' => 'register'],
[['username', 'password'], 'required', 'on' => 'login'],
];
}
| 類別 | 驗證器 | 用途 |
|---|---|---|
| 基本 | required, safe, filter, trim | 必填、安全屬性、過濾、修剪 |
| 型別 | string, number, integer, boolean, date | 資料型別驗證 |
| 格式 | email, url, ip, match | 特定格式驗證 |
| 比較 | compare, exist, unique | 比較、存在性、唯一性 |
| 範圍 | in, range, between | 值域驗證 |
| 檔案 | file, image | 上傳檔案驗證 |
| 進階 | captcha, captchaAction | 驗證碼 |
// 區塊賦值 - 簡潔安全
$model->attributes = \Yii::$app->request->post('ContactForm');
// 手動賦值 - 複雜易錯
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
// ...
scenarios() 定義的活動屬性可被區塊賦值safe 驗證器明確宣告無需驗證但可賦值的屬性public function rules() {
return [
[['title', 'description'], 'safe'], // 宣告為安全屬性
];
}
需驗證但不允許區塊賦值,在屬性名前加 !:
public function scenarios() {
return [
'login' => ['username', 'password', '!secret'],
];
}
// 僅能明確賦值
$model->secret = $secretValue;
$post = Post::findOne(100); $array = $post->attributes; // 所有屬性值
// 指定欄位 $array = $model->toArray(); // 預設欄位 $array = $model->toArray([], ['prettyName']); // 含額外欄位
public function fields() {
return [
'id', // 同名屬性
'email' => 'email_address', // 重新命名
'name' => function () { // 計算欄位
return $this->first_name . ' ' . $this->last_name;
},
];
}
public function extraFields() {
return [
'fullAddress' => function () {
return $this->address . ', ' . $this->city;
},
];
}
// 使用方式
$array = $model->toArray([], ['fullAddress']);
public function fields() {
$fields = parent::fields();
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}
// 共用基礎模型層
namespace common\models;
class Post extends \yii\db\ActiveRecord {
// 通用規則、關聯、方法
}
// 前台應用專用模型
namespace frontend\models;
class Post extends \common\models\Post {
// 前台特定規則、邏輯
}
// 後台應用專用模型
namespace backend\models;
class Post extends \common\models\Post {
// 後台特定規則、邏輯
}
解決:在 rules() 中加入 [['attr'], 'safe'] 或確保屬性出現在驗證規則中。
解決:明確指定 'on' => 'scenarioName' 限制適用場景。
解決:覆寫 fields() 移除敏感欄位,或使用 DTO/Resource 類別控制輸出。
Yii 模型系統提供完整的資料封裝、驗證與轉換機制。核心掌握點包括:
遵循最佳實踐原則,建立清晰的模型層次架構,能讓應用程式在規模增長時保持可維護性與擴充性。