PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Field/Base.php

http://github.com/atk4/atk4
PHP | 581 lines | 221 code | 52 blank | 308 comment | 19 complexity | 018addf6b0049da26edac99040ed17e8 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Undocumented.
  4. */
  5. class Field_Base extends AbstractObject
  6. {
  7. public $auto_track_element = true;
  8. protected $table = null;
  9. public $actual_field = null;
  10. public $type = 'string';
  11. public $readonly = false;
  12. public $system = false;
  13. public $group = null;
  14. public $mandatory = false;
  15. public $has_default_value = false;
  16. public $defaultValue = null;
  17. public $allowHTML = false;
  18. /** @var array|null */
  19. public $listData = null;
  20. public $theModel = null;
  21. public $description = null;
  22. public $sortable = false;
  23. public $searchable = false;
  24. public $hint = null;
  25. public $editable = true;
  26. public $hidden = false;
  27. public $visible = true; // ???
  28. public $display = null;
  29. public $caption = null;
  30. public $placeholder = null;
  31. public $emptyText = null; // ???
  32. // Callbacks
  33. public $onField = null;
  34. /** @var Model */
  35. public $owner;
  36. /**
  37. * Implementation of generic setter-getter method which supports "UNDEFINED"
  38. * constant. This method is used by all other sette-getters.
  39. *
  40. * @param string $type Corresponds to the name of property of a field
  41. * @param mixed $value New value for a property.
  42. *
  43. * @return mixed|$this new or current pperty (if value is UNDEFINED)
  44. */
  45. public function setterGetter($type, $value = UNDEFINED)
  46. {
  47. if ($value === UNDEFINED) {
  48. return $this->$type;
  49. }
  50. $this->$type = $value;
  51. return $this;
  52. }
  53. /**
  54. * Sets the value of the field. Identical to $model[$fieldname]=$value.
  55. *
  56. * @param mixed $value new value
  57. *
  58. * @return Field $this
  59. */
  60. public function set($value = null)
  61. {
  62. $this->owner->set($this->short_name, $value);
  63. return $this;
  64. }
  65. /**
  66. * Get the value of the field of a loaded model. If model is not loaded
  67. * will return default value instead.
  68. *
  69. * @return mixed current value of a field
  70. */
  71. public function get()
  72. {
  73. if ($this->owner->loaded()
  74. || isset($this->owner->data[$this->short_name])
  75. ) {
  76. return $this->owner->get($this->short_name);
  77. }
  78. return $this->defaultValue();
  79. }
  80. /**
  81. * If field is accidentally converted to string, provide some
  82. * descriptive information.
  83. *
  84. * @return string descriptive
  85. */
  86. public function __toString()
  87. {
  88. return get_class($this)." ['".$this->short_name."']".' of '.$this->owner;
  89. }
  90. /**
  91. * Logical type of model field. This universal type is recognized by
  92. * view controllers (such as Controller_MVCForm, Controller_MVCGrid to
  93. * convert into supported field types.
  94. *
  95. * @param string $t new value
  96. *
  97. * @return string|$this current value if $t=UNDEFINED
  98. */
  99. public function type($t = UNDEFINED)
  100. {
  101. return $this->setterGetter('type', $t);
  102. }
  103. /**
  104. * Sets field caption which will be used by forms, grids and other view
  105. * elements as a label. The caption will be localized through $app->_().
  106. *
  107. * @param string $t new value
  108. *
  109. * @return string|$this current value if $t=UNDEFINED
  110. */
  111. public function caption($t = UNDEFINED)
  112. {
  113. if ($t === UNDEFINED && !$this->caption) {
  114. return ucwords(strtr(
  115. preg_replace('/_id$/', '', $this->short_name),
  116. '_',
  117. ' '
  118. ));
  119. }
  120. return $this->setterGetter('caption', $t);
  121. }
  122. /**
  123. * Sets field hint which will be used by forms.
  124. *
  125. * @param string $t new value
  126. *
  127. * @return string|$this current value if $t=UNDEFINED
  128. */
  129. public function hint($t = UNDEFINED)
  130. {
  131. return $this->setterGetter('hint', $t);
  132. }
  133. /**
  134. * Sets field placeholder (gray text inside input when it's empty).
  135. *
  136. * @param string $t new value
  137. *
  138. * @return string|$this current value if $t=UNDEFINED
  139. */
  140. public function placeholder($t = UNDEFINED)
  141. {
  142. return $this->setterGetter('placeholder', $t);
  143. }
  144. /**
  145. * While you may use visible(), editable() to include or exclude fields
  146. * from appearing in certain scenarios, you can also define a group which
  147. * you then can display instead of listing all fields manually inside
  148. * setModel(). Read more about Actual Fields.
  149. *
  150. * @param string $t new value
  151. *
  152. * @return string|$this current value if $t=UNDEFINED
  153. */
  154. public function group($t = UNDEFINED)
  155. {
  156. return $this->setterGetter('group', $t);
  157. }
  158. /**
  159. * Read only setting will affect the way how field is presented by views.
  160. * While model field is still writable directly, the Form will not try to
  161. * change the value of this field.
  162. *
  163. * @param bool $t new value
  164. *
  165. * @return bool|$this current value if $t=UNDEFINED
  166. */
  167. public function readonly($t = UNDEFINED)
  168. {
  169. return $this->setterGetter('readonly', $t);
  170. }
  171. /**
  172. * Asterisk will be displayed by the form (if field is include in "actual"
  173. * fields. This property will not affect the direct use of the field inside
  174. * model. If you would like that your model complains about empty fields,
  175. * you should edit beforeSave hook.
  176. *
  177. * @param bool $t new value
  178. *
  179. * @return bool|$this current value if $t=UNDEFINED
  180. */
  181. public function mandatory($t = UNDEFINED)
  182. {
  183. return $this->setterGetter('mandatory', $t);
  184. }
  185. /**
  186. * Set editable to false, if you want to exclude field from forms
  187. * or other means of editing data. This does not affect the actual model
  188. * values.
  189. *
  190. * @param bool $t new value
  191. *
  192. * @return bool|$this current value if $t=UNDEFINED
  193. */
  194. public function editable($t = UNDEFINED)
  195. {
  196. return $this->setterGetter('editable', $t);
  197. }
  198. /**
  199. * Configures the behavior of Form to disable tag stripping form user input.
  200. * By default all tags are stripped, setting this property to true will
  201. * no longer strip tags.
  202. *
  203. * @param bool $t new value
  204. *
  205. * @return bool|$this current value if $t=UNDEFINED
  206. */
  207. public function allowHTML($t = UNDEFINED)
  208. {
  209. return $this->setterGetter('allowHTML', $t);
  210. }
  211. /**
  212. * Setting searchable(true) will instruct Filter and similar views that
  213. * it should be possible to perform search by this field.
  214. *
  215. * @param bool $t new value
  216. *
  217. * @return bool|$this current value if $t=UNDEFINED
  218. */
  219. public function searchable($t = UNDEFINED)
  220. {
  221. return $this->setterGetter('searchable', $t);
  222. }
  223. /**
  224. * Will instruct Grid and similar views that the sorting controls must be
  225. * enabled for this field.
  226. *
  227. * @param bool $t new value
  228. *
  229. * @return bool|$this current value if $t=UNDEFINED
  230. */
  231. public function sortable($t = UNDEFINED)
  232. {
  233. return $this->setterGetter('sortable', $t);
  234. }
  235. /**
  236. * Normally views will attempt to pick most suitable way to present field.
  237. * For example, type='date' will be presented with DatePicker field in form.
  238. * You might be using add-ons or might have created your own field class.
  239. * If you would like to use it to present the field, use display(). If you
  240. * specify string it will be used by all views, otherwise specify it as
  241. * associtive array:.
  242. *
  243. * $field->display(array('form'=>'line','grid'=>'button'));
  244. *
  245. * @param mixed $t new value
  246. *
  247. * @return mixed|$this current value if $t=UNDEFINED
  248. */
  249. public function display($t = UNDEFINED)
  250. {
  251. return $this->setterGetter('display', $t);
  252. }
  253. /**
  254. * In most cases $model['field'] would match "field" inside a database. In
  255. * some cases, however, you would want to use different database field. This
  256. * can happen when you join multiple tables and 'field' appears in multiple
  257. * tables.
  258. *
  259. * You can specify actual field when you declare a field within a model:
  260. *
  261. * $model->addField('modelfield','dbfield');
  262. *
  263. * If you are unable to use addField (such as using custom field class),
  264. * you can use actual() modifier:
  265. *
  266. * $model->add('filestore/File','modelfield')->actual('dbfield');
  267. *
  268. * Another potential use is if your database structure does not match
  269. * model convention:
  270. *
  271. * $model->hasOne('Book')->actual('IDBOOK');
  272. *
  273. * @param string $t new value
  274. *
  275. * @return string|$this current value if $t=UNDEFINED
  276. */
  277. public function actual($t = UNDEFINED)
  278. {
  279. return $this->setterGetter('actual_field', $t);
  280. }
  281. /**
  282. * Marking field as system will cause it to always be loaded, even if
  283. * it's not requested through Actual Fields. It will also hide the field
  284. * making it dissapear from Grids and Forms. A good examples of system
  285. * fields are "id" or "created_dts".
  286. *
  287. * @param bool $t new value
  288. *
  289. * @return bool|$this current value if $t=UNDEFINED
  290. */
  291. public function system($t = UNDEFINED)
  292. {
  293. if ($t === true) {
  294. $this->editable(false)->visible(false);
  295. }
  296. return $this->setterGetter('system', $t);
  297. }
  298. /**
  299. * Hide field. Not sure!
  300. *
  301. * @param bool $t new value
  302. *
  303. * @return bool|$this current value if $t=UNDEFINED
  304. */
  305. public function hidden($t = UNDEFINED)
  306. {
  307. return $this->setterGetter('hidden', $t);
  308. }
  309. /**
  310. * This will provide a HTML settings on a field for maximum field size.
  311. * The length of a field will not be enforced by this setting.
  312. *
  313. * @param int $t new value
  314. *
  315. * @return int|$this current value if $t=UNDEFINED
  316. */
  317. public function length($t = UNDEFINED)
  318. {
  319. return $this->setterGetter('length', $t);
  320. }
  321. /**
  322. * Default Value is used inside forms when you present them without loaded
  323. * data. This does not change how model works, which will simply avoid
  324. * including unchanged field into insert/update queries.
  325. *
  326. * @param bool $t new value
  327. *
  328. * @return bool|$this current value if $t=UNDEFINED
  329. */
  330. public function defaultValue($t = UNDEFINED)
  331. {
  332. if ($t !== UNDEFINED) {
  333. $this->has_default_value = true;
  334. }
  335. return $this->setterGetter('defaultValue', $t);
  336. }
  337. /**
  338. * Controls field appearance in Grid or similar views.
  339. *
  340. * @param bool $t new value
  341. *
  342. * @return bool|$this current value if $t=UNDEFINED
  343. */
  344. public function visible($t = UNDEFINED)
  345. {
  346. return $this->setterGetter('visible', $t);
  347. }
  348. /**
  349. * Supplies a list data for multi-value fields (selects, radio buttons,
  350. * checkbox area, autocomplete). You may also use enum(). This setting
  351. * is typically used with a static falues (Male / Female), if your field
  352. * values could be described through a different model, use setModel()
  353. * or better yet - hasOne().
  354. *
  355. * @param array $t Array( id => val )
  356. *
  357. * @return array|$this current value if $t=UNDEFINED
  358. */
  359. public function listData($t = UNDEFINED)
  360. {
  361. if ($this->type() === 'boolean' && $t !== UNDEFINED) {
  362. $this->owner->addHook('afterLoad,afterUpdate,afterInsert', function ($m) use ($t) {
  363. // Normalize boolean data
  364. $val = array_search($m->data[$this->short_name], $t);
  365. if ($val === false) {
  366. return;
  367. } // do nothing
  368. $m->data[$this->short_name] = (boolean) !$val;
  369. });
  370. $this->owner->addHook('beforeUpdate,beforeInsert', function ($m, &$data) use ($t) {
  371. // De-Normalize boolean data
  372. $val = (int) (!$data[$this->short_name]);
  373. if (!isset($t[$val])) {
  374. return;
  375. } // do nothing
  376. $data[$this->short_name] = $t[$val];
  377. });
  378. }
  379. return $this->setterGetter('listData', $t);
  380. }
  381. /**
  382. * What to display when nothing is selected or entered? This will be
  383. * displayed on a drop-down when no value is selected: ("Choose ..")
  384. * if you are using this setting with a text field it will set a
  385. * placeholder HTML property.
  386. *
  387. * @param string $t new value
  388. *
  389. * @return string|$this current value if $t=UNDEFINED
  390. */
  391. public function emptyText($t = UNDEFINED)
  392. {
  393. return $this->setterGetter('emptyText', $t);
  394. }
  395. /**
  396. * Pass a callback to onField() which is executed with the argument
  397. * containing a field view, when model is associated with a form.
  398. *
  399. * @param callback $c new value
  400. *
  401. * @return string|$this current value if $t=UNDEFINED
  402. */
  403. public function onField($c = UNDEFINED)
  404. {
  405. return $this->setterGetter('onField', $c);
  406. }
  407. /**
  408. * Will execute setModel() on a field. Some fields will change their
  409. * behaviour with this. The value is a string (either Model_Book or Book)
  410. * but you might be able to use object also.
  411. *
  412. * I suggest to use $model->hasOne($model) instead of setModel($model)
  413. *
  414. * @param string $t new value
  415. *
  416. * @return string|$this current value if $t=UNDEFINED
  417. */
  418. public function setModel($t = UNDEFINED)
  419. {
  420. return $this->setterGetter('theModel', $t);
  421. }
  422. /**
  423. * Returns current model. This is different than other setters getters,
  424. * but it's done to keep consistency with the rest of Agile Toolkit.
  425. *
  426. * @return string current associated model Class
  427. */
  428. public function getModel()
  429. {
  430. return $this->theModel;
  431. }
  432. /**
  433. * Same as listData().
  434. *
  435. * @param array $t Array( id => val )
  436. *
  437. * @return array current value if $t=UNDEFINED
  438. */
  439. public function setValueList($t)
  440. {
  441. return $this->listData($t);
  442. }
  443. /**
  444. * Similar to listData() but accepts array of values instead of hash:.
  445. *
  446. * listData(array(1=>'Male', 2=>'Female'));
  447. * enum(array('male','female'));
  448. *
  449. * The value will be stored in database and also displayed to user.
  450. *
  451. *
  452. * @param array $t Array( id => val )
  453. *
  454. * @return array current value if $t=UNDEFINED
  455. */
  456. public function enum($t)
  457. {
  458. return $this->listData(array_combine($t, $t));
  459. }
  460. /**
  461. * If is set, it contains the correct table that stores this field.
  462. * Used in join environment.
  463. *
  464. * @param string $t
  465. *
  466. * @return string|$this
  467. */
  468. public function table($t = UNDEFINED)
  469. {
  470. return $this->setterGetter('table', $t);
  471. }
  472. /**
  473. * This is used in Controller_Data_SQL to sanitize the value forwarded to dsql.
  474. * @todo sanitize the value.
  475. *
  476. * @param mixed $value
  477. *
  478. * @return mixed
  479. */
  480. public function sanitize($value)
  481. {
  482. return $value;
  483. }
  484. /**
  485. * Converts true/false into boolean representation according to the "enum"
  486. *
  487. * @param mixed $value
  488. *
  489. * @return int|null
  490. */
  491. public function getBooleanValue($value)
  492. {
  493. if ($value === null) {
  494. return;
  495. }
  496. if ($this->listData) {
  497. reset($this->listData);
  498. list(, $yes_value) = @each($this->listData);
  499. list(, $no_value) = @each($this->listData);
  500. if ($no_value === null) {
  501. $no_value = '';
  502. }
  503. /* not to convert N to Y */
  504. if ($yes_value == $value) {
  505. return $yes_value;
  506. }
  507. if ($no_value == $value) {
  508. return $no_value;
  509. }
  510. } else {
  511. $yes_value = 1;
  512. $no_value = 0;
  513. }
  514. return $value ? $yes_value : $no_value;
  515. }
  516. // {{{ Deprecated methods
  517. /**
  518. * @deprecated 4.3.0 use mandatory() instead
  519. *
  520. * @param bool $t new value
  521. *
  522. * @return bool|$this current value if $t=UNDEFINED
  523. */
  524. public function required($t = UNDEFINED)
  525. {
  526. return $this->mandatory($t);
  527. }
  528. // }}}
  529. }