PageRenderTime 88ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/yii/framework/base/CModel.php

https://github.com/joshuaswarren/weatherhub
PHP | 624 lines | 254 code | 41 blank | 329 comment | 23 complexity | d2997c83f8c65259013dad4a573aa28f MD5 | raw file
  1. <?php
  2. /**
  3. * CModel class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CModel is the base class providing the common features needed by data model objects.
  12. *
  13. * CModel defines the basic framework for data models that need to be validated.
  14. *
  15. * @author Qiang Xue <qiang.xue@gmail.com>
  16. * @version $Id: CModel.php 3276 2011-06-15 14:21:12Z alexander.makarow $
  17. * @package system.base
  18. * @since 1.0
  19. */
  20. abstract class CModel extends CComponent implements IteratorAggregate, ArrayAccess
  21. {
  22. private $_errors=array(); // attribute name => array of errors
  23. private $_validators; // validators
  24. private $_scenario=''; // scenario
  25. /**
  26. * Returns the list of attribute names of the model.
  27. * @return array list of attribute names.
  28. * @since 1.0.1
  29. */
  30. abstract public function attributeNames();
  31. /**
  32. * Returns the validation rules for attributes.
  33. *
  34. * This method should be overridden to declare validation rules.
  35. * Each rule is an array with the following structure:
  36. * <pre>
  37. * array('attribute list', 'validator name', 'on'=>'scenario name', ...validation parameters...)
  38. * </pre>
  39. * where
  40. * <ul>
  41. * <li>attribute list: specifies the attributes (separated by commas) to be validated;</li>
  42. * <li>validator name: specifies the validator to be used. It can be the name of a model class
  43. * method, the name of a built-in validator, or a validator class (or its path alias).
  44. * A validation method must have the following signature:
  45. * <pre>
  46. * // $params refers to validation parameters given in the rule
  47. * function validatorName($attribute,$params)
  48. * </pre>
  49. * A built-in validator refers to one of the validators declared in {@link CValidator::builtInValidators}.
  50. * And a validator class is a class extending {@link CValidator}.</li>
  51. * <li>on: this specifies the scenarios when the validation rule should be performed.
  52. * Separate different scenarios with commas. If this option is not set, the rule
  53. * will be applied in any scenario. Please see {@link scenario} for more details about this option.</li>
  54. * <li>additional parameters are used to initialize the corresponding validator properties.
  55. * Please refer to individal validator class API for possible properties.</li>
  56. * </ul>
  57. *
  58. * The following are some examples:
  59. * <pre>
  60. * array(
  61. * array('username', 'required'),
  62. * array('username', 'length', 'min'=>3, 'max'=>12),
  63. * array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
  64. * array('password', 'authenticate', 'on'=>'login'),
  65. * );
  66. * </pre>
  67. *
  68. * Note, in order to inherit rules defined in the parent class, a child class needs to
  69. * merge the parent rules with child rules using functions like array_merge().
  70. *
  71. * @return array validation rules to be applied when {@link validate()} is called.
  72. * @see scenario
  73. */
  74. public function rules()
  75. {
  76. return array();
  77. }
  78. /**
  79. * Returns a list of behaviors that this model should behave as.
  80. * The return value should be an array of behavior configurations indexed by
  81. * behavior names. Each behavior configuration can be either a string specifying
  82. * the behavior class or an array of the following structure:
  83. * <pre>
  84. * 'behaviorName'=>array(
  85. * 'class'=>'path.to.BehaviorClass',
  86. * 'property1'=>'value1',
  87. * 'property2'=>'value2',
  88. * )
  89. * </pre>
  90. *
  91. * Note, the behavior classes must implement {@link IBehavior} or extend from
  92. * {@link CBehavior}. Behaviors declared in this method will be attached
  93. * to the model when it is instantiated.
  94. *
  95. * For more details about behaviors, see {@link CComponent}.
  96. * @return array the behavior configurations (behavior name=>behavior configuration)
  97. * @since 1.0.2
  98. */
  99. public function behaviors()
  100. {
  101. return array();
  102. }
  103. /**
  104. * Returns the attribute labels.
  105. * Attribute labels are mainly used in error messages of validation.
  106. * By default an attribute label is generated using {@link generateAttributeLabel}.
  107. * This method allows you to explicitly specify attribute labels.
  108. *
  109. * Note, in order to inherit labels defined in the parent class, a child class needs to
  110. * merge the parent labels with child labels using functions like array_merge().
  111. *
  112. * @return array attribute labels (name=>label)
  113. * @see generateAttributeLabel
  114. */
  115. public function attributeLabels()
  116. {
  117. return array();
  118. }
  119. /**
  120. * Performs the validation.
  121. *
  122. * This method executes the validation rules as declared in {@link rules}.
  123. * Only the rules applicable to the current {@link scenario} will be executed.
  124. * A rule is considered applicable to a scenario if its 'on' option is not set
  125. * or contains the scenario.
  126. *
  127. * Errors found during the validation can be retrieved via {@link getErrors}.
  128. *
  129. * @param array $attributes list of attributes that should be validated. Defaults to null,
  130. * meaning any attribute listed in the applicable validation rules should be
  131. * validated. If this parameter is given as a list of attributes, only
  132. * the listed attributes will be validated.
  133. * @param boolean $clearErrors whether to call {@link clearErrors} before performing validation
  134. * @return boolean whether the validation is successful without any error.
  135. * @see beforeValidate
  136. * @see afterValidate
  137. */
  138. public function validate($attributes=null, $clearErrors=true)
  139. {
  140. if($clearErrors)
  141. $this->clearErrors();
  142. if($this->beforeValidate())
  143. {
  144. foreach($this->getValidators() as $validator)
  145. $validator->validate($this,$attributes);
  146. $this->afterValidate();
  147. return !$this->hasErrors();
  148. }
  149. else
  150. return false;
  151. }
  152. /**
  153. * This method is invoked after a model instance is created by new operator.
  154. * The default implementation raises the {@link onAfterConstruct} event.
  155. * You may override this method to do postprocessing after model creation.
  156. * Make sure you call the parent implementation so that the event is raised properly.
  157. */
  158. protected function afterConstruct()
  159. {
  160. if($this->hasEventHandler('onAfterConstruct'))
  161. $this->onAfterConstruct(new CEvent($this));
  162. }
  163. /**
  164. * This method is invoked before validation starts.
  165. * The default implementation calls {@link onBeforeValidate} to raise an event.
  166. * You may override this method to do preliminary checks before validation.
  167. * Make sure the parent implementation is invoked so that the event can be raised.
  168. * @return boolean whether validation should be executed. Defaults to true.
  169. * If false is returned, the validation will stop and the model is considered invalid.
  170. */
  171. protected function beforeValidate()
  172. {
  173. $event=new CModelEvent($this);
  174. $this->onBeforeValidate($event);
  175. return $event->isValid;
  176. }
  177. /**
  178. * This method is invoked after validation ends.
  179. * The default implementation calls {@link onAfterValidate} to raise an event.
  180. * You may override this method to do postprocessing after validation.
  181. * Make sure the parent implementation is invoked so that the event can be raised.
  182. */
  183. protected function afterValidate()
  184. {
  185. $this->onAfterValidate(new CEvent($this));
  186. }
  187. /**
  188. * This event is raised after the model instance is created by new operator.
  189. * @param CEvent $event the event parameter
  190. * @since 1.0.2
  191. */
  192. public function onAfterConstruct($event)
  193. {
  194. $this->raiseEvent('onAfterConstruct',$event);
  195. }
  196. /**
  197. * This event is raised before the validation is performed.
  198. * @param CModelEvent $event the event parameter
  199. * @since 1.0.2
  200. */
  201. public function onBeforeValidate($event)
  202. {
  203. $this->raiseEvent('onBeforeValidate',$event);
  204. }
  205. /**
  206. * This event is raised after the validation is performed.
  207. * @param CEvent $event the event parameter
  208. * @since 1.0.2
  209. */
  210. public function onAfterValidate($event)
  211. {
  212. $this->raiseEvent('onAfterValidate',$event);
  213. }
  214. /**
  215. * Returns all the validators declared in the model.
  216. * This method differs from {@link getValidators} in that the latter
  217. * would only return the validators applicable to the current {@link scenario}.
  218. * Also, since this method return a {@link CList} object, you may
  219. * manipulate it by inserting or removing validators (useful in behaviors).
  220. * For example, <code>$model->validatorList->add($newValidator)</code>.
  221. * The change made to the {@link CList} object will persist and reflect
  222. * in the result of the next call of {@link getValidators}.
  223. * @return CList all the validators declared in the model.
  224. * @since 1.1.2
  225. */
  226. public function getValidatorList()
  227. {
  228. if($this->_validators===null)
  229. $this->_validators=$this->createValidators();
  230. return $this->_validators;
  231. }
  232. /**
  233. * Returns the validators applicable to the current {@link scenario}.
  234. * @param string $attribute the name of the attribute whose validators should be returned.
  235. * If this is null, the validators for ALL attributes in the model will be returned.
  236. * @return array the validators applicable to the current {@link scenario}.
  237. * @since 1.0.1
  238. */
  239. public function getValidators($attribute=null)
  240. {
  241. if($this->_validators===null)
  242. $this->_validators=$this->createValidators();
  243. $validators=array();
  244. $scenario=$this->getScenario();
  245. foreach($this->_validators as $validator)
  246. {
  247. if($validator->applyTo($scenario))
  248. {
  249. if($attribute===null || in_array($attribute,$validator->attributes,true))
  250. $validators[]=$validator;
  251. }
  252. }
  253. return $validators;
  254. }
  255. /**
  256. * Creates validator objects based on the specification in {@link rules}.
  257. * This method is mainly used internally.
  258. * @return CList validators built based on {@link rules()}.
  259. */
  260. public function createValidators()
  261. {
  262. $validators=new CList;
  263. foreach($this->rules() as $rule)
  264. {
  265. if(isset($rule[0],$rule[1])) // attributes, validator name
  266. $validators->add(CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)));
  267. else
  268. throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.',
  269. array('{class}'=>get_class($this))));
  270. }
  271. return $validators;
  272. }
  273. /**
  274. * Returns a value indicating whether the attribute is required.
  275. * This is determined by checking if the attribute is associated with a
  276. * {@link CRequiredValidator} validation rule in the current {@link scenario}.
  277. * @param string $attribute attribute name
  278. * @return boolean whether the attribute is required
  279. * @since 1.0.2
  280. */
  281. public function isAttributeRequired($attribute)
  282. {
  283. foreach($this->getValidators($attribute) as $validator)
  284. {
  285. if($validator instanceof CRequiredValidator)
  286. return true;
  287. }
  288. return false;
  289. }
  290. /**
  291. * Returns a value indicating whether the attribute is safe for massive assignments.
  292. * @param string $attribute attribute name
  293. * @return boolean whether the attribute is safe for massive assignments
  294. * @since 1.1
  295. */
  296. public function isAttributeSafe($attribute)
  297. {
  298. $attributes=$this->getSafeAttributeNames();
  299. return in_array($attribute,$attributes);
  300. }
  301. /**
  302. * Returns the text label for the specified attribute.
  303. * @param string $attribute the attribute name
  304. * @return string the attribute label
  305. * @see generateAttributeLabel
  306. * @see attributeLabels
  307. */
  308. public function getAttributeLabel($attribute)
  309. {
  310. $labels=$this->attributeLabels();
  311. if(isset($labels[$attribute]))
  312. return $labels[$attribute];
  313. else
  314. return $this->generateAttributeLabel($attribute);
  315. }
  316. /**
  317. * Returns a value indicating whether there is any validation error.
  318. * @param string $attribute attribute name. Use null to check all attributes.
  319. * @return boolean whether there is any error.
  320. */
  321. public function hasErrors($attribute=null)
  322. {
  323. if($attribute===null)
  324. return $this->_errors!==array();
  325. else
  326. return isset($this->_errors[$attribute]);
  327. }
  328. /**
  329. * Returns the errors for all attribute or a single attribute.
  330. * @param string $attribute attribute name. Use null to retrieve errors for all attributes.
  331. * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
  332. */
  333. public function getErrors($attribute=null)
  334. {
  335. if($attribute===null)
  336. return $this->_errors;
  337. else
  338. return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array();
  339. }
  340. /**
  341. * Returns the first error of the specified attribute.
  342. * @param string $attribute attribute name.
  343. * @return string the error message. Null is returned if no error.
  344. * @since 1.0.2
  345. */
  346. public function getError($attribute)
  347. {
  348. return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
  349. }
  350. /**
  351. * Adds a new error to the specified attribute.
  352. * @param string $attribute attribute name
  353. * @param string $error new error message
  354. */
  355. public function addError($attribute,$error)
  356. {
  357. $this->_errors[$attribute][]=$error;
  358. }
  359. /**
  360. * Adds a list of errors.
  361. * @param array $errors a list of errors. The array keys must be attribute names.
  362. * The array values should be error messages. If an attribute has multiple errors,
  363. * these errors must be given in terms of an array.
  364. * You may use the result of {@link getErrors} as the value for this parameter.
  365. * @since 1.0.5
  366. */
  367. public function addErrors($errors)
  368. {
  369. foreach($errors as $attribute=>$error)
  370. {
  371. if(is_array($error))
  372. {
  373. foreach($error as $e)
  374. $this->_errors[$attribute][]=$e;
  375. }
  376. else
  377. $this->_errors[$attribute][]=$error;
  378. }
  379. }
  380. /**
  381. * Removes errors for all attributes or a single attribute.
  382. * @param string $attribute attribute name. Use null to remove errors for all attribute.
  383. */
  384. public function clearErrors($attribute=null)
  385. {
  386. if($attribute===null)
  387. $this->_errors=array();
  388. else
  389. unset($this->_errors[$attribute]);
  390. }
  391. /**
  392. * Generates a user friendly attribute label.
  393. * This is done by replacing underscores or dashes with blanks and
  394. * changing the first letter of each word to upper case.
  395. * For example, 'department_name' or 'DepartmentName' becomes 'Department Name'.
  396. * @param string $name the column name
  397. * @return string the attribute label
  398. */
  399. public function generateAttributeLabel($name)
  400. {
  401. return ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))));
  402. }
  403. /**
  404. * Returns all attribute values.
  405. * @param array $names list of attributes whose value needs to be returned.
  406. * Defaults to null, meaning all attributes as listed in {@link attributeNames} will be returned.
  407. * If it is an array, only the attributes in the array will be returned.
  408. * @return array attribute values (name=>value).
  409. */
  410. public function getAttributes($names=null)
  411. {
  412. $values=array();
  413. foreach($this->attributeNames() as $name)
  414. $values[$name]=$this->$name;
  415. if(is_array($names))
  416. {
  417. $values2=array();
  418. foreach($names as $name)
  419. $values2[$name]=isset($values[$name]) ? $values[$name] : null;
  420. return $values2;
  421. }
  422. else
  423. return $values;
  424. }
  425. /**
  426. * Sets the attribute values in a massive way.
  427. * @param array $values attribute values (name=>value) to be set.
  428. * @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
  429. * A safe attribute is one that is associated with a validation rule in the current {@link scenario}.
  430. * @see getSafeAttributeNames
  431. * @see attributeNames
  432. */
  433. public function setAttributes($values,$safeOnly=true)
  434. {
  435. if(!is_array($values))
  436. return;
  437. $attributes=array_flip($safeOnly ? $this->getSafeAttributeNames() : $this->attributeNames());
  438. foreach($values as $name=>$value)
  439. {
  440. if(isset($attributes[$name]))
  441. $this->$name=$value;
  442. else if($safeOnly)
  443. $this->onUnsafeAttribute($name,$value);
  444. }
  445. }
  446. /**
  447. * Unsets the attributes.
  448. * @param array $names list of attributes to be set null. If this parameter is not given,
  449. * all attributes as specified by {@link attributeNames} will have their values unset.
  450. * @since 1.1.3
  451. */
  452. public function unsetAttributes($names=null)
  453. {
  454. if($names===null)
  455. $names=$this->attributeNames();
  456. foreach($names as $name)
  457. $this->$name=null;
  458. }
  459. /**
  460. * This method is invoked when an unsafe attribute is being massively assigned.
  461. * The default implementation will log a warning message if YII_DEBUG is on.
  462. * It does nothing otherwise.
  463. * @param string $name the unsafe attribute name
  464. * @param mixed $value the attribute value
  465. * @since 1.1.1
  466. */
  467. public function onUnsafeAttribute($name,$value)
  468. {
  469. if(YII_DEBUG)
  470. Yii::log(Yii::t('yii','Failed to set unsafe attribute "{attribute}" of "{class}".',array('{attribute}'=>$name, '{class}'=>get_class($this))),CLogger::LEVEL_WARNING);
  471. }
  472. /**
  473. * Returns the scenario that this model is used in.
  474. *
  475. * Scenario affects how validation is performed and which attributes can
  476. * be massively assigned.
  477. *
  478. * A validation rule will be performed when calling {@link validate()}
  479. * if its 'on' option is not set or contains the current scenario value.
  480. *
  481. * And an attribute can be massively assigned if it is associated with
  482. * a validation rule for the current scenario. Note that an exception is
  483. * the {@link CUnsafeValidator unsafe} validator which marks the associated
  484. * attributes as unsafe and not allowed to be massively assigned.
  485. *
  486. * @return string the scenario that this model is in.
  487. * @since 1.0.4
  488. */
  489. public function getScenario()
  490. {
  491. return $this->_scenario;
  492. }
  493. /**
  494. * Sets the scenario for the model.
  495. * @param string $value the scenario that this model is in.
  496. * @see getScenario
  497. * @since 1.0.4
  498. */
  499. public function setScenario($value)
  500. {
  501. $this->_scenario=$value;
  502. }
  503. /**
  504. * Returns the attribute names that are safe to be massively assigned.
  505. * A safe attribute is one that is associated with a validation rule in the current {@link scenario}.
  506. * @return array safe attribute names
  507. * @since 1.0.2
  508. */
  509. public function getSafeAttributeNames()
  510. {
  511. $attributes=array();
  512. $unsafe=array();
  513. foreach($this->getValidators() as $validator)
  514. {
  515. if(!$validator->safe)
  516. {
  517. foreach($validator->attributes as $name)
  518. $unsafe[]=$name;
  519. }
  520. else
  521. {
  522. foreach($validator->attributes as $name)
  523. $attributes[$name]=true;
  524. }
  525. }
  526. foreach($unsafe as $name)
  527. unset($attributes[$name]);
  528. return array_keys($attributes);
  529. }
  530. /**
  531. * Returns an iterator for traversing the attributes in the model.
  532. * This method is required by the interface IteratorAggregate.
  533. * @return CMapIterator an iterator for traversing the items in the list.
  534. */
  535. public function getIterator()
  536. {
  537. $attributes=$this->getAttributes();
  538. return new CMapIterator($attributes);
  539. }
  540. /**
  541. * Returns whether there is an element at the specified offset.
  542. * This method is required by the interface ArrayAccess.
  543. * @param mixed $offset the offset to check on
  544. * @return boolean
  545. * @since 1.0.2
  546. */
  547. public function offsetExists($offset)
  548. {
  549. return property_exists($this,$offset);
  550. }
  551. /**
  552. * Returns the element at the specified offset.
  553. * This method is required by the interface ArrayAccess.
  554. * @param integer $offset the offset to retrieve element.
  555. * @return mixed the element at the offset, null if no element is found at the offset
  556. * @since 1.0.2
  557. */
  558. public function offsetGet($offset)
  559. {
  560. return $this->$offset;
  561. }
  562. /**
  563. * Sets the element at the specified offset.
  564. * This method is required by the interface ArrayAccess.
  565. * @param integer $offset the offset to set element
  566. * @param mixed $item the element value
  567. * @since 1.0.2
  568. */
  569. public function offsetSet($offset,$item)
  570. {
  571. $this->$offset=$item;
  572. }
  573. /**
  574. * Unsets the element at the specified offset.
  575. * This method is required by the interface ArrayAccess.
  576. * @param mixed $offset the offset to unset element
  577. * @since 1.0.2
  578. */
  579. public function offsetUnset($offset)
  580. {
  581. unset($this->$offset);
  582. }
  583. }