PageRenderTime 141ms CodeModel.GetById 23ms RepoModel.GetById 8ms app.codeStats 0ms

/atk4-addons/mvc/MVCFieldDefinition.php

https://github.com/mahimarathore/mahi
PHP | 448 lines | 299 code | 35 blank | 114 comment | 63 complexity | c529d9fbc4b79f4c55a99a429d9fa344 MD5 | raw file
Possible License(s): AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * This class contains field definition
  4. *
  5. * Most important and interesting feature of the definition is reference to other entities
  6. * Reference could be of two types:
  7. * 1) Reference Model - created by refModel($model_name) method
  8. * this type useful for cases when you need to show/edit entity defined by ID in current model
  9. * i.e. we define Invoice model which has contractor_id field, we define this field as:
  10. * $model->newField('contractor_id')->refModel('Model_Contractor')->displayField('legal_name'),
  11. * and this makes model display contractor_id field as string selected from Model_Contractor's table,
  12. * as well as edit this field in Forms using referenced model
  13. * Field is being selected in resulting query as "(select legal_name from ...) contractor_id", i.e.
  14. * no joins used
  15. * 2) Related Entity - created by relEntity() and addRelatedEntity() methods
  16. * this type can be used together with type (1)
  17. * This method results in joins in Model's select
  18. * i.e. we define Contractor Model and we need to show/edit address fields, which are in separate table
  19. * We join related entity by calling:
  20. * $model->addRelatedEntity('a','address','reg_address_id','left outer');
  21. * this will allow to join table 'address' with alias 'a' by 'a.id=contractor.reg_address_id'
  22. * Then we add fields from the joined table to Contractor Model as this:
  23. * $model->newField('country_id')
  24. * ->caption('Region')
  25. * ->refModel('Model_Country')
  26. * ->relEntity('a','country_id');
  27. * this will add field 'country_id' from table with alias 'a' (which is 'address', as defined before), this
  28. * field will be named 'country_id' in our model (and referenced like this in grids/forms), and will
  29. * have reference model Model_Country for edit purposes
  30. * $model->newField('state')
  31. * ->datatype('string')
  32. * ->relEntity('a','state');
  33. * this will ass field 'state' from table with alias 'a', this field will be referenced as 'state' in
  34. * Model_Contractor
  35. * Setting related entity with addRelatedEntity() will not cause join explicitely, joins are made on the basis of
  36. * fields defined in model, i.e. only if we add field with relEntity() defined.
  37. * This concept is complicated, so here is another example of related entities join.
  38. * We create a model with subsequent joins: Model_Company, which is based on 'system_sys_user table and needs
  39. * 'system' table to have access to system data and 'contractor' table, which is joined to 'system'. We do like this:
  40. * $model->addRelatedEntity('s','system','system_id'); // joined 'system' by 'system_sys_user.system_id'
  41. * $model->newField('system_id')
  42. * ->relEntity('s','id') // field 's.id' will appear in our model as 'system_id'
  43. * ->displayField('id') // data to display will be taken from field 'system.id'
  44. *
  45. * Created on 07.04.2009
  46. */
  47. class MVCFieldDefinition {
  48. protected $owner;
  49. protected $api;
  50. protected $instance_name;
  51. protected $name;
  52. protected $caption;
  53. protected $datatype;
  54. protected $displaytype;
  55. protected $length;
  56. protected $list_data;
  57. protected $ref_model;
  58. protected $default_value=null;
  59. protected $entity_alias=null;
  60. protected $dbname=null;
  61. protected $display_field='name'; // field name which will be used to select entity name from DB for reference fields
  62. protected $readonly=false; // prevents edit
  63. protected $required=false;
  64. protected $visible=true;
  65. protected $editable=true; // to show or not on forms. allows edit in distinction with readonly
  66. protected $searchable=false;
  67. protected $sortable=false;
  68. protected $system=false; // if true, this field never shown to user, but is editable
  69. protected $calculated=false;
  70. protected $aggregate=false; // aggregated status. if true, field is wrapped in sum() in selects
  71. // it is also excluded from edits
  72. protected $allow_html=false; // allow or not HTML code in field value
  73. // fields with allowed HTML should be processed separately to avoid injections, etc.
  74. protected $validation=null;
  75. protected $params=array(); // additional parameters required on, say, field calculation
  76. function __construct($owner){
  77. $this->owner=$owner;
  78. $this->api=$owner->api;
  79. $this->instance_name=$this->owner->name.'_undefined';
  80. }
  81. public function name($new_value=null) {
  82. if (is_null($new_value))
  83. return $this->name;
  84. else {
  85. $this->name = $new_value;
  86. $this->instance_name=$this->owner->name.'_'.$new_value;
  87. return $this;
  88. }
  89. }
  90. /* The following function can be used to describe field type as well as get the value, if called without argument */
  91. /* caption([new value]) - Sets label for forms, grid columns. Uses prettyfied 'name' if omitted. */
  92. function caption($new_value=null) {
  93. if (is_null($new_value)) {
  94. return (empty($this->caption))?ucwords(str_replace('_',' ',$this->name)):$this->caption;
  95. }
  96. else {
  97. $this->caption = $new_value;
  98. return $this;
  99. }
  100. }
  101. /* readonly([boolean]) - Model will NEVER attempt to update this field. Calculated fields are readonly by default. */
  102. function readonly($new_value=null) {
  103. if (is_null($new_value)){
  104. return $this->readonly;
  105. } else {
  106. $this->readonly = $new_value;
  107. return $this;
  108. }
  109. }
  110. /* editable([boolean]) - Field will not appear on forms by default. If added anyway, will use HTML readonly property.
  111. You still can update this field manually through ->set() */
  112. function editable($new_value=null){
  113. if (is_null($new_value))
  114. return $this->editable;
  115. else {
  116. $this->editable = $new_value;
  117. return $this;
  118. }
  119. }
  120. /* allowHTML([boolean]) - By default fields will strip HTML tags for security. If true, will not strip HTML from field */
  121. function allowHTML($new_value=null){
  122. if (is_null($new_value))
  123. return $this->allow_html;
  124. else {
  125. $this->allow_html = $new_value;
  126. return $this;
  127. }
  128. }
  129. /* searchable([boolean]) - Field will apear in filter. When generating SQL, will be automatically indexed. Also makes
  130. field sortable by default */
  131. function searchable($new_value=null){
  132. if(is_null($new_value)){
  133. return $this->searchable;
  134. }else{
  135. if($new_value===true)$this->sortable(true); // sort searchable fields by default
  136. $this->searchable=$new_value;
  137. return $this;
  138. }
  139. }
  140. /* sortable([boolean]) - Grids will allow ordering results by this field. You can use sortable with physical or
  141. calculated fields */
  142. function sortable($new_value=null){
  143. if(is_null($new_value)){
  144. return $this->sortable;
  145. }else{
  146. $this->sortable=$new_value;
  147. return $this;
  148. }
  149. }
  150. /* type([boolean]) - defines field type. Consult documentation for available types. If you use your own type,
  151. make sure to define display() property or type_correspondence in Controller */
  152. function type($new_value = null){
  153. /* 'string'(default),'date','datetime','text','int','real','boolean','reference','password','list' */
  154. /*
  155. if (!in_array($new_value,array(
  156. 'string','date','datetime','text','readonly',
  157. 'int','real','money','boolean','reference','reference_id','password','list',
  158. 'daytime','daytime_total','image','radio','file'
  159. */
  160. if (is_null($new_value)){
  161. return (empty($this->datatype))?'string':$this->datatype;
  162. } else {
  163. $this->datatype = $new_value;
  164. return $this;
  165. }
  166. }
  167. /* display([array]) - override controller's type correspondence. If string is specified it will be used a form's field
  168. type */
  169. function display($new_value = null, $context = null) {
  170. if (is_null($new_value)){
  171. if (!$context){
  172. return (empty($this->display))?'default':$this->display;
  173. } else {
  174. if (isset($this->display[$context])){
  175. return $this->display[$context];
  176. } else {
  177. return null;
  178. }
  179. }
  180. } else {
  181. if(!is_array($new_value)){
  182. $new_value=array('form'=>$new_value);
  183. }
  184. $this->display = $new_value;
  185. }
  186. return $this;
  187. }
  188. /* system([boolean]) - system fields are always loaded, even if you do not ask for them. They are hidden by default, but
  189. you cacn enable with editable(true). ID is system field. last modified date, would also be system field. */
  190. function system($new_value=null){
  191. if (is_null($new_value))
  192. return $this->system;
  193. else {
  194. $this->system = $new_value;
  195. if($new_value===true)$this->editable(false); // hide system fields by default
  196. return $this;
  197. }
  198. }
  199. /* calculated([boolean|callable|string]) - calculated field will execute a custom SQL statement returned by calculate_myfieldnamehere()
  200. function. You can also specify string of custom function or callable type. */
  201. function calculated($new_value=null){
  202. if (is_null($new_value))
  203. return $this->calculated;
  204. else {
  205. if($new_value===true){
  206. // check some stuff
  207. if($this->datatype()=='reference'||$this->datatype()=='password'||$this->datatype()=='list')
  208. throw new Exception_InitError("Datatype '".$this->datatype()."' cannot be calculated");
  209. $this->readonly(true);
  210. }
  211. $this->calculated = $new_value;
  212. return $this;
  213. }
  214. }
  215. /* aggregate([boolean|string]) - defines name of the agregate function (such as sum, avg, max, etc) which will be applied
  216. on this field when you enable grouping */
  217. function aggregate($new_value = null){
  218. if (is_null($new_value))
  219. return $this->aggregate;
  220. else {
  221. $this->aggregate = $new_value;
  222. if($new_value===true)$this->editable(false);
  223. return $this;
  224. }
  225. }
  226. /* length([int]) - define maximum length of the field. Would be used by SQL generator, inside forms. If not defined, or
  227. false is specified, then check will not be performed. Some field types might default to certain length if not
  228. specified */
  229. function length($new_value = null) {
  230. if (is_null($new_value))
  231. return (empty($this->length))?255:$this->length;
  232. else {
  233. $this->length = $new_value;
  234. return $this;
  235. }
  236. }
  237. /* default([value]) - specify default value for a field. This will be inserted into form if no record is loaded. Also if
  238. you are adding new record, unspecified fields will use default value */
  239. function defaultValue($value='**not_set**'){
  240. if($value==='**not_set**'){
  241. return $this->default_value;
  242. }
  243. $this->default_value=$value;
  244. $this->owner->setDefaultField($this->name,$value);
  245. return $this;
  246. }
  247. /* required([boolean|string]) - specifies that this field is typically required by user interface. Manual manipulation of the
  248. record would not trigger exception though. Form will typically display asterisk next to field. If string is specified,
  249. it used as default error message */
  250. function required($new_value=null) {
  251. if (is_null($new_value))
  252. return $this->required;
  253. else {
  254. $this->required = $new_value;
  255. return $this;
  256. }
  257. }
  258. /* validate([string|callable]) - Sets validation method for this field. If string is specified it's used as a method. */
  259. function validate($new_value=null){
  260. if(is_null($new_value)){
  261. return $this->validation;
  262. }else{
  263. $this->validation=$new_value;
  264. return $this;
  265. }
  266. }
  267. /* visible([boolean]) - Display or hides field form grids and forms */
  268. public function visible($new_value=null){
  269. if(is_null($new_value)){
  270. return $this->visible;
  271. }
  272. $this->visible=$new_value;
  273. return $this;
  274. }
  275. /* When called on a field with foreign key (reference), this create a new field and somehow links it
  276. function addRelation($model,$alias_name,$referenced_field=null){
  277. */
  278. function refModel($model=null,$loadref=true) {
  279. if (!is_null($model)) {
  280. $noid=str_replace('_id','',$this->name);
  281. if($noid==$this->name || $this->owner->fieldExists($noid)){
  282. $this->datatype('reference');
  283. $this->ref_model = $model;
  284. return $this;
  285. }
  286. $r2=$this->owner->addField($noid)
  287. ->visible(true)
  288. ->editable(false)
  289. ->sortable($this->sortable())
  290. ->readonly(true)
  291. ->datatype('reference');
  292. if($this->entity_alias)$r2->relEntity($this->entity_alias);
  293. $this->system(true);
  294. $this->editable(true);
  295. $this->datatype('reference_id');
  296. $this->ref_model = $model;
  297. return $this;
  298. }
  299. else{
  300. if(!$this->ref_model)throw new Exception_InitError("No reference model set for ".$this->owner->name."::$this->name");
  301. if(!is_object($this->ref_model))$this->ref_model=$this->owner->add($this->ref_model);
  302. if(!$loadref)return $this->ref_model;
  303. // trying to load data depending on owner field value
  304. if($this->owner->isInstanceLoaded() && $id=$this->owner->get($this->name))$this->ref_model->loadData($id,true);
  305. return $this->ref_model;
  306. }
  307. }
  308. /* Several obsolete functions. To be removed in 4.2 */
  309. /* obsolete, use allowHTML */
  310. public function allow_html($new_value=null){
  311. return $this->allowHTML($new_value);
  312. }
  313. public function mandatory($new_value=null){
  314. return $this->required($new_value);
  315. }
  316. function datatype($new_value = null) {
  317. return $this->type($new_value);
  318. }
  319. function displaytype($new_value=null){
  320. // OBSOLETE due to
  321. return $this->display($new_value);
  322. }
  323. function listData($new_value=null) {
  324. if (is_null($new_value)) {
  325. return $this->list_data;
  326. }
  327. else {
  328. $this->list_data = $new_value;
  329. return $this;
  330. }
  331. }
  332. /**
  333. * @return boolean TRUE if field used in external entity
  334. */
  335. public function isExternal() {
  336. return (empty($this->entity_alias)||$this->entity_alias=='')?false:true; // aliases defined only for external fields
  337. }
  338. public function alias($new_value=null) {
  339. if (is_null($new_value)) {
  340. return $this->entity_alias;
  341. }
  342. else {
  343. $this->entity_alias = $new_value;
  344. return $this;
  345. }
  346. }
  347. public function dbname($new_value=null) {
  348. if (is_null($new_value)) {
  349. return (empty($this->dbname))?$this->name:$this->dbname;
  350. }
  351. else {
  352. $this->dbname = $new_value;
  353. return $this;
  354. }
  355. }
  356. public function displayField($new_value=null){
  357. if(is_null($new_value))return $this->display_field;
  358. else{
  359. $this->display_field=$new_value;
  360. return $this;
  361. }
  362. }
  363. /**
  364. * Define properties for external fields (from related entities)
  365. * @param string $alias alias for related entity in query
  366. * @param string $dbname name of field in db (equal of name if not set)
  367. * @return $this
  368. */
  369. public function relEntity($alias, $dbname=null) {
  370. $this->entity_alias = $alias;
  371. if (is_null($dbname))
  372. $dbname = $this->name;
  373. $this->dbname = $dbname;
  374. return $this;
  375. }
  376. /**
  377. * Goal: construct correct field definition in select sql
  378. * @param string $alias main entity alias in query
  379. * @return string sometring like 'a.dbname as fieldname'
  380. */
  381. public function getDBfield($alias = null) {
  382. if (!empty($this->entity_alias)) {
  383. $res = $this->entity_alias.'.'.$this->dbname;
  384. if ($this->dbname!=$this->name)
  385. $res .= ' as '.$this->name;
  386. }else{
  387. $res = ((is_null($alias))?'':$alias.'.').$this->dbname();
  388. if ($this->dbname()!=$this->name)
  389. $res .= ' as '.$this->name;
  390. }
  391. return $res;
  392. }
  393. /**
  394. * Adds a parameter to a field
  395. * Parameters may be required on some custom calculations
  396. * You can specify either single value (->addParam("my string")) or an array of values
  397. * (->addParam(array('p1'=>1,'p2'=>'my string')))
  398. *
  399. * In second case all array values will be appended to existing values
  400. */
  401. public function parameter($param = null){
  402. if(!is_null($param)){
  403. if(is_array($param))$this->params=array_merge($this->params,$param);
  404. else $this->params=$param;
  405. return $this;
  406. }
  407. return $this->params;
  408. }
  409. public function getOwner(){
  410. return $this->owner;
  411. }
  412. }
  413. ?>