Xataface 2.0
Xataface Application Framework
Dataface/RelatedRecord.php
Go to the documentation of this file.
00001 <?php
00002 /*-------------------------------------------------------------------------------
00003  * Xataface Web Application Framework
00004  * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca)
00005  * 
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *-------------------------------------------------------------------------------
00020  */
00061 class Dataface_RelatedRecord {
00062 
00070         var $secureDisplay = true;
00071 
00075         var $vetoSecurity;
00080         var $_record;
00081         
00086         var $_relationshipName;
00087         
00092         var $_relationship;
00093         
00099         var $_values;
00100         
00107         var $_metaDataValues;
00108         
00117         var $_absoluteColumnNames;
00118         
00122         var $_lockedFields=array();
00127         var $_records;
00131         var $_dirtyFlags=array();
00132         
00136         var $cache=array();
00137         
00138         
00146         function Dataface_RelatedRecord($record, $relationshipName, $values=null){
00147                 
00148                 if ( !is_a($record, 'Dataface_Record') ){
00149                         throw new Exception("Error in Dataface_RelatedRecord constructor.  Expected first argument to be of type 'Dataface_Record' but received '".get_class($record)."'.", E_USER_ERROR);
00150                 }
00151                 $this->_record =& $record;
00152                 $this->_relationshipName = $relationshipName;
00153                 $this->_relationship =& $record->_table->getRelationship($relationshipName);
00154                 if ( is_array($values) ){
00155                         $this->setValues($values);
00156                         $this->clearFlags();
00157                 }
00158         }
00159         
00160         
00165         function _initValues(){
00166                 if ( !isset( $this->_values ) ){
00167                         $fkeys = $this->_relationship->getForeignKeyValues();
00168                         $this->_values = array();
00169                         $this->_absoluteColumnNames = array();
00170                         //$cols = $this->_relationship->_schema['columns'];
00171                         $cols = $this->_relationship->fields(true); // we will get all fields - even grafted ones.
00172                         foreach ($cols as $col){
00173                                 list($table, $field) = explode('.', $col);
00174                                 $this->_values[$field] = null;
00175                                 if ( isset($this->_absoluteColumnNames[$field]) and
00176                                         Dataface_Table::loadTable($this->_relationship->getDomainTable())->hasField($field) ){
00177                                                 $this->_absoluteColumnNames[$field] = $this->_relationship->getDomainTable().'.'.$field;
00178                                 } else {
00179                                         $this->_absoluteColumnNames[$field] = $col;
00180                                 }
00181                                 
00182                                 
00183                                 /*
00184                                  * We want to check for locked fields. Locked fields are fields that *must* have a particular
00185                                  * value for the relationship to remain valid.
00186                                  */
00187                                 if ( isset( $fkeys[$table][$field]) and is_scalar($fkeys[$table][$field]) and strpos($fkeys[$table][$field],'$') ===0 ){
00188                                         $this->_lockedFields[$field] = $fkeys[$table][$field];
00189                                         $this->_values[$field] = $this->_record->parseString($fkeys[$table][$field]);
00190                                 }
00191                         }
00192                 }
00193         }
00194         
00195         //---------------------------------------------------------------------------------
00196         //{@
00197         
00210         function clearCache(){
00211                 $this->cache = array();
00212                 return $this;
00213         }
00214         
00215         
00224         function &toRecord($tablename=null){
00225                 if ( isset($this->cache[__FUNCTION__][$tablename]) ){
00226                         return $this->cache[__FUNCTION__][$tablename];
00227                 }
00228                 if ( !isset($tablename) ){
00229                         $tablename =  $this->_relationship->getDomainTable();
00230                         
00231                         
00232                 } 
00233                 
00234                 $table =& Dataface_Table::loadTable($tablename);
00235                 
00236                 
00237                 $values = array();
00238                 
00239                 
00240                 $absVals = $this->getAbsoluteValues();
00241                 $fieldnames = $this->_relationship->fields(true);
00242                 //foreach ( array_keys($absVals) as $key ){
00243                 foreach ( $fieldnames as $key ){
00244                         list($currTablename, $columnName) = explode('.', $key);
00245                         if ( ($currTablename == $tablename or $table->hasField($columnName)) and array_key_exists($key, $absVals)){
00246                                                                 
00247                                 $values[$columnName] = $absVals[$key];
00248                                 
00249                         } else if ( isset($this->_relationship->_schema['aliases'][$columnName]) /*and 
00250                                 /*$table->hasField($this->_relationship->_schema['aliases'][$columnName])*/ ){
00251                                 $values[$this->_relationship->_schema['aliases'][$columnName]] = $absVals[$key];
00252                         }
00253                 }
00254                 
00255                 foreach ( $this->_values as $key=>$val ){
00256                         if ( !isset($values[$key]) and $table->hasField($key) ) $values[$key] = $this->_values[$key];
00257                 }
00258                 
00259                 $record = new Dataface_Record($tablename, $values);
00260                 $record->secureDisplay = $this->secureDisplay;
00261                 foreach (array_keys($values) as $key){
00262 
00263                         if ( $this->isDirty($key) ) $record->setFlag($key);
00264 
00265                 }
00266                 $this->cache[__FUNCTION__][$tablename] =& $record;
00267 
00268                 return $record;
00269         }
00270         
00271         
00272         
00281         function toRecords(){
00282                 $tables =& $this->_relationship->getDestinationTables();
00283                 $out = array();
00284                 foreach ( array_keys($tables) as $index ){
00285                         $out[] =& $this->toRecord($tables[$index]->tablename);
00286                 }
00287                 return $out;
00288         }
00289         
00290         
00316         function &getParent(){
00317                 return $this->_record;
00318         }
00319         
00320         
00337         function testCondition($condition){
00338                 extract($this->strvals());
00339                 return eval('return ('.$condition.');');
00340         }
00341         
00342         
00353         function getActions($params=array()){
00354                 $params['record'] =& $this;
00355                 return $this->_record->_table->tablename->getActions($params);
00356         }
00357         
00358         
00359         // @}
00360         // END OF Utility Methods
00361         //-------------------------------------------------------------------------------------
00362         
00363         
00364         //-------------------------------------------------------------------------------------
00365         // @{
00379         function clearFlags(){
00380                 $this->_dirtyFlags = array();
00381                 return $this;
00382         }
00383         
00389         function setFlag($fieldname){
00390                 $this->_dirtyFlags[$fieldname] = true;
00391                 return $this;
00392         }
00393         
00399         function clearFlag($fieldname){
00400                 unset($this->_dirtyFlags[$fieldname]);
00401                 return $this;
00402         }
00403         
00409         function isDirty($fieldname){
00410                 return ( isset($this->_dirtyFlags[$fieldname]) );
00411         }
00412         
00413         // @}
00414         // End Transactions
00415         //---------------------------------------------------------------------------------------
00416         
00417         //----------------------------------------------------------------------------------------
00418         // @{
00419         
00434         function setMetaDataValue($key, $value){
00435                 if ( !isset( $this->_metaDataValues ) ) $this->_metaDataValues = array();
00436                 $this->_metaDataValues[$key] = $value;
00437                 return $this;
00438                 
00439         }
00440         
00441         
00451         function getLength($fieldname){
00452                 if ( strpos($fieldname, '.') !== false ){
00453                         list($tablename, $fieldname) = explode('.', $fieldname);
00454                         return $this->getLength($fieldname);
00455                 }
00456                 $key = '__'.$fieldname.'_length';
00457                 if ( isset( $this->_metaDataValues[$key] ) ){
00458                         return $this->_metaDataValues[$key];
00459                 } else {
00460                         return strlen($this->getValueAsString($fieldname));
00461                 }       
00462         }
00463         
00472         function setValue($fieldname, $value){
00473                 $this->_initValues();
00474                 
00475                 
00476                 if ( strpos($fieldname,'.') === false ){
00477                         if ( strpos($fieldname, '__') === 0 ){
00478                                 return $this->setMetaDataValue($fieldname, $value);
00479                         }
00480                 
00481                         if ( isset( $this->_lockedFields[$fieldname]) ) return;
00482                         $val = $this->_record->_table->parse($this->_relationshipName.".".$fieldname, $value);
00483                         if ( $val != $this->_values[$fieldname] ){
00484                                 $this->_values[$fieldname] = $val;
00485                                 $this->clearCache();
00486                                 $this->setFlag($fieldname);
00487                         }
00488                 
00489                 } else {
00490                         
00491                         list ( $table, $field )  = explode('.', $fieldname);
00492                         if ( $table != $this->_relationship->getDomainTable() and Dataface_Table::loadTable($this->_relationship->getDomainTable())->hasField($field) ){
00493                                 return PEAR::raiseError("Cannot set duplicate value in relationship.   The field $fieldname is part of the domain table and not part of $table in this relationship.");
00494                         }
00495                         return $this->setValue($field, $value);
00496                 }
00497                         
00498         }
00499         
00508         function setValues($values){
00509                 if ( !is_array($values) ){
00510                         throw new Exception( "setValues() expects 1st parameter to be an array but received a '".get_class($values)."' ",E_USER_WARNING);
00511                 }
00512                 foreach ( $values as $key=>$value){
00513                         
00514                         $this->setValue($key, $value);
00515                 }
00516         
00517         }
00518         
00519         
00520         
00521                 
00522                 
00531         function getValue($fieldname){
00532                 $this->_initValues();
00533                 if ( strpos($fieldname,'.') === false ){
00534                         
00535                         if ( !array_key_exists( $fieldname,  $this->_values ) ){
00536                                 // The key does not exist as a normal field -- so check if it is a calculated field.
00537                                 $tables = $this->_relationship->getDestinationTables();
00538                                 $dt =& Dataface_Table::loadTable($this->_relationship->getDomainTable());
00539                                 
00540                                 if ( $dt->hasField($fieldname) ){
00541                                         echo "Domain table has $fieldname";
00542                                         $tables = array($this->_relationship->getDomainTable());
00543                                 } else {
00544                                         echo "Domain table doesn't have $fieldname";
00545                                 }       
00546                                 foreach ( array_keys($tables) as $tkey){
00547                                         if ( $tables[$tkey]->hasField($fieldname) ){
00548                                                 $tempRecord = new Dataface_Record($tables[$tkey]->tablename,$this->getValues());
00549                                                 return $tempRecord->getValue($fieldname);
00550                                         }
00551                                 }
00552                                 throw new Exception("Attempt to get value for fieldname '$fieldname' that does not exist in related record of relationship '".$this->_relationshipName."'.  Acceptable values include {".implode(', ', array_keys($this->_values))."}.\n<br>", E_USER_ERROR);
00553                         }
00554 
00555                         return $this->_values[$fieldname];
00556                         
00557                 } else {
00558                         list ( $table, $field )  = explode('.', $fieldname);
00559                         return $this->getValue($field);
00560                 }
00561         }
00562         
00573         function getValueAsString($fieldname){
00574                 $value = $this->getValue($fieldname);
00575 
00576                 $parent =& $this->getParent();
00577                 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname);
00578                 if ( PEAR::isError($table) ){
00579                         throw new Exception($table->toString(), E_USER_ERROR);
00580                 }
00581                 $delegate =& $table->getDelegate();
00582                 $rel_fieldname = $fieldname; //$table->relativeFieldName($fieldname);
00583                 if ( $delegate !== null and method_exists( $delegate, $rel_fieldname.'__toString') ){
00584                         $value = call_user_func( array(&$delegate, $rel_fieldname.'__toString'), $value);
00585                 } else 
00586                 
00587                 
00588                 if ( is_array($value) ){
00589                         if ( method_exists( $table, $table->getType($fieldname)."_to_string") ){
00590                                 $value = call_user_func( array( &$table, $table->getType($fieldname)."_to_string"), $value );
00591                         } else {
00592                                 $value = implode(', ', $value);
00593                         }
00594                 }
00595                 
00596                 
00597                 $evt = new stdClass;
00598                 $evt->table = $table;
00599                 $evt->field =& $table->getField($rel_fieldname);
00600                 $evt->value = $value;
00601                 $evt->type = $table->getType($rel_fieldname);
00602                 $table->app->fireEvent('after_getValueAsString', $evt);
00603                 $value = $evt->value;
00604                 
00605                 return $value;
00606         
00607         }
00608         
00616         function htmlValue($fieldname){
00617                 $value = $this->getValue($fieldname);
00618                 $parent =& $this->getParent();
00619                 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname);
00620                 if ( PEAR::isError($table) ){
00621                         throw new Exception($table->toString(), E_USER_ERROR);
00622                 }
00623                 $record =& $this->toRecord($table->tablename);
00624                 $htmlval = $record->htmlValue($fieldname);
00625                 return $htmlval;
00626         }
00627         
00638         function getValuesAsStrings($fields=''){
00639                 $keys = is_array($fields) ? $fields : array_keys($this->_values);
00640                 $values = array();
00641                 foreach ($keys as $key){
00642                         $values[$key] = $this->getValueAsString($key);
00643                 }
00644                 return $values;
00645         }
00646         
00647         
00652         function strvals($fields=''){
00653                 return $this->getValuesAsStrings($fields);
00654         }
00655         
00656         
00661         function strval($fieldname, $index=0){
00662                 return $this->getValueAsString($fieldname, $index);
00663         }
00664         
00665         
00670         function stringValue($fieldname){
00671                 return $this->getValueAsString($fieldname);
00672         }
00673         
00684         function &getValues($columns=null, $excludeNulls=false){
00685                 $this->_initValues();
00686                 return $this->_values;
00687         }
00688 
00693         function &values($fields = null){
00694                 return $this->getValues($fields);
00695         }
00696         
00701         function &vals($fields = null){
00702                 return $this->getValues($fields);
00703         }
00704         
00705         
00710         function val($fieldname){
00711                 return $this->getValue($fieldname);
00712         }
00713         
00714         
00715         
00723         function getAbsoluteValues($excludeNulls=false, $includeAll=false){
00724                 $absVals = array();
00725                 foreach ( $this->getValues() as $key=>$value){
00726                         $absVals[ $this->_absoluteColumnNames[$key] ] = $value;
00727                 }
00728                 return $absVals;
00729                 
00730         
00731         }
00732         
00743         function getForeignKeyValues($sql = null){
00744                 if ( !isset($sql) ) $sql_index = 0;
00745                 else $sql_index = $sql;
00746                 if ( isset($this->cache[__FUNCTION__][$sql_index]) ){
00747                         return $this->cache[__FUNCTION__][$sql_index];
00748                 }
00749                 $fkeys = $this->_relationship->getForeignKeyValues();
00750                 $absVals = $this->getAbsoluteValues(true);
00751         
00752                 $out = $this->_relationship->getForeignKeyValues($absVals, $sql, $this->getParent());
00753                 $this->cache[__FUNCTION__][$sql_index] = $out;
00754                 return $out;
00755                 
00756         }
00757         
00758         
00759         
00760         
00785         function display($fieldname){
00786                 if ( isset($this->cache[__FUNCTION__][$fieldname]) ){
00787                         return $this->cache[__FUNCTION__][$fieldname];
00788                 }
00789                 $parent =& $this->getParent();
00790                 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname);
00791                 if (PEAR::isError($table) ){
00792                         throw new Exception("Error loading table while displaying $fieldname because ".$table->getMessage(), E_USER_ERROR);
00793                         
00794                 }
00795                 
00796                 
00797                 if ( !$table->isBlob($fieldname) ){
00798                         $record =& $this->toRecord($table->tablename);
00799                         if ( $this->secureDisplay and $this->checkPermission('view', array('field'=>$fieldname)) ){
00800                                 //echo "HERE";
00801                                 $oldSecure = $record->secureDisplay;
00802                                 $record->secureDisplay = false;
00803                                 $out = $record->display($fieldname);
00804                                 $record->secureDisplay = $oldSecure;
00805                         } else {
00806                                 $out = $record->display($fieldname);
00807                         }
00808                         
00809                         $this->cache[__FUNCTION__][$fieldname] = $out;
00810                         return $out;
00811                         
00812                 } else {
00813                         $keys = array_keys($table->keys());
00814                         $qstr = '';
00815                         foreach ($keys as $key){
00816                                 $qstr .= "&$key"."=".$this->strval($key);
00817                         }
00818                         $out = DATAFACE_SITE_HREF."?-action=getBlob&-table=".$table->tablename."&-field=$fieldname$qstr";
00819                         $this->cache[__FUNCTION__][$fieldname] = $out;
00820                         return $out;
00821                 }
00822                                 
00823                                 
00824         }
00825         
00837         function preview($fieldname, $index=0, $maxlength=255){
00838                 if ( isset($this->cache[__FUNCTION__][$fieldname][$index][$maxlength]) ){
00839                         return $this->cache[__FUNCTION__][$fieldname][$index][$maxlength];
00840                 }
00841                 $strval = strip_tags($this->display($fieldname,$index));
00842                 $out = substr($strval, 0, $maxlength);
00843                 if ( strlen($strval)>$maxlength) {
00844                         $out .= '...';
00845                 }
00846                 $this->cache[__FUNCTION__][$fieldname][$index][$maxlength] = $out;
00847                 return $out;
00848         }
00849         
00854         function printValue($fieldname){
00855                 return $this->display($fieldname);
00856         }
00857         
00862         function printval($fieldname){
00863                 return $this->display($fieldname);
00864         }
00865         
00870         function q($fieldname){
00871                 return $this->display($fieldname);
00872         }
00873         
00881         function qq($fieldname){
00882                 $parent =& $this->getParent();
00883                 $table =& $parent->_table->getTableTableForField($this->_relationshipName.'.'.$fieldname);
00884                 if ( PEAR::isError($table) ){
00885                         throw new Exception($table->toString(), E_USER_ERROR);
00886                 }
00887                 if ( !$table->isBlob($fieldname) ){
00888                         return htmlspecialchars($this->q($fieldname, $index));
00889                 } else {
00890                         return $this->display($fieldname, $index);
00891                 }
00892         }
00893         
00894         
00895         // @}
00896         // End Accessing Field Data
00897         //-------------------------------------------------------------------------------------------
00898         
00899         
00900         //-------------------------------------------------------------------------------------------
00901         // @{
00919         function getId(){
00920                 if ( isset($this->cache[__FUNCTION__]) ){
00921                         return $this->cache[__FUNCTION__];
00922                 }
00923                 $parentid = $this->_record->getId();
00924                 list($tablename, $querystr) = explode('?',$parentid);
00925                 $id = $tablename.'/'.$this->_relationshipName.'?'.$querystr;
00926                 $keys = array_keys($this->_relationship->keys());
00927                 $params = array();
00928                 foreach ($keys as $key){
00929                         $params[] = urlencode($this->_relationshipName.'::'.$key).'='.urlencode($this->strval($key));
00930                 }
00931                 $out = $id.'&'.implode('&',$params);
00932                 $this->cache[__FUNCTION__] = $out;
00933                 return $out;
00934         }
00935         
00936         
00945         function getTitle(){
00946                 $record =& $this->toRecord();
00947                 return $record->getTitle();
00948         }
00949          
00950          
00951          
00952         // @}
00953         // END Metadata
00954         //--------------------------------------------------------------------------------------------  
00955         
00956         // @{
00976         function validate( $fieldname, $value, &$params){
00977                 if ( strpos($fieldname, '.') !== false ){
00978                         list($relname, $fieldname) = explode('.', $fieldname);
00979                         return $this->validate($fieldname, $value, $params);
00980                 }
00981                 
00982                 if ( !is_array($params) ){
00983                         $params = array('message'=> &$params);
00984                 }
00985                 $table =& $this->_relationship->getTable($fieldname);
00986                 if (PEAR::isError($table) ){
00987                         error_log($table->toString().implode("\n", $table->getBacktrace()));
00988                         throw new Exception("Failed to get table for field $fieldname.  See error log for details", E_USER_ERROR);
00989                         
00990                 } else if (!$table ){
00991                         throw new Exception("Could not load table for field $fieldname .", E_USER_ERROR);
00992                 }
00993                 $field =& $table->getField($fieldname);
00994                 
00995                 if ( $field['widget']['type'] == 'file' and @$field['validators']['required'] and is_array($value) and $this->getLength($fieldname) == 0 and !is_uploaded_file(@$value['tmp_name'])){
00996                         // This bit of validation operates on the upload values assuming the file was just uploaded as a form.  It assumes
00997                         // that $value is of the form
00999                         $params['message'] = "$fieldname is a required field.";
01000                         return false;
01001                 }
01002         
01003                 $res = $table->validate($fieldname, $value, $params);
01004                 if ( $res ){
01005                         $delegate =& $table->getDelegate();
01006                         if ( $delegate !== null and method_exists($delegate, $fieldname."__validate") ){
01007                                 /*
01008                                  *
01009                                  * The delegate defines a custom validation method for this field.  Use it.
01010                                  *
01011                                  */
01012                                 $methodname = $fieldname."__validate";
01013                                 $res = $delegate->$methodname($this,$value,$params);
01014                                 //$res = call_user_func(array(&$delegate, $fieldname."__validate"), $this, $value, $params);
01015                         }
01016                 }
01017                 return $res;
01018                 
01019         }
01020         
01021         // @}
01022         // END Form Validation
01023         //--------------------------------------------------------------------------------------------
01024         
01025         // @{
01032         function save($lang=null, $secure=false){
01033                 $recs =& $this->toRecords();
01034                 
01035                 foreach (array_keys($recs) as $i){
01036 
01037                         $res = $recs[$i]->save($lang, $secure);
01038                         if ( PEAR::isError($res) ) return $res;
01039                 }
01040         }
01041         
01042         // @}
01043         // End Saving
01044         //----------------------------------------------------------------------------------------------
01045         
01046         //----------------------------------------------------------------------------------------------
01047         // @{
01055         function getPermissions($params=array()){
01056         
01057                 // 1. Get the permissions for the particular field
01058                 if ( isset($params['field']) ){
01059                         if ( strpos($params['field'],'.') !== false ){
01060                                 list($junk,$fieldname) = explode('.', $params['field']);
01061                         } else {
01062                                 $fieldname = $params['field'];
01063                         }
01064                         $t =& $this->_relationship->getTable($fieldname);
01065                         $rec = $this->toRecord($t->tablename);
01066                         
01067                         
01068                         $perms = $rec->getPermissions(array('field'=>$fieldname, 'nobubble'=>1));
01069                         if ( !$perms ) $perms = array();
01070                         
01071                         
01072                         $rfperms = $this->_record->getPermissions(array('relationship'=>$this->_relationshipName, 'field'=>$fieldname, 'nobubble'=>1));
01073                         //echo "RFPerms: ";print_r($rfperms);
01074                         if ( $rfperms ){
01075                                 foreach ($rfperms as $k=>$v){
01076                                         $perms[$k] = $v;
01077                                 }
01078                         }
01079                 
01080                         //print_r($perms);
01081                         return $perms;
01082                 } else {
01083                         $perms = array();
01084                         foreach ( $this->toRecords() as $record){
01085                                 $rperms = $record->getPermissions(array());
01086                                 if ( $perms ){
01087                                         $perms = array_intersect_assoc($perms, $rperms);
01088                                         
01089                                 } else {
01090                                         $perms = $rperms;
01091                                 }
01092                                 
01093                         }
01094                         return $perms;
01095                         
01096                 }
01097         }
01098         
01115         function checkPermission($perm, $params=array()){
01116                 $perms = $this->getPermissions($params);
01117                 return @$perms[$perm]?1:0;
01118                 
01119         }
01120         
01121         
01122         // @}
01123         // End Permissions
01124         //----------------------------------------------------------------------------------------------
01125         
01126         
01127         
01128         
01129         
01130         
01131         
01132 
01133 
01134 
01135 }
 All Data Structures Namespaces Files Functions Variables Enumerations