PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/protected/extensions/yiibooster/widgets/TbEditableField.php

https://gitlab.com/zenfork/vektor
PHP | 242 lines | 175 code | 20 blank | 47 comment | 31 complexity | 2971450c2572f43147bcbe3abf98b99a MD5 | raw file
  1. <?php
  2. /**
  3. * TbEditableField class file.
  4. *
  5. * @author Vitaliy Potapov <noginsk@rambler.ru>
  6. * @link https://github.com/vitalets/x-editable-yii
  7. * @copyright Copyright &copy; Vitaliy Potapov 2012
  8. * @version 1.3.1
  9. */
  10. Yii::import('booster.widgets.TbEditable');
  11. /**
  12. * TbEditableField widget makes editable single attribute of model.
  13. *
  14. * @package widgets
  15. */
  16. class TbEditableField extends TbEditable
  17. {
  18. /**
  19. * @var CActiveRecord ActiveRecord to be updated.
  20. */
  21. public $model = null;
  22. /**
  23. * @var string attribute name.
  24. */
  25. public $attribute = null;
  26. /**
  27. * @var instance of model that is created always:
  28. * E.g. if related model does not exist, it will be `newed` to be able to get Attribute label, etc
  29. * for live update.
  30. */
  31. private $staticModel = null;
  32. /**
  33. * initialization of widget
  34. *
  35. */
  36. public function init()
  37. {
  38. if (!$this->model) {
  39. throw new CException('Parameter "model" should be provided for TbEditableField');
  40. }
  41. if (!$this->attribute) {
  42. throw new CException('Parameter "attribute" should be provided for TbEditableField');
  43. }
  44. $originalModel = $this->model;
  45. $originalAttribute = $this->attribute;
  46. $originalText = strlen($this->text) ? $this->text : CHtml::value($this->model, $this->attribute);
  47. //if apply set manually to false --> just render text, no js plugin applied
  48. if($this->apply === false) {
  49. $this->text = $originalText;
  50. } else {
  51. $this->apply = true;
  52. }
  53. //try to resolve related model (if attribute contains '.')
  54. $resolved = $this->resolveModels($this->model, $this->attribute);
  55. $this->model = $resolved['model'];
  56. $this->attribute = $resolved['attribute'];
  57. $this->staticModel = $resolved['staticModel'];
  58. $staticModel = $this->staticModel;
  59. $isMongo = $resolved['isMongo'];
  60. $isFormModel = $this->model instanceOf CFormModel;
  61. //if real (related) model not exists --> just print text
  62. if(!$this->model) {
  63. $this->apply = false;
  64. $this->text = $originalText;
  65. }
  66. //for security reason only safe attributes can be editable (e.g. defined in rules of model)
  67. //just print text (see 'run' method)
  68. if (!$staticModel->isAttributeSafe($this->attribute)) {
  69. $this->apply = false;
  70. $this->text = $originalText;
  71. }
  72. /*
  73. try to detect type from metadata if not set
  74. */
  75. if ($this->type === null) {
  76. $this->type = 'text';
  77. if (!$isMongo && !$isFormModel && array_key_exists($this->attribute, $staticModel->tableSchema->columns)) {
  78. $dbType = $staticModel->tableSchema->columns[$this->attribute]->dbType;
  79. if($dbType == 'date') {
  80. $this->type = 'date';
  81. }
  82. if($dbType == 'datetime') {
  83. $this->type = 'datetime';
  84. }
  85. if(stripos($dbType, 'text') !== false) {
  86. $this->type = 'textarea';
  87. }
  88. }
  89. }
  90. //name
  91. if(empty($this->name)) {
  92. $this->name = $isMongo ? $originalAttribute : $this->attribute;
  93. }
  94. //pk (for mongo takes pk from parent!)
  95. $pkModel = $isMongo ? $originalModel : $this->model;
  96. if(!$isFormModel) {
  97. if($pkModel && !$pkModel->isNewRecord) {
  98. $this->pk = $pkModel->primaryKey;
  99. }
  100. } else {
  101. //formModel does not have pk, so set `send` option to `always` (send without pk)
  102. if(empty($this->send) && empty($this->options['send'])) {
  103. $this->send = 'always';
  104. }
  105. }
  106. parent::init();
  107. /*
  108. If text not defined, generate it from model attribute for types except lists ('select', 'checklist' etc)
  109. For lists keep it empty to apply autotext.
  110. $this->_prepareToAutotext calculated in parent class TbEditable.php
  111. */
  112. if (!strlen($this->text) && !$this->_prepareToAutotext) {
  113. $this->text = $originalText;
  114. }
  115. //set value directly for autotext generation
  116. if($this->model && $this->_prepareToAutotext) {
  117. $this->value = CHtml::value($this->model, $this->attribute);
  118. }
  119. //generate title from attribute label
  120. if ($this->title === null) {
  121. $titles = array(
  122. 'Select' => array('select', 'date'),
  123. 'Check' => array('checklist')
  124. );
  125. $title = Yii::t('TbEditableField.editable', 'Enter');
  126. foreach($titles as $t => $types) {
  127. if(in_array($this->type, $types)) {
  128. $title = Yii::t('TbEditableField.editable', $t);
  129. }
  130. }
  131. $this->title = $title . ' ' . $staticModel->getAttributeLabel($this->attribute);
  132. } else {
  133. $this->title = strtr($this->title, array('{label}' => $staticModel->getAttributeLabel($this->attribute)));
  134. }
  135. //scenario
  136. if($pkModel && !isset($this->params['scenario'])) {
  137. $this->params['scenario'] = $pkModel->getScenario();
  138. }
  139. }
  140. public function getSelector()
  141. {
  142. return str_replace('\\', '_', get_class($this->staticModel)).'_'.parent::getSelector();
  143. }
  144. /**
  145. * Checks is model is instance of mongo model
  146. * see: http://www.yiiframework.com/extension/yiimongodbsuite
  147. *
  148. * @param mixed $model
  149. * @return bool
  150. */
  151. public static function isMongo($model)
  152. {
  153. return in_array('EMongoEmbeddedDocument', class_parents($model, false));
  154. }
  155. /**
  156. * Resolves model and returns array of values:
  157. * - staticModel: static class of model, need for checki safety of attribute
  158. * - real model: containing attribute. Can be null
  159. * - attribute: it will be without dots for activerecords
  160. *
  161. * @param mixed $model
  162. * @param mixed $attribute
  163. */
  164. public static function resolveModels($model, $attribute)
  165. {
  166. //attribute contains dot: related model, trying to resolve
  167. $explode = explode('.', $attribute);
  168. $len = count($explode);
  169. $isMongo = self::isMongo($model);
  170. if($len > 1) {
  171. $attribute = $explode[$len-1];
  172. //try to resolve model instance
  173. $resolved = true;
  174. for($i = 0; $i < $len-1; $i++) {
  175. $name = $explode[$i];
  176. if($model->$name instanceof CModel) {
  177. $model = $model->$name;
  178. } else {
  179. //related model not exist! Render text only.
  180. //$this->apply = false;
  181. $resolved = false;
  182. //$this->text = $originalText;
  183. break;
  184. }
  185. }
  186. if($resolved) {
  187. $staticModel = $model;
  188. } else { //related model not resolved: maybe not exists
  189. $relationName = $explode[$len-2];
  190. if($model instanceof CActiveRecord) {
  191. $className = $model->getActiveRelation($relationName)->className;
  192. } elseif($isMongo) {
  193. $embedded = $model->embeddedDocuments();
  194. if(isset($embedded[$relationName])) {
  195. $className = $embedded[$relationName];
  196. } else {
  197. throw new CException('Embedded relation not found');
  198. }
  199. } else {
  200. throw new CException('Unsupported model class '.$relationName);
  201. }
  202. $staticModel = new $className();
  203. $model = null;
  204. }
  205. } else {
  206. $staticModel = $model;
  207. }
  208. return array(
  209. 'model' => $model,
  210. 'staticModel' => $staticModel,
  211. 'attribute' => $attribute,
  212. 'isMongo' => $isMongo
  213. );
  214. }
  215. }