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