PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/Field.php

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