模型类是数据模型的基类.此类继承了组件类,实现了3个接口

  1. 先介绍一下模型类前面的大量注释说了什么:
  2.  
  3. * 模型类是数据模型的基类.此类继承了组件类,实现了3个接口
  4. * 实现了IteratorAggregate(聚合式迭代器)接口,实现了ArrayAccess接口,可以像数组一样访问对象,这两个接口是php自带
  5. * Arrayable接口是yii2框架自带
  6. * 模型实现了以下常用功能:
  7. *
  8. * - 属性声明: 默认情况下,每个公共类成员都被认为是模型属性
  9. * - 属性标签: 每个属性可以与用于显示目的的标签相关联。
  10. * - 大量的属性分配
  11. * - 基于场景的验证
  12. *
  13. * 在执行数据验证时,模型还引发下列事件:
  14. *
  15. * - [[EVENT_BEFORE_VALIDATE]]: 在开始时提出的事件 [[validate()]]
  16. * - [[EVENT_AFTER_VALIDATE]]: 结束时提出的事件[[validate()]]
  17. *
  18. * 您可以直接使用模型存储模型数据, 或延长定制.
  1. <?php
  2. /**
  3. * @property \yii\validators\Validator[] $activeValidators 使用场景效验[[scenario]],此属性是只读的
  4. * @property array $attributes 属性值键值对方式 (name => value).
  5. * @property array $errors 所有属性的错误数组. 数组为空表示没有错误. 结果是一个二维数组
  6. * [[getErrors()]]获取详细错误信息,此属性是只读的
  7. * @property array $firstErrors 第一个错误,数组的键是属性名, 数组的值是错误信息,空数组表示没有错误,此属性只读
  8. * @property ArrayIterator $iterator 遍历列表中的项的迭代器,此属性只读.
  9. * @property string $scenario 模型所在的场景.默认是[[SCENARIO_DEFAULT]].
  10. * @property ArrayObject|\yii\validators\Validator[] $validators 在模型中定义的所有方法,此属性只读.
  11. *
  12. * @author Qiang Xue <qiang.xue@gmail.com>
  13. * @since 2.0
  14. */
  15. class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable
  16. {
  17. //自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits,此处就使用了trait
  18. use ArrayableTrait;
  19.  
  20. /**
  21. * 默认场景名
  22. */
  23. const SCENARIO_DEFAULT = 'default';
  24. /**
  25. * @event 模型事件先被调用验证,调用[[validate()]]这个方法. You may set
  26. * 可以设置[[ModelEvent::isValid]] 的isValid属性为false来阻止验证.
  27. */
  28. const EVENT_BEFORE_VALIDATE = 'beforeValidate';
  29. /**
  30. * @event 验证[[validate()]]后执行的事件
  31. */
  32. const EVENT_AFTER_VALIDATE = 'afterValidate';
  33.  
  34. /**
  35. * @var array 存放验证错误的数组,键是属性名,值是有关错误的数组 (attribute name => array of errors)
  36. */
  37. private $_errors;
  38. /**
  39. * @var ArrayObject list 验证器集合
  40. */
  41. private $_validators;
  42. /**
  43. * @var string 当前场景
  44. */
  45. private $_scenario = self::SCENARIO_DEFAULT;
  46.  
  47. /**
  48. * 返回属性的验证规则.
  49. *
  50. * 验证规则通过 [[validate()]] 方法检验属性是否是有效的
  51. * 子类应该覆盖这个方法来声明不同的验证规则
  52. *
  53. * 每个验证规则都是下列结构的数组:
  54. *
  55. * ```php
  56. * [
  57. * ['attribute1', 'attribute2'],
  58. * 'validator type',
  59. * 'on' => ['scenario1', 'scenario2'],
  60. * //...other parameters...
  61. * ]
  62. * ```
  63. *
  64. * where
  65. *
  66. * - 属性集合: 必选, 指定要验证的属性数组, 对于单个属性,你可以直接传递字符串;
  67. * - 验证类型: 必选, 指定要使用的验证. 它可以是一个内置验证器的名字,
  68. * 模型类的方法名称, 匿名函数, 或验证器类的名称.
  69. * - on: 可选参数, 是一个数组,表示在指定场景使用,没设置,表示应用于所有的场景
  70. * - 额外的名称-值对可以指定初始化相应的验证特性.
  71. *
  72. * 一个验证器可以是一个类的对象延伸扩展[[Validator]], 或模型类方法*(*内置验证器*),具有以下特征:
  73. *
  74. * ```php
  75. * // $params 引用给验证规则的参数
  76. * function validatorName($attribute, $params)
  77. * ```
  78. *
  79. * 上面的 `$attribute` 指当前正在验证的属性 。。。
  80. * `$params` 包含一个数组验证配置选项,例如:当字符串验证时的max属性,验证当前的属性值
  81. * 可以访问为 `$this->$attribute`. 注意 `$` before `attribute`; 这是取变量$attribute的值和使用它作为属性的名称访问
  82. *
  83. * Yii提供了一套[[Validator::builtInValidators|built-in validators]].
  84. * 每一个都有别名,可以在指定验证规则时使用
  85. *
  86. * 看下面的一些例子:
  87. *
  88. * ```php
  89. * [
  90. * // 内置 "required" 验证器
  91. * [['username', 'password'], 'required'],
  92. * // 内置 "string" 验证器 用长度区间定制属性
  93. * ['username', 'string', 'min' => 3, 'max' => 12],
  94. * // 内置 "compare" 验证器,只能在 "register" 场景中使用
  95. * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'],
  96. * // 一个内置验证器 "authenticate()"方法被定义在模型类里
  97. * ['password', 'authenticate', 'on' => 'login'],
  98. * // 一个验证器的类 "DateRangeValidator"
  99. * ['dateRange', 'DateRangeValidator'],
  100. * ];
  101. * ```
  102. *
  103. * 注意,为了继承定义在父类中的规则, 一个子类应该使用函数合并父类的规则,例如array_merge()这个函数
  104. *
  105. * @return array validation rules返回验证规则数组
  106. * @see scenarios()
  107. */
  108. public function rules()
  109. {
  110. return [];
  111. }
  112.  
  113. /**
  114. * 返回一个场景列表和每个场景对应的属性,此属性是活动属性
  115. * 一个场景中的属性只在当前场景中被验证
  116. * 返回的数组应该是下列格式的:
  117. *
  118. * ```php
  119. * [
  120. * 'scenario1' => ['attribute11', 'attribute12', ...],
  121. * 'scenario2' => ['attribute21', 'attribute22', ...],
  122. * ...
  123. * ]
  124. * ```
  125. *
  126. * 默认情况下,活动属性被认为是安全的,并且可以被赋值
  127. * 如果一个属性不应该被赋值 (因此认为不安全),
  128. * 请用感叹号前缀属性 (例如: `'!rank'`).
  129. *
  130. * 此方法默认返回声明中的所有属性 [[rules()]]
  131. * 一个特殊的场景被称为默认场景[[SCENARIO_DEFAULT]] 将会包含在rules()规则里的所有的属性
  132. * 每个场景将与正在应用于场景的验证规则进行验证的属性关联
  133. *
  134. * @return array 返回一个数组和相应的属性列表
  135. */
  136. public function scenarios()
  137. {
  138. //在场景数组里先把默认场景放入
  139. $scenarios = [self::SCENARIO_DEFAULT => []];
  140. //获取所有场景迭代,getValidators()获取关于验证类对象的数组
  141. foreach ($this->getValidators() as $validator) {
  142. //$validator->on的值是数组,获得所有应用的场景名字
  143. foreach ($validator->on as $scenario) {
  144. $scenarios[$scenario] = [];
  145. }
  146. //$validator->on的值是数组,获得当前不使用的场景的名字
  147. foreach ($validator->except as $scenario) {
  148. $scenarios[$scenario] = [];
  149. }
  150. }
  151. //$names获得了所有的场景
  152. $names = array_keys($scenarios);
  153.  
  154. foreach ($this->getValidators() as $validator) {
  155. if (empty($validator->on) && empty($validator->except)) {
  156. //on为空数组,except也是空数组,表示验证规则中没有设置场景,则把验证规则运用到所有的场景
  157. foreach ($names as $name) {
  158. foreach ($validator->attributes as $attribute) {
  159. //把所有模型验证属性添加到每个场景
  160. $scenarios[$name][$attribute] = true;
  161. }
  162. }
  163. } elseif (empty($validator->on)) {
  164. //on为空,except不为空,表示除了except场景外,应用于所有的场景
  165. foreach ($names as $name) {
  166. if (!in_array($name, $validator->except, true)) {
  167. //找到不在except中的场景,放进场景属性数组,表示在其它场景中验证
  168. foreach ($validator->attributes as $attribute) {
  169. $scenarios[$name][$attribute] = true;
  170. }
  171. }
  172. }
  173. } else {
  174. //on不为空,在on场景中验证这些属性
  175. foreach ($validator->on as $name) {
  176. foreach ($validator->attributes as $attribute) {
  177. $scenarios[$name][$attribute] = true;
  178. }
  179. }
  180. }
  181. }
  182.  
  183. //使每个场景名对应一个属性数组,$scenarios的键是场景名
  184. foreach ($scenarios as $scenario => $attributes) {
  185. if (!empty($attributes)) {
  186. $scenarios[$scenario] = array_keys($attributes);
  187. }
  188. }
  189. //场景名对应属性名的数组
  190. return $scenarios;
  191. }
  192.  
  193. /**
  194. * 返回表单的名称,就是这个 model 的类名.
  195. *
  196. * 在一个模型中表单的name值经常被使用在 [[\yii\widgets\ActiveForm]] 决定如何命名属性的输入字段
  197. * 如果表单的name值是A,表单元素属性名是b,则表单元素的name值为"A[b]"
  198. * 如果表单的name值为空字符串,则表单元素的name为"b"
  199. *
  200. * 上述命名模式的目的是针对包含多个不同模型的表单,比较容易区分不同模型的不同属性
  201. * 每个模型的属性被分组在后的数据的子数组中,它是更容易区分它们。
  202. *
  203. * 默认情况下,此方法返回模型类名 (不包含命名空间)
  204. * 你可以覆盖此方法,当一个表单中有多个模型时
  205. *
  206. * @return string the form name of this model class.模型类的名字
  207. * @see load()
  208. */
  209. public function formName()
  210. {
  211. //ReflectionClass是php中的扩展反射类
  212. $reflector = new ReflectionClass($this);
  213. //getShortName()返回不带命名空间的类名
  214. return $reflector->getShortName();
  215. }
  216.  
  217. /**
  218. * 返回一个属性列表
  219. * 默认情况下,此方法返回类的所有公共非静态属性。
  220. * 可以覆盖此方法返回你想要的属性
  221. * @return array list of attribute names.
  222. */
  223. public function attributes()
  224. {
  225. //获取这个类的相关信息
  226. $class = new ReflectionClass($this);
  227. $names = [];
  228. //遍历这个类的每一个属性,如果这个属性是公共的,就把它放入name数组中
  229. foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
  230. //不是public并且不是static
  231. if (!$property->isStatic()) {
  232. $names[] = $property->getName();
  233. }
  234. }
  235.  
  236. return $names;
  237. }
  238.  
  239. /**
  240. * 返回属性的标签
  241. *
  242. * 属性标签主要用于显示. 例如, `firstName`属性将会显示成`First Name`标签,可以有友好的展示给终端用户
  243. *
  244. * 默认的标签生成是使用 [[generateAttributeLabel()]]这个方法
  245. * 此方法允许您显式指定属性标签.
  246. *
  247. * 注意,为了继承父类中定义的标签, 子类标签可以使用`array_merge()`与父类标签合并
  248. *
  249. * @return array attribute labels (name => label)
  250. * @see generateAttributeLabel()
  251. */
  252. public function attributeLabels()
  253. {
  254. return [];
  255. }
  256.  
  257. /**
  258. * 返回属性提示
  259. *
  260. * 属性提示主要用于显示. 例如,`isPublic`这个属性可以用来描述“未登录用户的帖子是否应该可见”
  261. * 它提供了用户友好的描述属性的含义,并可以显示给最终用户.
  262. *
  263. * 如果省略了显式声明,则不会生成标记提示
  264. *
  265. * 注意,为了继承父类中定义的标签, 子类标签可以使用`array_merge()`与父类标签合并.
  266. *
  267. * @return array attribute hints (name => hint)
  268. * @since 2.0.4
  269. */
  270. public function attributeHints()
  271. {
  272. return [];
  273. }
  274.  
  275. /**
  276. * 执行数据验证.
  277. *
  278. * 此方法执行适用于当前场景的验证规则 [[scenario]].
  279. * 下列标准用于判断规则是否适用:
  280. *
  281. * - 规则必须与当前场景相关的属性关联;
  282. * - 规则在所处的情况下必须是有效的,
  283. *
  284. * validate()在执行前会先执行 [[beforeValidate()]] ,
  285. * validate()执行后会执行 [[afterValidate()]]
  286. * 如果[[beforeValidate()]] 返回false,接下来的验证将被取消
  287. *
  288. * 验证期间发现的错误可以通过 [[getErrors()]],[[getFirstErrors()]] and [[getFirstError()]]获得错误信息,
  289. *
  290. * @param array $attributeNames 一个应该被验证的属性列表
  291. * 若$attributeNames 为空,这意味着在适用的验证规则中列出的任何属性都应该经过验证。
  292. * @param boolean $clearErrors 表示在执行验证前是否先清除错误 [[clearErrors()]]
  293. * @return boolean 验证是否成功无任何错误.
  294. * @throws InvalidParamException 不知道当前场景时会抛出异常.
  295. */
  296. public function validate($attributeNames = null, $clearErrors = true)
  297. {
  298. if ($clearErrors) {
  299. //清除所有的错误
  300. $this->clearErrors();
  301. }
  302.  
  303. if (!$this->beforeValidate()) {
  304. //没通过before验证就返回false
  305. return false;
  306. }
  307. //返回当前活动的场景
  308. $scenarios = $this->scenarios();
  309. //返回此模型应用的场景
  310. $scenario = $this->getScenario();
  311. if (!isset($scenarios[$scenario])) {
  312. //若当前活动的场景不在此模型中,抛出异常
  313. throw new InvalidParamException("Unknown scenario: $scenario");
  314. }
  315.  
  316. if ($attributeNames === null) {
  317. //属性数组为空,自动查找当前场景下的安全属性,并返回这些属性
  318. $attributeNames = $this->activeAttributes();
  319. }
  320.  
  321. //$this->getActiveValidators()返回Validator对象数组
  322. foreach ($this->getActiveValidators() as $validator) {
  323. //通过Validator对象验证属性
  324. $validator->validateAttributes($this, $attributeNames);
  325. }
  326. //验证的后置方法
  327. $this->afterValidate();
  328. //没有错误就返回真
  329. return !$this->hasErrors();
  330. }
  331.  
  332. /**
  333. * 在验证前被验证
  334. * 默认的实现提出了一个` beforevalidate `的事件
  335. * 验证之前,您可以重写此方法进行初步检查。
  336. * 请确保调用父实现,然后就可以引发此事件。
  337. * @return boolean是否应执行接下来的验证,默认是真
  338. * 如果返回false,则验证将停止,该模型被认为是无效的
  339. */
  340. public function beforeValidate()
  341. {
  342. //这个不说了ModelEvent里一个方法都木有
  343. $event = new ModelEvent;
  344. $this->trigger(self::EVENT_BEFORE_VALIDATE, $event);
  345.  
  346. return $event->isValid;
  347. }
  348.  
  349. /**
  350. * 验证执行后被调用
  351. * 废话不多说了,可以覆盖,记得调用父类方法
  352. */
  353. public function afterValidate()
  354. {
  355. $this->trigger(self::EVENT_AFTER_VALIDATE);
  356. }
  357.  
  358. /**
  359. * 返回声明在 [[rules()]]中的验证.
  360. *
  361. * 此方法和 [[getActiveValidators()]] 不同,[[getActiveValidators()]]只返回当前场景的验证
  362. *
  363. * 由于该方法返回一个数组对象的对象,你可以操纵它通过插入或删除验证器(模型行为的有用)。
  364. * For example,
  365. *
  366. * ```php
  367. * $model->validators[] = $newValidator;
  368. * ```
  369. *
  370. * @return ArrayObject|\yii\validators\Validator[] 返回在模型中定义的所有验证器
  371. */
  372. public function getValidators()
  373. {
  374. if ($this->_validators === null) {
  375. $this->_validators = $this->createValidators();
  376. }
  377. return $this->_validators;
  378. }
  379.  
  380. /**
  381. * Returns the validators applicable to the current [[scenario]].
  382. * @param string $attribute the name of the attribute whose applicable validators should be returned.
  383. * If this is null, the validators for ALL attributes in the model will be returned.
  384. * @return \yii\validators\Validator[] the validators applicable to the current [[scenario]].
  385. */
  386. public function getActiveValidators($attribute = null)
  387. {
  388. $validators = [];
  389. $scenario = $this->getScenario();
  390. foreach ($this->getValidators() as $validator) {
  391. if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
  392. $validators[] = $validator;
  393. }
  394. }
  395. return $validators;
  396. }
  397.  
  398. /**
  399. * 根据 [[rules()]]里的验证规则创建一个验证对象.
  400. * 和 [[getValidators()]]不一样, 每次调用此方法,一个新的列表验证器将返回。
  401. * @return ArrayObject validators
  402. * @throws InvalidConfigException 如果任何验证规则配置无效,抛出异常
  403. */
  404. public function createValidators()
  405. {
  406. $validators = new ArrayObject;
  407. foreach ($this->rules() as $rule) {
  408. //遍历规则中的每一项
  409. if ($rule instanceof Validator) {
  410. ///如果规则属于Validator对象,添加入数组对象
  411. $validators->append($rule);
  412. } elseif (is_array($rule) && isset($rule[0], $rule[1])) {
  413. //如果子规则是数组,创建一个验证器类,把验证的类型,模型,属性名,验证属性的初始值传入
  414. $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
  415. //把创建的对象加入数组对象
  416. $validators->append($validator);
  417. } else {
  418. //抛出规则必须包含属性名和验证类型
  419. throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
  420. }
  421. }
  422. return $validators;
  423. }
  424.  
  425. /**
  426. * 检查属性是否在当前场景中被应用
  427. * This is determined by checking if the attribute is associated with a
  428. * [[\yii\validators\RequiredValidator|required]] validation rule in the
  429. * current [[scenario]].
  430. *
  431. * 注意,当确认有条件验证的应用,使用
  432. * [[\yii\validators\RequiredValidator::$when|$when]] 这个方法将会返回
  433. * `false` 不管 `when` 条件, 因为它可能在模型加载数据前被调用
  434. *
  435. * @param string $attribute 属性名
  436. * @return boolean whether the attribute is required
  437. */
  438. public function isAttributeRequired($attribute)
  439. {
  440. foreach ($this->getActiveValidators($attribute) as $validator) {
  441. if ($validator instanceof RequiredValidator && $validator->when === null) {
  442. return true;
  443. }
  444. }
  445. return false;
  446. }
  447.  
  448. /**
  449. * 返回一个值,该值指示属性是否是安全的
  450. * @param string $attribute 属性名
  451. * @return boolean whether the attribute is safe for massive assignments
  452. * @see safeAttributes()
  453. */
  454. public function isAttributeSafe($attribute)
  455. {
  456. //判断属性是否在数组里,true表示全等(数值和类型都相等)
  457. return in_array($attribute, $this->safeAttributes(), true);
  458. }
  459.  
  460. /**
  461. * 返回一个值,该值指示当前场景中的属性是否处于活动状态。
  462. * @param string $attribute 属性名
  463. * @return boolean whether the attribute is active in the current scenario
  464. * @see activeAttributes()
  465. */
  466. public function isAttributeActive($attribute)
  467. {
  468. return in_array($attribute, $this->activeAttributes(), true);
  469. }
  470.  
  471. /**
  472. * 返回指定属性的文本标签
  473. * @param string $attribute 属性名
  474. * @return string 属性标签
  475. * @see generateAttributeLabel()
  476. * @see attributeLabels()
  477. */
  478. public function getAttributeLabel($attribute)
  479. {
  480. //获得所有属性标签,并给$lable这个数组
  481. $labels = $this->attributeLabels();
  482. //如果这个方法被子类重写了,直接返回自定义的标签,如果没有被重写,返回默认的
  483. return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute);
  484. }
  485.  
  486. /**
  487. * 返回指定属性的文本提示
  488. * @param string $attribute 属性名
  489. * @return string the attribute hint
  490. * @see attributeHints()
  491. * @since 2.0.4
  492. */
  493. public function getAttributeHint($attribute)
  494. {
  495. $hints = $this->attributeHints();
  496. //如果这个方法被子类重写了,直接返回自定义的文本提示,如果没有被重写,返回''
  497. return isset($hints[$attribute]) ? $hints[$attribute] : '';
  498. }
  499.  
  500. /**
  501. * 返回一个值,该值指示是否有任何验证错误
  502. * @param string|null $attribute attribute name. Use null to check all attributes.
  503. * @return boolean whether there is any error.
  504. */
  505. public function hasErrors($attribute = null)
  506. {
  507. //如果有错,_errors[$attribute]有值
  508. return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]);
  509. }
  510.  
  511. /**
  512. * 返回所有属性或单个属性的错误。
  513. * @param string $attribute attribute name. 使用NULL检索所有属性的错误
  514. * @property array An array of errors for all attributes. 其结果是二维数组,空数组意味着没有错误
  515. * See [[getErrors()]] for detailed description.
  516. * @return array 返回一个或多个属性所指定的错误。
  517. *
  518. * ```php
  519. * [
  520. * 'username' => [
  521. * 'Username is required.',
  522. * 'Username must contain only word characters.',
  523. * ],
  524. * 'email' => [
  525. * 'Email address is invalid.',
  526. * ]
  527. * ]
  528. * ```
  529. *
  530. * @see getFirstErrors()
  531. * @see getFirstError()
  532. */
  533. public function getErrors($attribute = null)
  534. {
  535. if ($attribute === null) {
  536. //如果属性为空,返回所有属性的验证结果
  537. return $this->_errors === null ? [] : $this->_errors;
  538. } else {
  539. //属性不为空,验证单个属性返回的错误结果
  540. return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : [];
  541. }
  542. }
  543.  
  544. /**
  545. * 返回模型中每个属性的第一个错误
  546. * @return array the first errors. 数组的键是属性名, 数组的值是错误信息
  547. * 没有错误就返回空数组
  548. * @see getErrors()
  549. * @see getFirstError()
  550. */
  551. public function getFirstErrors()
  552. {
  553. if (empty($this->_errors)) {
  554. //没有错误,返回空数组
  555. return [];
  556. } else {
  557. $errors = [];
  558. //遍历所有属性的第一个错误,放进数组
  559. foreach ($this->_errors as $name => $es) {
  560. if (!empty($es)) {
  561. $errors[$name] = reset($es);
  562. }
  563. }
  564.  
  565. return $errors;
  566. }
  567. }
  568.  
  569. /**
  570. * 返回指定属性的第一个错误。
  571. * @param string $attribute 属性名
  572. * @return string 错误信息,空意味着没有错误信息
  573. * @see getErrors()
  574. * @see getFirstErrors()
  575. */
  576. public function getFirstError($attribute)
  577. {
  578. //如果这个属性的错误在验证时存在,则返回这个错误,否则返回null
  579. return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
  580. }
  581.  
  582. /**
  583. * 向指定属性添加新错误
  584. * @param string $attribute attribute name
  585. * @param string $error new error message
  586. */
  587. public function addError($attribute, $error = '')
  588. {
  589. //把错误信息添加入指定属性的数组
  590. $this->_errors[$attribute][] = $error;
  591. }
  592.  
  593. /**
  594. * 添加错误列表
  595. * @param array $items 错误信息列表. 数组的键必须是属性的名字
  596. * 数组的值是错误信息. 如果一个属性有很多错误,则错误需要是数组的形式,
  597. * 你可以使用 [[getErrors()]] 作为此参数的值
  598. * @since 2.0.2
  599. */
  600. public function addErrors(array $items)
  601. {
  602. //遍历属性和对应的错误
  603. foreach ($items as $attribute => $errors) {
  604. if (is_array($errors)) {
  605. foreach ($errors as $error) {
  606. $this->addError($attribute, $error);
  607. }
  608. } else {
  609. $this->addError($attribute, $errors);
  610. }
  611. }
  612. }
  613.  
  614. /**
  615. * 删除所有属性或单个属性的错误。
  616. * @param string $attribute attribute name. 使用NULL删除所有属性的错误。
  617. */
  618. public function clearErrors($attribute = null)
  619. {
  620. if ($attribute === null) {
  621. //如果没传属性,把所有的错误清除掉
  622. $this->_errors = [];
  623. } else {
  624. //删除对应属性的错误
  625. unset($this->_errors[$attribute]);
  626. }
  627. }
  628.  
  629. /**
  630. * 根据给定属性名称生成用户友好的属性标签。
  631. * 这是通过用空格替换下划线、破折号和圆点,并将每个单词的第一个字母替换为大写字母。
  632. * @param string $name the column name
  633. * @return string the attribute label
  634. */
  635. public function generateAttributeLabel($name)
  636. {
  637. //camel2words定义了一个正则来替换字符串
  638. return Inflector::camel2words($name, true);
  639. }
  640.  
  641. /**
  642. * 返回属性值
  643. * @param array $names 返回需要的属性列表
  644. * 默认为null, 意味着定义在 [[attributes()]] 里的所有属性都会被返回
  645. * 如果是数组,则只返回数组中的属性
  646. * @param array $except 不应返回值的属性列表
  647. * @return array attribute values (name => value).
  648. */
  649. public function getAttributes($names = null, $except = [])
  650. {
  651. $values = [];
  652. if ($names === null) {
  653. //$names为null,$names设置成所有的属性
  654. $names = $this->attributes();
  655. }
  656. foreach ($names as $name) {
  657. $values[$name] = $this->$name;
  658. }
  659. foreach ($except as $name) {
  660. //不返回哪个属性,从数组中删除哪个属性
  661. unset($values[$name]);
  662. }
  663.  
  664. return $values;
  665. }
  666.  
  667. /**
  668. * 以大量的方式设置属性值。
  669. * @param array $values 属性以 (name => value) 被分配到模型
  670. * @param boolean $safeOnly 赋值是否只对安全的属性进行
  671. * 安全属性是与当前中的验证规则关联的属性,定义在[[scenario]]中.
  672. * @see safeAttributes()
  673. * @see attributes()
  674. */
  675. public function setAttributes($values, $safeOnly = true)
  676. {
  677. if (is_array($values)) {
  678. //array_flip交换数组中的键和值,$safeOnly为true返回安全属性,否则返回所有的属性
  679. $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());
  680. foreach ($values as $name => $value) {
  681. if (isset($attributes[$name])) {
  682. // 如果存在该属性,就直接赋值
  683. $this->$name = $value;
  684. } elseif ($safeOnly) {
  685. // 如果不存在,而且是 safeOnly 的话,就触发一下 onUnsafeAttribute 方法
  686. $this->onUnsafeAttribute($name, $value);
  687. }
  688. }
  689. }
  690. }
  691.  
  692. /**
  693. * 当一个不安全的属性被赋值时调用此方法
  694. * 如果是在yii_debug,默认实现会记录一个警告消息
  695. * @param string $name the unsafe attribute name
  696. * @param mixed $value the attribute value
  697. */
  698. public function onUnsafeAttribute($name, $value)
  699. {
  700. if (YII_DEBUG) {
  701. //debug模式,警告信息出现
  702. Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
  703. }
  704. }
  705.  
  706. /**
  707. * 返回在模型中应用的场景
  708. *
  709. * 场景影响如何进行验证,哪些属性可以大量分配。
  710. *
  711. * @return string the scenario that this model is in. 默认场景是 [[SCENARIO_DEFAULT]].
  712. */
  713. public function getScenario()
  714. {
  715. return $this->_scenario;
  716. }
  717.  
  718. /**
  719. * 为模型设置场景
  720. * 注意,此方法不检查场景是否存在
  721. * [[validate()]]会检查场景是否存在.
  722. * @param string $value 再模型中的场景名.
  723. */
  724. public function setScenario($value)
  725. {
  726. $this->_scenario = $value;
  727. }
  728.  
  729. /**
  730. * 返回当前场景中大量分配的安全的属性名称
  731. * @return string[] safe attribute names
  732. */
  733. public function safeAttributes()
  734. {
  735. // 获取当前的场景
  736. $scenario = $this->getScenario();
  737. // 获取所有场景及其属性
  738. $scenarios = $this->scenarios();
  739. if (!isset($scenarios[$scenario])) {
  740. // 场景不存在,就返回空
  741. return [];
  742. }
  743. $attributes = [];
  744. foreach ($scenarios[$scenario] as $attribute) {
  745. // 将开头不是!的属性才会放入到 $attributes 中
  746. if ($attribute[0] !== '!' && !in_array('!' . $attribute, $scenarios[$scenario])) {
  747. $attributes[] = $attribute;
  748. }
  749. }
  750.  
  751. return $attributes;
  752. }
  753.  
  754. /**
  755. * 返回当前场景中要受验证的属性名称
  756. * @return string[] 返回安全的属性名称
  757. */
  758. public function activeAttributes()
  759. {
  760. $scenario = $this->getScenario();
  761. $scenarios = $this->scenarios();
  762. if (!isset($scenarios[$scenario])) {
  763. return [];
  764. }
  765. // 获取当前场景中的所有属性
  766. $attributes = $scenarios[$scenario];
  767. foreach ($attributes as $i => $attribute) {
  768. if ($attribute[0] === '!') {
  769. // 如果属性名以!开头,就把!截取掉,并放入数组
  770. $attributes[$i] = substr($attribute, 1);
  771. }
  772. }
  773.  
  774. return $attributes;
  775. }
  776.  
  777. /**
  778. * 填充模型的输入数据(把数据加载到模型中)
  779. *
  780. * 这种方法提供了一个方便快捷的方式:
  781. *
  782. * ```php
  783. * if (isset($_POST['FormName'])) {
  784. * $model->attributes = $_POST['FormName'];
  785. * if ($model->save()) {
  786. * // handle success
  787. * }
  788. * }
  789. * ```
  790. *
  791. * 如果使用load方法
  792. *
  793. * ```php
  794. * if ($model->load($_POST) && $model->save()) {
  795. * // handle success
  796. * }
  797. * ```
  798. *
  799. * `load()` gets the `'FormName'` from the model's [[formName()]] method (which you may override), unless the
  800. * `$formName` parameter is given. If the form name is empty, `load()` populates the model with the whole of `$data`,
  801. * instead of `$data['FormName']`.
  802. *
  803. * 注意,被填充的数据将会接受[[setAttributes()]]的安全检查.
  804. *
  805. * @param array $data 模型加载的数组, 典型的是 `$_POST` or `$_GET`.
  806. * @param string $formName 用于将数据加载到模型中的表单名称
  807. * If not set, [[formName()]] is used.
  808. * @return boolean whether `load()` found the expected form in `$data`.
  809. */
  810. public function load($data, $formName = null)
  811. {
  812. //如果没有传表单名称,就取所在类的名称
  813. $scope = $formName === null ? $this->formName() : $formName;
  814. if ($scope === '' && !empty($data)) {
  815. //如果 $scope 为空字符串,且 $data不为空,就设置属性
  816. $this->setAttributes($data);
  817.  
  818. return true;
  819. } elseif (isset($data[$scope])) {
  820. // 存在 $data[$scope],使用 $data[$scope] 去设置属性
  821. $this->setAttributes($data[$scope]);
  822.  
  823. return true;
  824. } else {
  825. return false;
  826. }
  827. }
  828.  
  829. /**
  830. * 从终端用户获取数据,形成模型
  831. * 该方法主要用于收集表格数据输入
  832. * 为每个模型加载的数据`$data[formName][index]`, where `formName`
  833. * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array.
  834. * If [[formName()]] is empty, `$data[index]` 将用于填充每个模型.
  835. * 每个模型的数据都要经过安全检查 [[setAttributes()]].
  836. * @param array $models 要填充的模型 注意所有的模型都应该有相同的类
  837. * @param array $data the data array. 通常是 `$_POST` or `$_GET`, 也可以是最终用户提供的任何有效数组。
  838. * @param string $formName 要把数据加载到模型的表单名
  839. * If not set, it will use the [[formName()]] value of the first model in `$models`.
  840. * This parameter is available since version 2.0.1.
  841. * @return boolean whether at least one of the models is successfully populated.
  842. */
  843. public static function loadMultiple($models, $data, $formName = null)
  844. {
  845. if ($formName === null) {
  846. /* @var $first Model */
  847. //reset — 将数组的内部指针指向第一个单元
  848. $first = reset($models);
  849. if ($first === false) {
  850. // 不存在就返回 false
  851. return false;
  852. }
  853. // 拿到所在类的名称
  854. $formName = $first->formName();
  855. }
  856.  
  857. $success = false;
  858. // 遍历 $models,一个个 load 数据
  859. foreach ($models as $i => $model) {
  860. /* @var $model Model */
  861. if ($formName == '') {
  862. if (!empty($data[$i])) {
  863. // 数据不为空,就 load 到相应的 model 中
  864. $model->load($data[$i], '');
  865. $success = true;
  866. }
  867. } elseif (!empty($data[$formName][$i])) {
  868. // 存在 $formName,且数据不为空,就 load 到相应的 model 中
  869. $model->load($data[$formName][$i], '');
  870. $success = true;
  871. }
  872. }
  873.  
  874. return $success;
  875. }
  876.  
  877. /**
  878. * 验证多模型
  879. * 这种方法将验证每一个模型。被验证的模型可以是相同的或不同类型的。
  880. * @param array $models 要验证的模型
  881. * @param array $attributeNames 应该验证的属性名称列表。
  882. * 如果这个参数是空的,它意味着在适用的验证规则中列出的任何属性都应该被验证。
  883. * @return boolean 所有模型的验证规则是否有效. 一个或多个验证不通过都会返回false
  884. */
  885. public static function validateMultiple($models, $attributeNames = null)
  886. {
  887. $valid = true;
  888. /* @var $model Model */
  889. foreach ($models as $model) {
  890. //遍历$models 调用validate()方法
  891. $valid = $model->validate($attributeNames) && $valid;
  892. }
  893.  
  894. return $valid;
  895. }
  896.  
  897. /**
  898. * 当没有指定特定字段时,通过[[toArray()]] 方法返回默认情况下应返回的字段列表
  899. *
  900. * 此方法应返回字段名称或字段定义的数组
  901. * 当没有指定特定字段时,字段名称将被视为对象属性名称,其值将用作字段值
  902. * 当指定字段时,数组键应该是字段名,而数组值应该是相应的字段定义,它可以是对象属性名,也可以是PHP可调用返回相应字段值
  903. *
  904. *回调函数格式为:
  905. * ```php
  906. * function ($model, $field) {
  907. * // return field value
  908. * }
  909. * ```
  910. *
  911. * 例如,下面的代码声明四个字段:
  912. *
  913. * - `email`: 字段名称与属性名称相同`email`;
  914. * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
  915. * values are obtained from the `first_name` and `last_name` properties;
  916. * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
  917. * and `last_name`.
  918. *
  919. * ```php
  920. * return [
  921. * 'email',
  922. * 'firstName' => 'first_name',
  923. * 'lastName' => 'last_name',
  924. * 'fullName' => function ($model) {
  925. * return $model->first_name . ' ' . $model->last_name;
  926. * },
  927. * ];
  928. * ```
  929. *
  930. * 在此方法中,还可以根据一些上下文信息返回字段的不同列表
  931. * 例如,取决于场景[scenario]或当前应用程序用户的特权
  932. * 您可以返回不同的可见字段集或筛选一些字段。
  933. *
  934. * 此方法返回[[attributes()]] 的默认实现,索引是属性名
  935. *
  936. * @return array 返回字段名称或字段定义的列表.
  937. * @see toArray()
  938. */
  939. public function fields()
  940. {
  941. $fields = $this->attributes();
  942. //合并两个数组来创建一个新数组,其中的一个数组元素为键名,另一个数组元素为键值
  943. return array_combine($fields, $fields);
  944. }
  945.  
  946. /**
  947. * 返回用于遍历模型中的属性的迭代器
  948. * 此方法所需的接口[[\IteratorAggregate]].
  949. * @return ArrayIterator 遍历列表中的项的迭代器
  950. */
  951. public function getIterator()
  952. {
  953. $attributes = $this->getAttributes();
  954. return new ArrayIterator($attributes);
  955. }
  956.  
  957. /**
  958. * 返回指定偏移量是否有元素
  959. * 此方法需要SPL接口 [[\ArrayAccess]].
  960. * 它会隐式调用 `isset($model[$offset])`.
  961. * @param mixed $offset 检查的偏移量
  962. * @return boolean 是否存在偏移
  963. */
  964. public function offsetExists($offset)
  965. {
  966. return isset($this->$offset);
  967. }
  968.  
  969. /**
  970. * 返回指定偏移量的元素
  971. * 此方法需要SPL接口 [[\ArrayAccess]].
  972. * 会隐式调用 `$value = $model[$offset];`.
  973. * @param mixed $offset the offset to retrieve element.
  974. * @return mixed the element at the offset,如果在偏移处没有找到元素,返回null
  975. */
  976. public function offsetGet($offset)
  977. {
  978. return $this->$offset;
  979. }
  980.  
  981. /**
  982. * 设置指定偏移量的元素
  983. * 此方法需要SPL接口[[\ArrayAccess]].
  984. * 会被隐式调用 `$model[$offset] = $item;`.
  985. * @param integer $offset the offset to set element
  986. * @param mixed $item 节点的值
  987. */
  988. public function offsetSet($offset, $item)
  989. {
  990. $this->$offset = $item;
  991. }
  992.  
  993. /**
  994. * 将指定偏移量的元素值设置为空
  995. * 此方法需要SPL接口 [[\ArrayAccess]].
  996. * 会隐式调用 `unset($model[$offset])`.
  997. * @param mixed $offset the offset to unset element
  998. */
  999. public function offsetUnset($offset)
  1000. {
  1001. $this->$offset = null;
  1002. }
  1003. }

yii2 源码分析 model类分析 (五)的更多相关文章

  1. JDK源码之Integer类分析

    一 简介 Integer是int基本类型的包装类,同样继承了Number类,实现了Comparable接口,String类中的一些转化方法就使用了Integer类中的一些API,且fianl修饰不可继 ...

  2. [Android FrameWork 6.0源码学习] LayoutInflater 类分析

    LayoutInflater是用来解析XML布局文件,然后生成对象的ViewTree的工具类.是这个工具类的存在,才能让我们写起Layout来那么省劲. 我们接下来进去刨析,看看里边的奥秘 //调用i ...

  3. JDK源码之Byte类分析

    一 简介 byte,即字节,由8位的二进制组成.在Java中,byte类型的数据是8位带符号的二进制数,以二进制补码表示的整数 取值范围:默认值为0,最小值为-128(-2^7);最大值是127(2^ ...

  4. JDK源码之Boolean类分析

    一 简介 boolean类型的封装类,将基本类型为boolean的值包装在一个对象中,实现序列化接口,和Comparable接口 额外提供了许多便捷方法,比较简单,直接贴代码分析 二 源码分析 //t ...

  5. JDK源码之AbstractStringBuilder类分析

    一 概述 二 实现接口 AbstractStringBuilder实现了两个接口: Appendable 概述: Appendable的实现类的对象可以附加字符序列和值. 要追加的字符应该是Unico ...

  6. Yii2 源码分析 入口文件执行流程

    Yii2 源码分析  入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...

  7. Yii2源码分析(一):入口

    写在前面,写这些随笔是记录下自己看Yii2源码的过程,可能会有些流水账,大部分解析放在注释里说明,由于个人水平有限,有不正确的地方还望斧正. web入口文件Index.php // 定义全局的常量,Y ...

  8. Okhttp3源码解析(3)-Call分析(整体流程)

    ### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...

  9. Okhttp3源码解析(2)-Request分析

    ### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...

随机推荐

  1. bat脚本设置系统环境变量即时生效

    关于bat的资料多但零碎,记录一下. 1.设置环境变量即时生效:通过重启explorer来实现即时生效(亲测有效) @echo off set curPath=%cd% wmic ENVIRONMEN ...

  2. nginx版本如何选择?

    生产环境使用Stable version:最新稳定版,现在最新的版本是nginx-1.8.1 注意各版本的区别:Nginx官网提供了三个类型的版本 1.Mainline version:Mainlin ...

  3. myeclipse 奔溃解决办法

    myeclipse 突然奔溃 解决办法 JVM terminated.Exit code=1 意思是说java 虚拟机挂了,而不是myeclipse挂了. 进入myeclipse 目录 查看myecl ...

  4. 配置python+mod_wsgi+apache 时 在浏览器中访问服务器时报错:Invalid HTTP_HOST header: 'XXXXX'. You may need to add u'XXXXX' to ALLOWED_HOSTS,在setting.py中添加‘*”无效的原因

    配置python+mod_wsgi+apache 时 在浏览器中访问服务器时报错:Invalid HTTP_HOST header: 'XXXXX'. You may need to add u'XX ...

  5. SpringMVC之GET请求参数中文乱码

    server.xml 文件中的编码过滤器设置是针对POST请求的,tomacat对GET和POST请求处理方式是不同的,要处理针对GET请求的编码问题,则需要改tomcat,conf目录下的serve ...

  6. js_4_函数

    js的函数是怎么定义的? function 函数名(形参1,......) { 函数体: return 返回值 :                                 //  可以没有返回 ...

  7. maven依赖问题

    我的一个maven项目A依赖于我的另一个maven项目B,但是maven dependencies中显示的是文件.如下图: 而且项目A部署的时候,部署到tomcat容器的时候也是直接部署的B的编译后的 ...

  8. PHP面试题超强总结(PHP中文网)

    PHP面试基础题目 1.双引号和单引号的区别 双引号解释变量,单引号不解释变量 双引号里插入单引号,其中单引号里如果有变量的话,变量解释 双引号的变量名后面必须要有一个非数字.字母.下划线的特殊字符, ...

  9. keepalived深度结合lvs_dr模式

    keepalived与dr模式结合 keepalived介绍 keepalived可提供vrrp以及health-check功能,可以只用它提供双机浮动的vip(vrrp虚拟路由功能), 这样可以简单 ...

  10. awk使用正则精确匹配

    [root@localhost home]# cat file 5001][YRSD5-1][YRSD5-1-2][0203008400028411] 010102 5001][YRSD7-1][YR ...