PHP的Yii架構中Model模型的學習教程,yiimodel
模型是 MVC 模式中的一部分, 是代表業務資料、規則和邏輯的對象。
模型是 CModel 或其子類的執行個體。模型用於保持資料以及與其相關的商務邏輯。
模型是單獨的資料對象。它可以是資料表中的一行,或者一個使用者輸入的表單。 資料對象的每個欄位對應模型中的一個屬性。每個屬性有一個標籤(label), 並且可以通過一系列規則進行驗證。
Yii 實現了兩種類型的模型:表單模型和 Active Record。二者均繼承於相同的基類 CModel。
表單模型是 CFormModel 的執行個體。表單模型用於保持從使用者的輸入擷取的資料。 這些資料經常被擷取,使用,然後丟棄。例如,在一個登入頁面中, 我們可以使用表單模型用於表示由終端使用者提供的使用者名稱和密碼資訊。
Active Record (AR) 是一種用於通過物件導向的風格抽象化資料庫訪問的設計模式。 每個 AR 對象是一個 CActiveRecord 或其子類的執行個體。代表資料表中的一行。 行中的欄位對應 AR 對象中的屬性。
可通過繼承 yii\base\Model 或它的子類定義模型類,基類yii\base\Model支援許多實用的特性:
- 屬性: 代表可像普通類屬性或數組一樣被訪問的業務資料;
- 屬性標籤: 指定屬性顯示出來的標籤;
- 塊賦值: 支援一步給許多屬性賦值;
- 驗證規則: 確保輸入資料符合所申明的驗證規則;
- 資料匯出: 允許模型資料匯出為自訂格式的數組。
屬性
模型通過 屬性 來代表業務資料,每個屬性像是模型的公有可訪問屬性, yii\base\Model::attributes() 指定模型所擁有的屬性。
可像訪問一個對象屬性一樣訪問模型的屬性:
$model = new \app\models\ContactForm;// "name" 是ContactForm模型的屬性$model->name = 'example';echo $model->name;
也可像訪問數組單元項一樣訪問屬性,這要感謝yii\base\Model支援 ArrayAccess 數組訪問 和 ArrayIterator 數組迭代器:
$model = new \app\models\ContactForm;// 像訪問數組單元項一樣訪問屬性$model['name'] = 'example';echo $model['name'];// 迭代器遍曆模型foreach ($model as $name => $value) { echo "$name: $value\n";}
定義屬性
預設情況下你的模型類直接從yii\base\Model繼承,所有 non-static public非靜態公有 成員變數都是屬性。 例如,下述ContactForm模型類有四個屬性name, email, subject and body, ContactForm 模型用來代表從HTML表單擷取的輸入資料。
namespace app\models;use yii\base\Model;class ContactForm extends Model{ public $name; public $email; public $subject; public $body;}
另一種方式是可覆蓋 yii\base\Model::attributes() 來定義屬性,該方法返回模型的屬性名稱。 例如 yii\db\ActiveRecord 返回對應資料表列名作為它的屬性名稱, 注意可能需要覆蓋魔術方法如__get(), __set()使屬性像普通對象屬性被訪問。
屬性標籤
當屬性顯示或擷取輸入時,經常要顯示內容相關標籤,例如假定一個屬性名稱為firstName, 在某些地方如表單輸入或錯誤資訊處,你可能想顯示對終端使用者來說更友好的 First Name 標籤。
可以調用 yii\base\Model::getAttributeLabel() 擷取屬性的標籤,例如:
$model = new \app\models\ContactForm;// 顯示為 "Name"echo $model->getAttributeLabel('name');
預設情況下,屬性標籤通過yii\base\Model::generateAttributeLabel()方法自動從屬性名稱產生. 它會自動將駝峰式大小寫變數名轉換為多個首字母大寫的單詞,例如 username 轉換為 Username, firstName 轉換為 First Name。
如果你不想用自動產生的標籤,可以覆蓋 yii\base\Model::attributeLabels() 方法明確指定屬性標籤,例如:
namespace app\models;use yii\base\Model;class ContactForm extends Model{ public $name; public $email; public $subject; public $body; public function attributeLabels() { return [ 'name' => 'Your name', 'email' => 'Your email address', 'subject' => 'Subject', 'body' => 'Content', ]; }}
應用支援多語言的情況下,可翻譯屬性標籤, 可在 yii\base\Model::attributeLabels() 方法中定義,如下所示:
public function attributeLabels(){ return [ 'name' => \Yii::t('app', 'Your name'), 'email' => \Yii::t('app', 'Your email address'), 'subject' => \Yii::t('app', 'Subject'), 'body' => \Yii::t('app', 'Content'), ];}
甚至可以根據條件定義標籤,例如通過使用模型的 scenario情境, 可對相同的屬性返回不同的標籤。
補充:屬性標籤是 視圖一部分,但是在模型中申明標籤通常非常方便,並可行程非常簡潔可重用代碼。
情境
模型可能在多個 情境 下使用,例如 User 模組可能會在收集使用者登入輸入,也可能會在使用者註冊時使用。 在不同的情境下,模型可能會使用不同的商務規則和邏輯,例如 email 屬性在註冊時強制要求有,但在登陸時不需要。
模型使用 yii\base\Model::scenario 屬性保持使用情境的跟蹤, 預設情況下,模型支援一個名為 default 的情境,如下展示兩種設定情境的方法:
// 情境作為屬性來設定$model = new User;$model->scenario = 'login';// 情境通過構造初始化配置來設定$model = new User(['scenario' => 'login']);
預設情況下,模型支援的情境由模型中申明的 驗證規則 來決定, 但你可以通過覆蓋yii\base\Model::scenarios()方法來自訂行為,如下所示:
namespace app\models;use yii\db\ActiveRecord;class User extends ActiveRecord{ public function scenarios() { return [ 'login' => ['username', 'password'], 'register' => ['username', 'email', 'password'], ]; }}
補充:在上述和下述的例子中,模型類都是繼承yii\db\ActiveRecord, 因為多情境的使用通常發生在Active Record 類中.
scenarios() 方法返回一個數組,數組的鍵為情境名,值為對應的 active attributes活動屬性。 活動屬性可被 塊賦值 並遵循驗證規則在上述例子中,username 和 password 在login情境中啟用,在 register 情境中, 除了 username and password 外 email也被啟用。
scenarios() 方法預設實現會返回所有yii\base\Model::rules()方法申明的驗證規則中的情境, 當覆蓋scenarios()時,如果你想在預設情境外使用新情境,可以編寫類似如下代碼:
namespace app\models;use yii\db\ActiveRecord;class User extends ActiveRecord{ public function scenarios() { $scenarios = parent::scenarios(); $scenarios['login'] = ['username', 'password']; $scenarios['register'] = ['username', 'email', 'password']; return $scenarios; }}
情境特性主要在驗證 和 屬性塊賦值 中使用。 你也可以用於其他目的,例如可基於不同的情境定義不同的 屬性標籤。
驗證規則
當模型接收到終端使用者輸入的資料,資料應當滿足某種規則(稱為 驗證規則, 也稱為 商務規則)。 例如假定ContactForm模型,你可能想確保所有屬性不為空白且 email 屬性包含一個有效郵箱地址, 如果某個屬性的值不滿足對應的商務規則,相應的錯誤資訊應顯示,以協助使用者修正錯誤。
可調用 yii\base\Model::validate() 來驗證接收到的資料, 該方法使用yii\base\Model::rules()申明的驗證規則來驗證每個相關屬性, 如果沒有找到錯誤,會返回 true,否則它會將錯誤儲存在 yii\base\Model::errors 屬性中並返回false,例如:
$model = new \app\models\ContactForm;// 使用者輸入資料賦值到模型屬性$model->attributes = \Yii::$app->request->post('ContactForm');if ($model->validate()) { // 所有輸入資料都有效 all inputs are valid} else { // 驗證失敗:$errors 是一個包含錯誤資訊的數組 $errors = $model->errors;}
通過覆蓋 yii\base\Model::rules() 方法指定模型屬性應該滿足的規則來申明模型相關驗證規則。 下述例子顯示ContactForm模型申明的驗證規則:
public function rules(){ return [ // name, email, subject 和 body 屬性必須有值 [['name', 'email', 'subject', 'body'], 'required'], // email 屬性必須是一個有效電子郵箱地址 ['email', 'email'], ];}
一條規則可用來驗證一個或多個屬性,一個屬性可對應一條或多條規則。 更多關於如何申明驗證規則的詳情請參考 驗證輸入 一節.
有時你想一條規則只在某個 情境 下應用,為此你可以指定規則的 on 屬性,如下所示:
public function rules(){ return [ // 在"register" 情境下 username, email 和 password 必須有值 [['username', 'email', 'password'], 'required', 'on' => 'register'], // 在 "login" 情境下 username 和 password 必須有值 [['username', 'password'], 'required', 'on' => 'login'], ];}
如果沒有指定 on 屬性,規則會在所有情境下應用, 在當前yii\base\Model::scenario 下應用的規則稱之為 active rule活動規則。
一個屬性只會屬於scenarios()中定義的活動屬性且在rules()申明對應一條或多條活動規則的情況下被驗證。
塊賦值
塊賦值只用一行代碼將使用者所有輸入填充到一個模型,非常方便, 它直接將輸入資料對應填充到 yii\base\Model::attributes 屬性。 以下兩段代碼效果是相同的,都是將終端使用者輸入的表單資料賦值到 ContactForm 模型的屬性, 明顯地前一段塊賦值的代碼比後一段代碼簡潔且不易出錯。
$model = new \app\models\ContactForm;$model->attributes = \Yii::$app->request->post('ContactForm');$model = new \app\models\ContactForm;$data = \Yii::$app->request->post('ContactForm', []);$model->name = isset($data['name']) ? $data['name'] : null;$model->email = isset($data['email']) ? $data['email'] : null;$model->subject = isset($data['subject']) ? $data['subject'] : null;$model->body = isset($data['body']) ? $data['body'] : null;
安全屬性
塊賦值只應用在模型當前yii\base\Model::scenario情境yii\base\Model::scenarios()方法 列出的稱之為 安全屬性 的屬性上,例如,如果User模型申明以下情境, 噹噹前情境為login時候,只有username and password 可被塊賦值,其他屬性不會被賦值。
public function scenarios(){ return [ 'login' => ['username', 'password'], 'register' => ['username', 'email', 'password'], ];}
補充: 塊賦值只應用在安全屬性上,因為你想控制哪些屬性會被終端使用者輸入資料所修改, 例如,如果 User 模型有一個permission屬性對應使用者的許可權, 你可能只想讓這個屬性在後台介面被管理員修改。
由於預設yii\base\Model::scenarios()的實現會返回yii\base\Model::rules()所有屬性和資料, 如果不覆蓋這個方法,表示所有只要出現在活動驗證規則中的屬性都是安全的。
為此,提供一個特別的別名為 safe 的驗證器來申明哪些屬性是安全的不需要被驗證, 如下樣本的規則申明 title 和 description都為安全屬性。
public function rules(){ return [ [['title', 'description'], 'safe'], ];}
非安全屬性
如上所述,yii\base\Model::scenarios() 方法提供兩個用處:定義哪些屬性應被驗證,定義哪些屬性安全。 在某些情況下,你可能想驗證一個屬性但不想讓他是安全的,可在scenarios()方法中屬性名稱加一個驚歎號 !。 例如像如下的secret屬性。
public function scenarios(){ return [ 'login' => ['username', 'password', '!secret'], ];}
當模型在 login 情境下,三個屬性都會被驗證,但只有 username和 password 屬性會被塊賦值, 要對secret屬性賦值,必須像如下例子明確對它賦值。
$model->secret = $secret;
資料匯出
模型通常要匯出成不同格式,例如,你可能想將模型的一個集合轉成JSON或Excel格式, 匯出過程可分解為兩個步驟,第一步,模型轉換成數組;第二步,數群組轉換成所需要的格式。 你只需要關注第一步,因為第二步可被通用的資料轉換器如yii\web\JsonResponseFormatter來完成。
將模型轉換為數組最簡單的方式是使用 yii\base\Model::attributes 屬性,例如:
$post = \app\models\Post::findOne(100);$array = $post->attributes;
yii\base\Model::attributes 屬性會返回 所有 yii\base\Model::attributes() 申明的屬性的值。
更靈活和強大的將模型轉換為數組的方式是使用 yii\base\Model::toArray() 方法, 它的行為預設和 yii\base\Model::attributes 相同, 但是它允許你選擇哪些稱之為欄位的資料項目放入到結果數組中並同時被格式化。 實際上,它是匯出模型到 RESTful 網頁服務開發的預設方法,詳情請參閱響應格式.
欄位
欄位是模型通過調用yii\base\Model::toArray()產生的數組的單元名。
預設情況下,欄位名對應屬性名稱,但是你可以通過覆蓋 yii\base\Model::fields() 和/或 yii\base\Model::extraFields() 方法來改變這種行為, 兩個方法都返回一個欄位定義列表,fields() 方法定義的欄位是預設欄位,表示toArray()方法預設會返回這些欄位。extraFields()方法定義額外可用欄位,通過toArray()方法指定$expand參數來返回這些額外可用欄位。 例如如下代碼會返回fields()方法定義的所有欄位和extraFields()方法定義的prettyName and fullAddress欄位。
$array = $model->toArray([], ['prettyName', 'fullAddress']);
可通過覆蓋 fields() 來增加、刪除、重新命名和重定義欄位,fields() 方法傳回值應為數組, 數組的鍵為欄位名,數組的值為對應的可為屬性名稱或匿名函數返回的欄位定義對應的值。 特使情況下,如果欄位名和屬性定義名相同,可以省略數組鍵,例如:
// 明確列出每個欄位,特別用於你想確保資料表或模型屬性改變不會導致你的欄位改變(保證後端的API相容).public function fields(){ return [ // 欄位名和屬性名稱相同 'id', // 欄位名為 "email",對應屬性名稱為 "email_address" 'email' => 'email_address', // 欄位名為 "name", 值通過PHP代碼返回 'name' => function () { return $this->first_name . ' ' . $this->last_name; }, ];}// 過濾掉一些欄位,特別用於你想繼承父類實現並不想用一些敏感欄位public function fields(){ $fields = parent::fields(); // 去掉一些包含敏感資訊的欄位 unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); return $fields;}
警告:由於模型的所有屬性會被包含在匯出數組,最好檢查資料確保沒包含敏感性資料, 如果有敏感性資料,應覆蓋 fields() 方法過濾掉,在上述列子中,我們選擇過濾掉 auth_key, password_hash and password_reset_token。
最佳實務
模型是代表業務資料、規則和邏輯的中心地方,通常在很多地方重用, 在一個設計良好的應用中,模型通常比控制器代碼多。
歸納起來,模型:
- 可包含屬性來展示業務資料;
- 可包含驗證規則確保資料有效和完整;
- 可包含方法實現商務邏輯;
- 不應直接存取請求,session和其他環境資料,這些資料應該由控制器傳入到模型;
- 應避免嵌入HTML或其他展示代碼,這些代碼最好在 視圖中處理;
- 單個模型中避免太多的情境.
在開發大型複雜系統時應經常考慮最後一條建議, 在這些系統中,模型會很大並在很多地方使用,因此會包含需要規則集和商務邏輯, 最後維護這些模型代碼成為一個噩夢,因為一個簡單修改會影響好多地方, 為確保模型好維護,最好使用以下策略:
定義可被多個 應用主體 或 模組 共用的模型基類集合。 這些模型類應包含通用的最小規則集合和邏輯。
在每個使用模型的 應用主體 或 模組中, 通過繼承對應的模型基類來定義具體的模型類,具體模型類包含應用主體或模組指定的規則和邏輯。
例如,在進階應用程式模板,你可以定義一個模型基類common\models\Post, 然後在前台應用中,定義並使用一個繼承common\models\Post的具體模型類frontend\models\Post, 在後台應用中可以類似地定義backend\models\Post。 通過這種策略,你清楚frontend\models\Post只對應前台應用,如果你修改它,就無需擔憂修改會影響後台應用。
您可能感興趣的文章:
- 詳解PHP的Yii架構中內建的前端資源套件的使用
- 簡介PHP的Yii架構中緩衝的一些進階用法
- 深入解析PHP的Yii架構中的緩衝功能
- PHP的Yii架構中View視圖的使用進階
- PHP的Yii架構中建立視圖和渲染視圖的方法詳解
- 詳解PHP的Yii架構中的Controller控制器
- PHP的Yii架構中移除組件所綁定的行為的方法
- PHP的Yii架構中行為的定義與Binder 方法講解
- 深入講解PHP的Yii架構中的屬性(Property)
- 詳解PHP的Yii架構中擴充的安裝與使用
http://www.bkjia.com/PHPjc/1117073.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1117073.htmlTechArticlePHP的Yii架構中Model模型的學習教程,yiimodel 模型是 MVC 模式中的一部分, 是代表業務資料、規則和邏輯的對象。 模型是 CModel 或其子類的實...