Source for file dbchoicelist.php

Documentation is available at dbchoicelist.php

  1. <?php
  2. /**
  3. * A choice list based on an SQL statement
  4. *
  5. @author    Stuart Prescott
  6. @copyright  Copyright Stuart Prescott
  7. @license    http://opensource.org/licenses/gpl-license.php GNU Public License
  8. @version    $Id$
  9. @package    Bumblebee
  10. @subpackage FormsLibrary
  11. */
  12.  
  13. /** Load ancillary functions */
  14. require_once 'inc/typeinfo.php';
  15.  
  16. /** DBO parent object */
  17. require_once 'dbobject.php';
  18.  
  19. /**
  20. * A choice list based on an SQL statement.
  21. *
  22. * Primitive class on which selection lists can be built from the
  23. * results of an SQL query. This may be used to determine the choices
  24. * that a user is permitted to select (e.g. dropdown list or radio buttons)
  25. * or also to permit additional entries to be created.
  26. *
  27. * Used in a 1:many relationship (i.e. a field in a table that is the
  28. * primary key in another table)
  29. *
  30. * Note that this class has no real way of displaying itself properly,
  31. * so it would usually be inherited and the descendent class used.
  32. *
  33. * Typical usage:
  34. * <code>
  35. * $f = new RadioList("myfield", "Field name");
  36. * $f->connectDB("mytable", array("id", "name"));
  37. * $f->setFormat("id", "%s", array("name"));
  38. * $newentryfield = new TextField("name","");
  39. * $newentryfield->namebase = "newentry-";
  40. * $newentryfield->suppressValidation = 0;
  41. * $f->list->append(array("-1","Create new: "), $newentryfield);
  42. *  </code>
  43. *
  44. @package    Bumblebee
  45. @subpackage FormsLibrary
  46. */
  47. class DBChoiceList extends DBO {
  48.   /** @var mixed  string or array (preferably) that defines the LEFT JOIN  */
  49.   var $join;
  50.   /** @var string an SQL restriction clause to be used in a WHERE  */
  51.   var $restriction;
  52.   /** @var string column for ORDER BY  */
  53.   var $order;
  54.   /** @var string LIMIT data  */
  55.   var $limit;
  56.   /** @var boolean   SELECT DISTINCT */
  57.   var $distinct;
  58.   /** @var boolean   list is editable  */
  59.   var $editable = 0;
  60.   /** @var boolean   list is extendable by adding new values  */
  61.   var $extendable = 0;
  62.   /** @var boolean   selected value in list has changed */
  63.   var $changed = 0;
  64.   /** @var tristate  NULL => do not restrict on deleted column, otherwise WHERE deleted = $deleted */
  65.   var $deleted;
  66.   /** @var array     the list of choices available  */
  67.   var $choicelist;
  68.   /** @var integer   the number of choices available */
  69.   var $length;
  70.   /** @var array     the list of appended entries  */
  71.   var $appendedfields;
  72.   /** @var array     the list of prepended entries  */
  73.  
  74.   /**
  75.   * Construct the DBlist object.
  76.   *
  77.   * Construct a new DBList object based on:
  78.   *     - database table ($table)
  79.   *     - calling for the fields in the array (or scalar) $fields
  80.   *     - with an SQL restriction (WHERE clause) $restriction
  81.   *     - ordering the listing by $order
  82.   *     - using the field $idfield as the control variable in the list
  83.   *       (i.e. the value='' in a radio list etc)
  84.   *     - with an SQL LIMIT statement of $limit
  85.   *
  86.   * @param string $table  the table to be queried for filling
  87.   * @param mixed $fields  string for the field or array of field names
  88.   * @param string $restriction  an SQL restriction clause to be used in a WHERE
  89.   * @param string $order  fields for SQL ORDER clause
  90.   * @param string $idfield  the field that should be used as the uniquely identifying value
  91.   * @param string $limit  for LIMIT clause
  92.   * @param mixed $join  string or array (preferably) that defines the LEFT JOIN
  93.   * @param boolean $distinct return only DISTINCT rows (default: false)
  94.   * @param boolean $deleted deleted=true/false in SQL; NULL means don't restrict
  95.   */
  96.   function DBChoiceList($table$fields=''$restriction='',
  97.                   $order=''$idfield='id'$limit=''$join=''$distinct=false$deleted=NULL{
  98.     #$this->DEBUG=10;
  99.     $this->DBO($table''$idfield);
  100.     $this->fields = (is_array($fields$fields array($fields));
  101.     $this->restriction = $restriction;
  102.     #$this->idfield = $idfield;
  103.     $this->order = $order;
  104.     $this->limit = $limit;
  105.     $this->distinct = $distinct;
  106.     $this->deleted = $deleted;
  107.     if (is_array($join)) {
  108.       $this->join = $join;
  109.     elseif ($join == ''{
  110.       $this->join = array();
  111.     else {
  112.       $this->join = array($join=>"$join.id=${join}id");
  113.     }
  114.     $this->choicelist = array();
  115.     $this->appendedfields = array();
  116.     $this->prependedfields = array();
  117.     $this->fill();
  118.   }
  119.  
  120.   /**
  121.   * Fill the object from the database using the already initialised
  122.   * members (->table etc).
  123.   */
  124.   function fill({
  125.     //preDump($this);
  126.     global $TABLEPREFIX;
  127.     $fields $this->fields;
  128.     $fields[= isset($this->idfieldreal?
  129.                       array($this->idfieldreal$this->idfield:
  130.                       $this->idfield;
  131.     $aliasfields array();
  132.     foreach ($fields as $v{
  133.       $aliasfields[is_array($v"$v[0] AS $v[1]$v;
  134.     }
  135.     $f implode(", "$aliasfields);
  136.     $joinSyntax '';
  137.     foreach ($this->join as $k => $v{
  138.       $joinSyntax .= 'LEFT JOIN '.$TABLEPREFIX.$k.' AS '.$k.' ON '.$v.' ';
  139.     }
  140.     $restrictions $this->restriction;
  141.     if ($this->deleted !== NULL{
  142.       if ($this->deleted{
  143.         $restrictions ($restrictions $restrictions.' AND ' ''$this->table.'.deleted=1 ';
  144.       else {
  145.         $restrictions ($restrictions $restrictions.' AND ' ''$this->table.'.deleted<>1 ';
  146.       }
  147.     }
  148.     $q 'SELECT '.($this->distinct?'DISTINCT ':'').$f
  149.         .' FROM '.$TABLEPREFIX.$this->table.' AS '.$this->table.' '
  150.         .$joinSyntax
  151.         .($restrictions != '' "WHERE $restrictions '')
  152.         .($this->order != '' "ORDER BY {$this->order" : '')
  153.         .($this->limit != '' "LIMIT {$this->limit" : '');
  154.     $sql = db_get($q, $this->fatal_sql);
  155.     if ($sql{
  156.       //then the SQL query was unsuccessful and we should bail out
  157.       return 0;
  158.     } else {
  159.       $this->choicelist = array();
  160.       while ($g db_fetch_array($sql)) {
  161.         $this->choicelist[$g#['key']] = $g['value'];
  162.       }
  163.       $this->length = count($this->choicelist);
  164.       //if this fill() has been called after extra fields have been prepended
  165.       //or appended to the field list, then we need to re-add them as they
  166.       //will be lost by this process
  167.       $this->_reAddExtraFields();
  168.     }
  169.     return 1;
  170.   }
  171.  
  172.   /**
  173.    * Construct an array suitable for storing the field and the values it takes for later reuse
  174.    * @param array $values list of values to be displayed
  175.    * @param Field $field  Field object associated with these values
  176.    */
  177.   function _mkaddedarray($values, $field='') {
  178.     $a = array();
  179.     for ($i=0; $i < count($values); $i++) {
  180.       $a[$this->fields[$i]] $values[$i];
  181.     }
  182.     $a['_field'] = is_object($field) ? clone($field) : $field;
  183.     return $a;
  184.   }
  185.  
  186.   /**
  187.    * Clone the array field structure
  188.    * @param array $a field structure array (see _mkaddedarray())
  189.    * @return array clone of $a
  190.    */
  191.   function _addedclone($a) {
  192.     $b = $a;
  193.     $b['_field'] = is_object($a['_field']) ? clone($a['_field']) : $a['_field'];
  194.     return $b;
  195.   }
  196.  
  197.   /**
  198.   * append a special field (such as "Create new:") to the choicelist
  199.   *
  200.   * Keep a copy of the field so it can be added again later if
  201.   * necessary, and then use a private function to actually do the adding
  202.   *
  203.   * @param array $values list of values to be displayed
  204.   * @param Field $field (optional) a field class object to be placed next to this entry, if possible
  205.   */
  206.   function append($values, $field='') {
  207.     $fa = $this->_mkaddedarray($values$field);
  208.     //keep a copy of the field so it can be added again after a fill()
  209.     $this->appendedfields[$fa;
  210.     $this->_append($fa);
  211.   }
  212.  
  213.   /**
  214.   * prepend a special field (such as "Create new:") to the choicelist
  215.   *
  216.   * Keep a copy of the field so it can be added again later if
  217.   * necessary, and then use a private function to actually do the adding
  218.   *
  219.   * @param array $values list of values to be displayed
  220.   * @param Field $field (optional) a field class object to be placed next to this entry, if possible
  221.   */
  222.   function prepend($values, $field='') {
  223.     $fa = $this->_mkaddedarray($values$field);
  224.     //keep a copy of the field so it can be added again after a fill()
  225.     $this->prependedfields[$fa;
  226.     $this->_prepend($fa);
  227.   }
  228.  
  229.   /**
  230.   * private functions _append and _prepend that will actually add the field
  231.   * to the field list after it has been properly constructed and saved for
  232.   * future reference
  233.   */
  234.   function _append($fa) {
  235.     array_push($this->choicelist$this->_addedclone($fa));
  236.   }
  237.  
  238.   function _prepend($fa) {
  239.     array_unshift($this->choicelist$this->_addedclone($fa));
  240.   }
  241.  
  242.   /**
  243.   * add back in the extra fields that were appended/prepended to the
  244.   * choicelist. Use this if they fields are lost due to a fill()
  245.   */
  246.   function _reAddExtraFields() {
  247.     foreach ($this->appendedfields as $v{
  248.       $this->_append($v);
  249.     }
  250.     foreach ($this->prependedfields as $v{
  251.       $this->_prepend($v);
  252.     }
  253.   }
  254.  
  255.   function display() {
  256.     return $this->text_dump();
  257.   }
  258.  
  259.   function text_dump() {
  260.     return "<pre>DBChoiceList:\n".print_r($this->choicelisttrue)."</pre>";
  261.   }
  262.  
  263.   /**
  264.   * update the value of the list based on user data:
  265.   *   - if it is within the range of current values, then take the value
  266.   *   - if the field contains a new value (and is allowed to) then keep
  267.   *     an illegal value, mark as being changed, and wait until later for
  268.   *     the field to be updated
  269.   *   - if the field contains a new value (and is not allowed to) or an
  270.   *     out-of-range value, then flag as being invalid
  271.   *
  272.   * @param string $newval the (possibly) new value for the field
  273.   * @param array ancillary user data (passed on to any appended or prepended fields)
  274.   */
  275.   function update($newval, $data) {
  276.     $this->log('DBChoiceList update: (changed='.$this->changed.', id='.$this->id.', newval='.$newval.')');
  277.     if (isset($newval)) {
  278.       //check to see if the newval is legal (does it exist on our choice list?)
  279.       $isExisting = 0;
  280.       foreach ($this->choicelist as $v{
  281.         $this->log('('.$isExisting.':'.$v[$this->idfield].':'.$newval.')');
  282.         if ($v[$this->idfield== $newval && $v[$this->idfield>= 0{
  283.           $isExisting = 1;
  284.           break;
  285.         }
  286.       }
  287.       if ($isExisting) {
  288.         // it is a legal, existing value, so we adopt it
  289.         $this->log('isExisting');
  290.         $this->changed += ($newval != $this->id);
  291.         $this->id = $newval;
  292.         $this->isValid = 1;
  293.         //isValid handling done by the Field that inherits it
  294.       } elseif ($this->extendable{
  295.         // then it is a new value and we should accept it
  296.         $this->log('isExtending');
  297.         $this->changed += 1;
  298.         // If we are extending the list, then we should have a negative
  299.         // number as the current value to trip the creation of the new
  300.         // entry later on in sync()
  301.         $this->id = -1;
  302.         foreach ($this->choicelist as $k => $v{
  303.           //preDump($v);
  304.           if (isset($v['_field']) && is_object($v['_field'])) {
  305.             $this->choicelist[$k]['_field']->update($data);
  306.             $this->isValid += $this->choicelist[$k]['_field']->isValid();
  307.           }
  308.         }
  309.       } else {
  310.         $this->log('isInvalid');
  311.         // else, it's a new value and we should not accept it
  312.         $this->isValid = 0;
  313.       }
  314.     }
  315.     if (! $this->isValid{
  316.       $this->errorMessage .= '<br />'.T_('Invalid data:').' '.$this->longname;
  317.     }
  318.     #echo " DBchoiceList::changed=$this->changed<br />";
  319.     return $this->isValid;
  320.   }
  321.  
  322.   /**
  323.   * sets the current value of the field
  324.   *
  325.   * (providing interface to Field object)
  326.   */
  327.   function set($value) {
  328.     #echo "DBchoiceList::set = $value<br/>";
  329.     $this->id = $value;
  330.   }
  331.  
  332.   /**
  333.   * synchronise with the database
  334.   *
  335.   * This also creates the true value for this field if it is undefined
  336.   * @return code from statuscodes
  337.   * @global string prefix for SQL table names
  338.   */
  339.   function sync() {
  340.     global $TABLEPREFIX;
  341.     #preDump($this);
  342.     // If the input isn't valid then bail out straight away
  343.     if (! $this->changed{
  344.       $this->log('not syncing: changed='.$this->changed);
  345.       return STATUS_NOOP;
  346.     } elseif (! $this->isValid{
  347.       $this->log('not syncing: valid='.$this->isValid);
  348.       return STATUS_ERR;
  349.     }
  350.     //echo "Syncing...<br />";
  351.     if ($this->id == -1{
  352.       //it's a new record, insert it
  353.       $vals = $this->_sqlvals();
  354.       $q 'INSERT '.$TABLEPREFIX.$this->table.' SET '.$vals;
  355.       $sql_result db_quiet($q$this->fatal_sql);
  356.       $this->id = db_new_id();
  357.       $this->fill();
  358.       return $sql_result;
  359.     }
  360.   }
  361.  
  362.   /**
  363.   * Returns an SQL assignment clause
  364.   *
  365.   * @return string of form name='value'
  366.   */
  367.   function _sqlvals() {
  368.     $vals = array();
  369.     if ($this->changed{
  370.       #echo "This has changed";
  371.       foreach ($this->choicelist as $v{
  372.         if (isset($v['_field'])) {
  373.           $vals[] = $v['_field']->name ."="qw($v['_field']->value);
  374.         }
  375.       }
  376.     }
  377.     #echo "<pre>"; print_r($this->choicelist); echo "</pre>";
  378.     #echo "<pre>"; print_r($this->fields); echo "</pre>";
  379.     #echo "<pre>"; print_r($vals); echo "</pre>";
  380.     return join(",",$vals);
  381.   }
  382.  
  383.   /**
  384.   * determine whih values are selected and return them
  385.   * @param boolean $returnArray  return an array of values
  386.   * @return mixed list of selected values (array) or current value (string)
  387.   */
  388.   function selectedValue($returnArray=0) {
  389.     $val = array();
  390.     foreach ($this->choicelist as $v{
  391.       //echo "H:$this->idfield, $k, $v, $this->id";
  392.       if ($v[$this->idfield== $this->id{
  393.         foreach ($this->fields as $f{
  394.           //echo "G=$f";
  395.           $val[] = $v[$f];
  396.         }
  397.       }
  398.     }
  399.     return ($returnArray ? $val : implode(' ', $val));
  400.   }
  401.  
  402.   /**
  403.   * set which option in the selection list is the default option
  404.   * @param string $val   default value to use
  405.   */
  406.   function setDefault($val) {
  407.     //echo "DBChoiceList::setDefault: $val";
  408.     if (isset($this->id|| $this->id < 0{
  409.       $this->id = $val;
  410.     }
  411.   }
  412.  
  413.   /**
  414.   * PHP5 clone method
  415.   *
  416.   * PHP5 clone statement will perform only a shallow copy of the object. Any subobjects must also be cloned
  417.   */
  418.   function __clone() {
  419.     //print "send in the clones! I'm cloning a ".get_class($this);
  420.     //preDump(debug_backtrace());
  421.     //preDump($this->appendedfields);
  422.     //preDump($this->prependedfields);
  423.     // Force a copy of contents of $this->fields array, otherwise the fields will only be references
  424.     foreach ($this->appendedfields as $k => $f{
  425.       //print "cloning $k<br />";
  426.       if (is_object($f['_field'])) $this->appendedfields[$k]['_field'clone($f['_field']);
  427.     }
  428.     foreach ($this->prependedfields as $k => $f{
  429.       //print "cloning $k<br />";
  430.       if (is_object($f['_field'])) $this->prependedfields[$k]['_field'clone($f['_field']);
  431.     }
  432.     //print "done cloning";
  433.   }
  434.  
  435.  
  436. } // class DBChoiceList

Documentation generated on Tue, 06 Mar 2007 10:01:17 +0000 by phpDocumentor 1.3.0