Controllers
[ class tree: Controllers ] [ index: Controllers ] [ all elements ]

Source for file ExternallyLinkedORM.php

Documentation is available at ExternallyLinkedORM.php

  1. <?php
  2. /*
  3.  * Created on Sep 26, 2008
  4.  *
  5.  * To change the template for this generated file go to
  6.  * Window - Preferences - PHPeclipse - PHP - Code Templates
  7.  */
  8.  
  9. define('ALLDATA''allRecords');
  10. /**
  11.  * Class: ExternallyLinkedORM
  12.  * Description:
  13.  * @remember currently the references mechanism only works well if the foreign key field refers to
  14.  *  the primary key field of the referenced table; also, this DDMS works on the assumption that primary key
  15.  *  fields are integer values.  local primary key fields must be autoincrement and the field name must be 'local_key'
  16.  *@todo possibly, as a step towards handling insertion of new records: allow for new records to be inserted but to remain
  17.  *  purely local, recognisable by null value in remote key field.  These records will be ignored during the refresh event, ie
  18.  *  they will not be updated or deleted at any stage.
  19.  * @todo possibly, include non-linked tables in the references table to indicate how these remotely linked cached data records
  20.  *  relate to the local non-linked application data.  Then upon deletion of records that are referenced by local tables the user
  21.  *  should have a choice not to delete those records in order to avoid data corruption. These records can then also continue as purely local records even though stored in linked table.
  22.  *  Alternatively, a separate table could be created for these purely local records.
  23.  * @todo the update function can now run more than once for the same table during
  24.  *  a refresh data event (eg during its regular update, but also in the case when a record
  25.  *  is deleted in a table that is has foreign key references to).  To maximise efficiency
  26.  *  we could consider keeping an array with key values that have already been updated during the current
  27.  *  refresh event, and before running another update check against this array.  Alternatively, make the update
  28.  *  function only ever execute once during any given refresh event, ie build up an array with key values
  29.  *  and only if it is complete run the update event.
  30.  * @todo rename the different refresh functions to 'update' 'insert' 'delete', separate out delete and insert
  31.  *  at start of refresh event retrieve set of remote keys as these are used in all three
  32.  * @todo for new records: go back to the server to check for foreign key references of other tables and
  33.  *  optionally refresh these in the local cache
  34.  * @todo separate out the ALLDATA refresh alltogether
  35.  *
  36.  */
  37.  
  38. class ExternallyLinkedORM extends ORM
  39. {
  40.  
  41.     private $primaryKey;
  42.     private $remoteKey;
  43.     public $remoteMap;
  44.     private $references;
  45.     private $tableName;
  46.     private $serverConnection;
  47.     private $primaryKeyIsString = FALSE;
  48.     private $remoteKeyIsString = FALSE;
  49.     private $partOfDefaultRefreshEvent;
  50.  
  51.  
  52.     /**
  53.      * @method __construct 
  54.      * @param Associative array $tableDetails.
  55.      *  Expected array keys: table_name, primary_key, remote_key
  56.      * @param Associative array $references, representing foreign key references of the linked table.
  57.      *  Expected array keys: `column`, referenced_table, referenced_column, remote_referenced_column
  58.      * @param ExternalDataServerConnection $serverConnection. 
  59.      *  Reference to the serverConnection object
  60.      * @return $this 
  61.      */
  62.     function __construct($tableDetails$references&$serverConnection)
  63.     {
  64.         $this->database 'rigregversiontwo';
  65.         $this->table["$tableDetails[table_name]"$tableDetails['table_name'];
  66.         $this->tableName = $tableDetails['table_name'];
  67.         $this->partOfDefaultRefreshEvent = (bool)$tableDetails['local_refresh'];
  68.         $this->serverConnection = $serverConnection;
  69.         $this->references = $references;
  70.  
  71.         //setup the table with the help of the MySql information_schema
  72.  
  73.         $link mysql_connect('localhost''root'''or die('Could not connect: ' .
  74.         mysql_error());
  75.         mysql_select_db($this->databaseor die('Could not select database');
  76.  
  77.         $sql "SELECT COLUMN_NAME,
  78.         COLUMN_KEY
  79.         FROM information_schema.COLUMNS C
  80.         where TABLE_SCHEMA = '$this->database'
  81.         and TABLE_NAME = '$this->tableName'";
  82.  
  83.         $result mysql_query($sqlor die('Query failed: ' mysql_error());
  84.  
  85.         $this->group["$this->tableName"array();
  86.         while ($row mysql_fetch_array($resultMYSQL_NUM))
  87.         {
  88.             $this->map["$row[0]"array("$this->tableName""$row[0]");
  89.             array_push($this->group["$this->tableName"]"$row[0]");
  90.              
  91.         }
  92.         //primary key details
  93.         $this->primaryKey = $tableDetails['primary_key'];
  94.  
  95.         $sql "SELECT CHARACTER_MAXIMUM_LENGTH
  96.         FROM information_schema.COLUMNS C
  97.         where TABLE_SCHEMA = '$this->database'
  98.         and TABLE_NAME = '$this->tableName'
  99.         and COLUMN_NAME = '$this->primaryKey'";
  100.         $result mysql_query($sqlor die('Query failed: ' mysql_error());
  101.         $row mysql_fetch_array($resultMYSQL_NUM);
  102.         if (!empty($row[0])){
  103.             $this->primaryKeyIsString = TRUE;
  104.         }
  105.  
  106.         //remote key details
  107.         $this->remoteKey = $tableDetails['remote_key'];
  108.         if (!$this->isSelectOnly()){
  109.             $sql "SELECT CHARACTER_MAXIMUM_LENGTH
  110.             FROM information_schema.COLUMNS C
  111.             where TABLE_SCHEMA = '$this->database'
  112.             and TABLE_NAME = '$this->tableName'
  113.             and COLUMN_NAME = '$this->remoteKey'";
  114.             $result mysql_query($sqlor die('Query failed: ' mysql_error());
  115.             $row mysql_fetch_array($resultMYSQL_NUM);
  116.             if (!empty($row[0])){
  117.                 $this->remoteKeyIsString = TRUE;
  118.             }
  119.         else {
  120.             $this->remoteKeyIsString = $this->primaryKeyIsString;
  121.         }
  122.         $this->map["max$this->remoteKey"array("$this->tableName"$this->remoteKey"MAX($this->remoteKey)");
  123.  
  124.         //make the map that will be used to communicate with the server
  125.         $this->makeRemoteMap();
  126.  
  127.         // Free resultset
  128.         mysql_free_result($result);
  129.         // Closing connection
  130.         mysql_close($link);
  131.  
  132.         return $this;
  133.  
  134.     }
  135.  
  136.     //methods to help constructing the Object
  137.     function makeRemoteMap()
  138.     {
  139.         $this->remoteMap = $this->map;
  140.         if ($this->primaryKey<>$this->remoteKey){
  141.             $this->remoteMap[$this->primaryKey][1]='';
  142.             $this->remoteMap [$this->primaryKey][2]'NULL';
  143.         }
  144.         unset($this->remoteMap["max$this->remoteKey"]);
  145.          
  146.  
  147.     }
  148.  
  149.     //-------------------------------methods to get information about this linked table-------------------------
  150.     function getTableName(){
  151.         return $this->tableName;
  152.     }
  153.  
  154.     /**
  155.      * @method isSelectOnly 
  156.      * @return TRUE if the linked table primary key field is the remote_key field
  157.      *          FALSE if the linked table primary key field is not the remote_key field
  158.      */
  159.     function isSelectOnly()
  160.     {
  161.  
  162.         if ($this->primaryKey==$this->remoteKey)
  163.         {
  164.             return true;
  165.         }
  166.         return false;
  167.          
  168.  
  169.     }
  170.  
  171.     private function _localTablesEmpty()
  172.     {
  173.         $this->select($this->primaryKey);
  174.         $this->limit(1);
  175.         $this->rows();
  176.         if (!$this->num_rows(== 0{
  177.             return false;
  178.         }
  179.  
  180.         return true;
  181.     }
  182.  
  183.     function getRemoteKey()
  184.     {
  185.         return $this->remoteKey;
  186.     }
  187.  
  188.     function getColumnNumber($fieldName){
  189.         $counter 0;
  190.         foreach(array_keys($this->mapas $map){
  191.             if ($map == $fieldName){
  192.                 return $counter;
  193.             }
  194.             $counter++;
  195.         }
  196.         return false;
  197.     }
  198.  
  199.     /**
  200.      * @method hasReferences 
  201.      * @return if the linked table has references to
  202.      *  other linked tables: TRUE, else FALSE
  203.      */
  204.     function hasReferences()
  205.     {
  206.         if (count($this->references)>0)
  207.         {
  208.             return true;
  209.         }
  210.         return false;
  211.     }
  212.  
  213.     /**
  214.      * @method hasLocalKeyReferences 
  215.      * @return if the linked table has references to
  216.      *  local_key values of other linked tables: TRUE, else FALSE
  217.      */
  218.     function hasLocalKeyReferences()
  219.     {
  220.         if (count($this->references)>0)
  221.         {
  222.             foreach ($this->references as $reference)
  223.             {
  224.                 if ($reference['referenced_column'== 'local_key')
  225.                 {
  226.                     return true;
  227.                 }
  228.             }
  229.         }
  230.         return false;
  231.     }
  232.  
  233.     //-------------------------------end methods to get information about this linked table-------------------------
  234.  
  235.  
  236.     //---------methods to get information about specific references belonging to this table------------------
  237.     /**
  238.      * @method isLocalKeyReference     *
  239.      * @param array $reference 
  240.      * @return if 'referenced_column' == 'local_key' TRUE else FALSE
  241.      */
  242.     function isLocalKeyReference($reference)
  243.     {
  244.         if ($reference['referenced_column']== 'local_key')
  245.         {
  246.             return true;
  247.         }
  248.         return false;
  249.     }
  250.  
  251.     /**
  252.      * @method getReferences 
  253.      * @return array containing names of other linked
  254.      *  tables this table has foreign key references to.
  255.      *
  256.      */
  257.     function getReferences()
  258.     {
  259.         $tables array();
  260.         foreach ($this->references as $reference)
  261.         {
  262.             array_push($tables$reference['referenced_table']);
  263.         }
  264.         return $tables;
  265.     }
  266.  
  267.     function getReferencedObjects()
  268.     {
  269.         return $this->serverConnection->getObjects($this->getReferences());
  270.     }
  271.  
  272.     function hasForeignKeyReferenceTo($tableName)
  273.     {
  274.         foreach($this->references as $reference)
  275.         {
  276.             if ($reference['referenced_table']==$tableName)
  277.             {
  278.                 return TRUE;
  279.             }
  280.         }
  281.         return FALSE;
  282.     }
  283.     /**
  284.      * returns the name of the column the values of which have
  285.      * a foreign key relation with $referredTable
  286.      *
  287.      * @param string $referredTable 
  288.      * @return IF this table has foreign key reference to $referredTable: string representing the column name
  289.      *  ELSE boolean FALSE
  290.      */
  291.     function getForeignKeyFieldName ($referredTable)
  292.     {
  293.         foreach ($this->references as $reference)
  294.         {
  295.             if ($reference['referenced_table']==$referredTable)
  296.             {
  297.                 return $reference['column'];
  298.             }
  299.         }
  300.         return false;
  301.     }
  302.  
  303.     function referenceIsCascadingInsert($tableName)
  304.     {
  305.         foreach($this->references as $reference)
  306.         {
  307.             if ($reference['referenced_table'== $tableName)
  308.             {
  309.                 return $reference['cascading_insert'];
  310.             }
  311.         }
  312.         return false;
  313.     }
  314.  
  315.     function referenceIsCascadingDelete($tableName)
  316.     {
  317.         foreach($this->references as $reference)
  318.         {
  319.             if ($reference['referenced_table'== $tableName)
  320.             {
  321.                 return $reference['cascading_delete'];
  322.             }
  323.         }
  324.         return false;
  325.     }
  326.  
  327.     function getReferringFieldForTable($tableName)
  328.     {
  329.         foreach($this->references as $reference)
  330.         {
  331.             if ($reference['referenced_table'== $tableName)
  332.             {
  333.                 return $reference['column'];
  334.             }
  335.         }
  336.         return false;
  337.     }
  338.     //------------------end methods to get information about specific references for this table ------------------
  339.  
  340.     //-------------------------methods to retrieve and convert key values from this table-------------------
  341.  
  342.     function getRemoteKeySet()
  343.     {
  344.         $this->select($this->remoteKey);
  345.         return $this->rows_array();
  346.     }
  347.  
  348.  
  349.     public function getKeySetForUpdate($field$operator$value)
  350.     {
  351.         $this->select($this->remoteKey);
  352.         $this->where($field$operator$value);
  353.         // $this->limit(5);
  354.  
  355.         return $this->rows_array();
  356.     }
  357.  
  358.  
  359.  
  360.     function getLocalRemoteKeyPairs($returnHash false,$field '' $operator=''$value ='')
  361.     {
  362.         $this->select($this->primaryKey$this->remoteKey);
  363.         if ($field <> '' && $operator<>'' && $value<> '')
  364.         {
  365.             $this->where($field$operator$value);
  366.         }
  367.         $result $this->rows_array();
  368.         if (!$returnHash){
  369.             return $result;
  370.         else {
  371.             $hash array();
  372.             foreach ($result as $record){
  373.                 $hash[$record[$this->remoteKey]]=$record[$this->primaryKey];
  374.             }
  375.             return $hash;
  376.              
  377.         }
  378.     }
  379.  
  380.      
  381.  
  382.     function convertRemoteKeyValuesToLocalKeyValues($remoteKeyValues)
  383.     {
  384.         $this->select($this->primaryKey);
  385.         $this->where($this->remoteKey'IN'implode(','$this->reduce($remoteKeyValues,$this->remoteKeyIsString)));
  386.         return $this->rows_array();
  387.     }
  388.  
  389.     function convertLocalKeyValuesToRemoteKeyValues($localKeyValues)
  390.     {
  391.         $this->select($this->remoteKey);
  392.         $this->where($this->primaryKey'IN'implode(','$this->reduce($localKeyValues$this->primaryKeyIsString)));
  393.         return $this->rows_array();
  394.     }
  395.  
  396.     /**
  397.      * A utility function to retrieve a set of remote_key values based
  398.      * on a set of foreign key values that might be referred to by records
  399.      * in this table
  400.      *
  401.      * @param rows_array $foreignKeyValues 
  402.      * @param string $column 
  403.      *  the column that is the foreign key
  404.      * @return rows_array $remoteKeyValues
  405.      *  A set of remote_key values for records that have foreign key references
  406.      *  to any of the values found in $foreignKeyValues.
  407.      */
  408.     function convertForeignKeyValuesToRemoteKeyValues($foreignKeyValues$column$isString FALSE)
  409.     {
  410.         $this->select($this->remoteKey);
  411.         $this->where($column'IN'implode(','$this->reduce($foreignKeyValues$isString)));
  412.         return $this->rows_array();
  413.     }
  414.  
  415.     /**
  416.      * A utility function to retrieve a set of local_key values based
  417.      * on a set of foreign key values that might be referred to by records
  418.      * in this table
  419.      *
  420.      * @param rows_array $foreignKeyValues 
  421.      * @param string $column 
  422.      *  the column that is the foreign key
  423.      * @return rows_array $remoteKeyValues
  424.      *  A set of remote_key values for records that have foreign key references
  425.      *  to any of the values found in $foreignKeyValues.
  426.      */
  427.     function convertForeignKeyValuesToLocalKeyValues($foreignKeyValues$column$isString FALSE)
  428.     {
  429.         $this->select($this->primaryKey);
  430.         $this->where($column'IN'implode(','$this->reduce($foreignKeyValues$isString)));
  431.         return $this->rows_array();
  432.     }
  433.  
  434.     //---------- end methods to retrieve and convert key values from this table  -----------------------
  435.  
  436.  
  437.  
  438.  
  439.     //-----------------------------------------data refresh methods----------------------------------------
  440.  
  441.     /**
  442.      * @method refreshLocalData 
  443.      *
  444.      * @param String $updateEventType - a valid update event type
  445.      *  as specified in ......  if this argument is not provided
  446.      *  the default update event is executed
  447.      * @todo implement the refresh thing as follows:
  448.      *  distinguish between the 'refreshType' and the 'updateType'.
  449.      *  Of 'refreshType' there are two: the 'regular' refresh event
  450.      *  including only those tables that need refresh regularly; and
  451.      *  the'all tables' refresh event.  Of updateTypes there can be
  452.      *  any number, one of which is the default update type.  The dataset
  453.      *  to be updated for each of these types is defined in the updatLocalDataRule object.
  454.      * @todo the delete and get new events can be combined into one event as the full
  455.      *  set of keys is returned to the application by the remote server for the delete event.
  456.      *  based on this it is easy to determine which records are not in the local cache and
  457.      *  request them from the server.
  458.      * @todo think about the implications of deleting records that might be referenced by
  459.      *  other tables.  Deleting records in referenced tables should probably result in some kind
  460.      *  of update event in table(s) that reference that table as foreign key values in the records
  461.      *  that refer to records that are now deleted should have been updated in the DoR.  In case of an
  462.      *  ALLDATA update this would be taken care of as a matter of course, but not necessarily in update events
  463.      *  that work with subsets of the data.
  464.      */
  465.     public function refreshLocalData($updateEventType '')
  466.     {
  467.         if (!$this->_localTablesEmpty()) {
  468.             //only if new records are created locally
  469.             if (!$this->isSelectOnly())
  470.             {
  471.                 //This code is here only as a reminder of things to come in future......
  472.                 //For now there is no implemented funnctionality for writing back new records
  473.                 //check for auditing results of previously sent new records
  474.                 //if and process these results locally
  475.                 //this is yet to be implemented, see method description
  476.                 //under 'writeNewRecords()' and 'checkAndProcessStagedNewRecords()'methods
  477.                 $this->checkAndProcessStagedNewRecords();
  478.                  
  479.                 $this->writeNewRecords();
  480.             }
  481.  
  482.             if ($updateEventType == ALLDATA)
  483.             {
  484.                 $this->refreshAllData();
  485.             else {
  486.                 //do a data refresh of a fragment as defined by the $updateEventType
  487.                 $this->refreshDataFragment($updateEventType);
  488.             }
  489.  
  490.         else {
  491.             $this->_populateLocalTables();
  492.         }
  493.  
  494.     }
  495.  
  496.  
  497.  
  498.     /**
  499.      * Does a blanket replacement of all local data with data from the DoR
  500.      * In future can be made more sophisticated for logging and 'undo' purposes
  501.      *
  502.      */
  503.     private function refreshAllData()
  504.     {
  505.         $fileString $this->_getLoadFile();
  506.  
  507.         if (!$this->isSelectOnly())
  508.         {
  509.             $this->addLocalKeysToLoadFile($fileString);
  510.         }
  511.         if ($this->hasLocalKeyReferences())
  512.         {
  513.             $fileString $this->localizeReferences($fileString);
  514.         }
  515.  
  516.         $this->query("delete from $this->tableName");//truncate $this->tableName
  517.  
  518.         $this->_createAndLoadFile($fileString);
  519.     }
  520.  
  521.  
  522.     private function refreshDataFragment($updateEventType '')
  523.     {
  524.         if ($this->partOfDefaultRefreshEvent)
  525.         {
  526.             //retrieve the set of DoR key values
  527.             //this will be used during delete and insert
  528.             $x $this->serverConnection->getUrlConnection()->getData("getKeySet/$this->tableName/$this->remoteKey/".serialize($this->remoteMap));
  529.             $dorKeySet $this->reduce(unserialize($this->serverConnection->getUrlConnection()->getData("getKeySet/$this->tableName/$this->remoteKey/".serialize($this->remoteMap))),$this->remoteKeyIsString);
  530.  
  531.             //check for deleted Dor records and delete those records locally
  532.             $this->DeleteLocalRecords($dorKeySet);
  533.              
  534.             //update a data fragment
  535.             $this->updateLocalRecords($updateEventType);
  536.              
  537.              
  538.             //get new records and insert them
  539.             $this->InsertLocalRecords($dorKeySet);
  540.         }
  541.  
  542.     }
  543.  
  544.  
  545.  
  546.     private function DeleteLocalRecords($dorKeySet)
  547.     {
  548.         //get the full set of key values from the DoR
  549.         // $dorKeySet = unserialize($this->serverConnection->getUrlConnection()->getData("getKeySet/$this->tableName/$this->remoteKey/".serialize($this->remoteMap)));
  550.  
  551.         //first select the key values of the records about to be deleted
  552.         //we need these values to determine which records in related tables
  553.         //would have been affected by deleting these records.  There is a possibility that these records
  554.         //still exist but have been changed as the records they refer to have been deleted, so they need to be updated in the local cache as well.
  555.         //if the referring records in the other table have also been deleted in the DoR there is no problem
  556.         //doing this anyway, no records will be updated in that case, and the locally cached records will be deleted during this refresh event.
  557.         $whereString implode(","$dorKeySet);
  558.         $this->select($this->primaryKey);
  559.         $this->where($this->remoteKey,'NOT IN'$whereString);
  560.         $delete_records $this->rows_array();
  561.         //now delete the records that no longer exist in the DoR
  562.         $this->where($this->remoteKey,'NOT IN'$whereString);
  563.  
  564.         $this->delete();
  565.         if (count($delete_records)>0){
  566.             //now get the objects that have foreign key references to this table
  567.             $referringTableObjects $this->serverConnection->getReferringObjects($this->tableName);
  568.             if (!empty($referringTableObjects)){
  569.                 foreach ($referringTableObjects as $object)
  570.                 {
  571.                     if ($object->referenceIsCascadingDelete($this->tableName))
  572.                     {
  573.                         //before running the update function in this way, translate/map the $delete_records
  574.                         //to remote_key values of the referencing table.
  575.                         if ($this->isSelectOnly()){
  576.                             $isString $this->primaryKeyIsString ;
  577.                         else {
  578.                             $isString $this->remoteKeyIsString;
  579.                         }
  580.                         $keySet $object->convertForeignKeyValuesToRemoteKeyValues($delete_records,$object->getForeignKeyFieldName ($this->tableName)$isString);
  581.                          
  582.  
  583.                          
  584.                         //and run the update function based on the $delete_records dataset
  585.                         $object->updateLocalRecords(''$keySet);
  586.                     }
  587.                 }
  588.                  
  589.             }
  590.         }
  591.     }
  592.  
  593.      
  594.  
  595.     /**
  596.      * @todo the get new records function should or could
  597.      *  send a message to the server to get any records from table(s) that
  598.      *  refer to this table and have a reference to the new record and
  599.      *  the local cache for this referring table can then be updated accordingly
  600.      * @todo split up the 'delete' and 'insert' events, which are currently both
  601.      *  included in this function.
  602.      *
  603.      */
  604.     private function InsertLocalRecords($dorKeySet)
  605.     {
  606.         //get the full set of key values from the DoR
  607.         // $dorKeySet = unserialize($this->serverConnection->getUrlConnection()->getData("getKeySet/$this->tableName/$this->remoteKey/".serialize($this->remoteMap)));
  608.  
  609.         //get new records from the DoR
  610.         $newRecKeys array_diff($dorKeySet$this->reduce($this->getRemoteKeySet(),$this->remoteKeyIsString));
  611.         //only if there are any new records to get
  612.         if (count($newRecKeys)>0){
  613.             $keyString implode(",",$newRecKeys);
  614.             $where " where $this->remoteKey IN ($keyString)";
  615.             //get the load data remotely - if nothing was specified in the Rule Object, no data will be fetched
  616.             $fileString $this->_getLoadFile($where);
  617.             if ($this->hasLocalKeyReferences())
  618.             {
  619.                 $fileString $this->localizeReferences($fileString);
  620.             }
  621.             $this->_createAndLoadFile($fileString);
  622.              
  623.             //1 if there are tables that refer to this one
  624.             //2 for each of those tables:
  625.             //3 get the set of remote key values of records that refer to any of the new records off the server
  626.             //4 reduce this set of key values to values that exist locally, and run an 'update' event for these records
  627.              
  628.             //1 get the objects that have foreign key references to this table
  629.             $referringTableObjects $this->serverConnection->getReferringObjects($this->tableName);
  630.             if (!empty($referringTableObjects)){
  631.                 //2
  632.                 foreach ($referringTableObjects as $object)
  633.                 {
  634.                     if ($object->referenceIsCascadingInsert($this->getTableName()))
  635.                     {
  636.                         //3
  637.                         //get the referring field's name
  638.                         $referringFieldName $object->getReferringFieldForTable($this->getTableName());
  639.                         $tableName $object->getTableName();
  640.                         $remoteKey $object->getRemoteKey();
  641.                         $keySet $this->serverConnection->getUrlConnection()->getData("getKeySet/$tableName/$remoteKey/".serialize($object->remoteMap)."/$referringFieldName/$keyString");
  642.                         $keySet unserialize($keySet);
  643.                         //$z = new DataInterFace();
  644.                         //   $keySet = unserialize($z->getKeySet($object->getTableName(),$object->getRemoteKey(),serialize($object->remoteMap),$referringFieldName,$keyString));
  645.                          
  646.                         //4
  647.                         $updateKeySet =  $object->getKeySetForUpdate($object->getRemoteKey()'IN'implode(",",$this->reduce($keySet$this->remoteKeyIsString)));// array_diff($this->reduce($keySet), $this->reduce($object->getRemoteKeySet()));
  648.                          
  649.                         //and run the update function based on the $delete_records dataset
  650.                         $object->updateLocalRecords(''$updateKeySet);
  651.                     }
  652.                 }
  653.                  
  654.             }
  655.              
  656.         }
  657.          
  658.  
  659.     }
  660.  
  661.     /**
  662.      * Enter description here...
  663.      * @todo possibly make it optional to include the update rules of referenced tables
  664.      *  currently this happens without choice.  Can be done with a boolean flag in the references table.
  665.      * @todo the 'keystring' that is used for the update event has the side effect that
  666.      *  records that have been deleted from the DoR will be permanently deleted from the cache
  667.      *  during the update event.  So the update event does more than just update.  As records that are
  668.      *  to be deleted are treated differently this should be changed.  One way to do this is to get the
  669.      *  keyset from the server and to trim the 'keystring' down to include only what's actually on the server.
  670.      *  A current workaround is to do the 'delete' event before the 'update' event.
  671.      * @param unknown_type $updateEventType 
  672.      */
  673.     protected function updateLocalRecords($updateEventType ''$keySet array())
  674.     {
  675.  
  676.         //$keySet = array();
  677.         $where '';
  678.         $fileString '';
  679.         $keyString '';
  680.         //if no $keySet was passed in as parameter, generate one first
  681.         if (empty($keySet)){
  682.             //retrieve set of keys for records to be updated
  683.              
  684.             //first get the keyset for the records to be updated
  685.             //according to the table's own update rule
  686.             $rule new UpdateLocalDataRule($this);
  687.             if (empty($updateEventType)){
  688.                 $keySet $rule->applyRule();
  689.             else {
  690.                 $keySet $rule->applyRule($updateEventType);
  691.             }
  692.             //if this table is dependent on other table(s)
  693.             //through foreign key references, determine which
  694.             //records to be updated according to the update rule(s) for the referenced table(s)
  695.              
  696.             if ($this->hasReferences())
  697.             {
  698.                 foreach ($this->references as $reference)
  699.                 {
  700.                     if ($reference['cascading_update'])
  701.                     {
  702.                         $referenced_table $this->serverConnection->getObject($reference['referenced_table']);
  703.                         $rule->setORM($referenced_table);
  704.                         $temp $rule->applyRule();
  705.                         if (!empty($temp)){
  706.                             //convert this set of remote key values from the
  707.                             //referenced table to local_key values if this is
  708.                             //a local_key reference
  709.                             //we have to do this because the foreign key values in this table will refer to local key values
  710.                             $keyIsString $referenced_table->remoteKeyIsString;
  711.                             if (!$referenced_table->isSelectOnly()){
  712.                                 $temp $referenced_table->convertRemoteKeyValuesToLocalKeyValues($temp);
  713.                                 $keyIsString $referenced_table->primaryKeyIsString;
  714.                             }
  715.  
  716.                             //finally, get the set of remote key values for any records that
  717.                             //refer to values in the $referenced_table that we now have in the $temp variable
  718.                             $temp $this->convertForeignKeyValuesToRemoteKeyValues($temp$reference['column'],$keyIsString);
  719.  
  720.                             //and add these to the keyset of records to be refreshed
  721.                             if (empty($keySet)){
  722.                                 $keySet $temp;
  723.                             else {
  724.                                 $keySet array_merge($keySet$temp);
  725.                             }
  726.                              
  727.                         }
  728.                     }
  729.                      
  730.                 }
  731.                 //lastly, remove any duplicates we might now have in our $keySet
  732.             }
  733.         }
  734.  
  735.         //if we now have a keySet, request a load file from the server
  736.         //the load file will contain the records that need to be updated in the local cache
  737.         if (!empty($keySet)){
  738.             $keyString implode(","$this->reduce($keySet$this->remoteKeyIsString));
  739.             $where " where $this->remoteKey IN ($keyString)";
  740.             //get the load data remotely - if nothing was specified in the Rule Object, no data will be fetched
  741.             $fileString $this->_getLoadFile($where);
  742.         }
  743.          
  744.  
  745.         //do the update thing
  746.         //if $fileString is empty it means that either the keyset was empty ie nothing to update
  747.         //or remotely all records were deleted, in which case the 'DeleteLocalRecords' method will
  748.         //do all that is needed
  749.          
  750.         if (!empty($fileString) ){
  751.             $keyPairs '';
  752.  
  753.             //if this is a table with local primary key values
  754.             //insert these values into the load file
  755.             if (!$this->isSelectOnly())
  756.             {
  757.                 $this->addLocalKeysToLoadFile($fileString);
  758.             }
  759.  
  760.             //if this is a table with references to local key values in other tables
  761.             //do the re-key thing now
  762.             if ($this->hasLocalKeyReferences())
  763.             {
  764.                 $fileString $this->localizeReferences($fileString);
  765.             }
  766.  
  767.             //delete the records that should be updated
  768.             //in future these should be written to a log file
  769.             //which should allow for a roll back of the whole event
  770.  
  771.             //we don't want to locally delete any records that no longer exist on the server
  772.             //at this point as we want to do this through the delete method whenever that runs
  773.             //that's why we're going to make sure that we delete only those records that are actually in the load file
  774.             $keyStringBasedOnLoadFile implode(","$this->extractColumnFromLoadFile($this->getColumnNumber($this->remoteKey)$fileString));
  775.             $this->where($this->remoteKey'IN'"$keyStringBasedOnLoadFile");
  776.             $this->delete();
  777.  
  778.  
  779.             //and load the new records to the table
  780.             $this->_createAndLoadFile($fileString);
  781.         }
  782.     }
  783.  
  784.  
  785.     private function _populateLocalTables()
  786.     {
  787.  
  788.         $x $this->_getLoadFile();
  789.         $x $this->localizeReferences($x);
  790.         $this->_createAndLoadFile($x);
  791.  
  792.  
  793.  
  794.     }
  795.  
  796.  
  797.  
  798.  
  799.     /**
  800.      * @method checkAndProcessStagedNewRecords 
  801.      * @todo Implement this method, and its counterpart on the server side as follows:
  802.      *  This method checks log files on the server side that report
  803.      *  the results of auditing previously submitted new records.
  804.      *  To this end the server side should implement a system of making the
  805.      *  relevant log files downloadable by clients who submitted new records.
  806.      *  The result of auditing is either 'pass', in which cas the log file will
  807.      *  contain the remote key value for the record, or 'fail' in which case the
  808.      *  new record was refused for some reason.  In the case of 'fail' there should
  809.      *  probably be a system of codes that indicate the reason, eg 'duplicate record' or something.
  810.      *
  811.      */
  812.     {
  813.          
  814.     }
  815.  
  816.     function writeNewRecords()
  817.     {
  818.  
  819.     }
  820.      
  821.     //----------------------------end data refresh methods -------------------------------------
  822.  
  823.  
  824.     //--------------------------------------methods to maniuplate loadfile strings---------------------------
  825.  
  826.     function addLocalKeysToLoadFile(&$fileString)
  827.     {
  828.         //first get the remote to lcal key mapping
  829.         $keyPairs $this->getLocalRemoteKeyPairs(TRUE);
  830.         $fileString $this->_replaceValuesInLoadFileString($fileString$keyPairs,$this->getColumnNumber($this->remoteKey)$this->getColumnNumber($this->primaryKey));
  831.  
  832.     }
  833.  
  834.     function extractColumnFromLoadFile($ColumnNumber$fileString)
  835.     {
  836.         $records explode("\n",$fileString);
  837.  
  838.         $extractedValues array();
  839.  
  840.  
  841.         foreach ($records as $record){
  842.             if (!empty($record)){
  843.                 $x explode("\t"$record);
  844.                 array_push($extractedValues$x[$ColumnNumber]);
  845.             }
  846.  
  847.  
  848.              
  849.         }
  850.         return $extractedValues;
  851.     }
  852.  
  853.     /**
  854.      * @method _replaceValuesInLoadFileString 
  855.      *  This method replaces values in a string in the manner as follows
  856.      *  from the description of the parameters
  857.      * @param string $fileString: 
  858.      *  representing the loadfile, tab delimited field values, newline delimited records
  859.      * @param Assoc Array $replaceValues:
  860.      *  key is lookup value, value is replace value
  861.      * @param integer $lookupColumn: 
  862.      *  column number that's the reference in the record to find the replace value
  863.      * @param integer $replaceColumn 
  864.      *  column number in the record the value of which should be replaced
  865.      * @return the $fileString with the $replaceColum updated with the replace value as found in
  866.      *  the $replaceValues array.
  867.      */
  868.     function _replaceValuesInLoadFileString($fileString$replaceValues$lookupColumn$replaceColumn)
  869.     {
  870.         //1 explode filestring into an array of strings
  871.         $records explode("\n",$fileString);
  872.  
  873.         $newRecs array();
  874.  
  875.  
  876.         foreach ($records as $record){
  877.             if (!empty($record)){
  878.                 $x explode("\t"$record);
  879.                  
  880.                 //to cover for the event that the load file has records that
  881.                 //cannot be resolved in the $replaceValues array
  882.                 //(can be caused by data corruption in terms of referential integrity
  883.                 //, or by the fact that the load file contains records that do not exist yet
  884.                 //in the local cache, which can happen in the all data refresh event)
  885.                 if (isset($replaceValues[$x[$lookupColumn]]))
  886.                 {
  887.                     $x[$replaceColumn]=$replaceValues[$x[$lookupColumn]];
  888.                 }
  889.                 array_push($newRecs,implode("\t"$x) );
  890.             }
  891.         }
  892.  
  893.         $fileString implode("\n"$newRecs);
  894.         return $fileString;
  895.     }
  896.  
  897.  
  898.  
  899.  
  900.     function localizeReferences($loadFile)
  901.     {
  902.         foreach ($this->references as $reference)
  903.         {
  904.             if ($this->isLocalKeyReference($reference))
  905.             {
  906.                  
  907.                 //get the value pairs for local/remote keys from the
  908.                 //referenced table
  909.                 $object $this->serverConnection->getObject($reference['referenced_table']);
  910.                 $keys $object->getLocalRemoteKeyPairs(TRUE);
  911.                  
  912.                 $loadFile $this->_replaceValuesInLoadFileString($loadFile$keys,$this->getColumnNumber($reference['column'])$this->getColumnNumber($reference['column']));
  913.                  
  914.             }
  915.         }
  916.         return $loadFile;
  917.     }
  918.  
  919.     function _createAndLoadFile($fileString)
  920.     {
  921.         
  922.         $handle fopen(PATH_LOADFILES."\\$this->tableName.txt""w");       
  923.         fwrite($handle$fileString);
  924.         fclose($handle);        
  925.         $this->query("Load data infile '".PATH_LOADFILES."/$this->tableName.txt'
  926.         into table $this->tableName CHARACTER SET utf8");       
  927.         unlink(PATH_LOADFILES."//$this->tableName.txt");
  928.  
  929.     }
  930.  
  931.     function  _getLoadFile($keySet '')
  932.     {
  933.         if (!empty($keySet))
  934.         {
  935.             //$x = new DataInterface();
  936.             //$_POST['var1']= serialize($keySet);
  937.             // return $x->getLoadFile($this->tableName,serialize($this->remoteMap));
  938.  
  939.             return $this->serverConnection->getUrlConnection()->postData("getLoadFile/$this->tableName/".serialize($this->remoteMap)array(serialize($keySet)));
  940.  
  941.         else {
  942.             return $this->serverConnection->getUrlConnection()->getData("getLoadFile/$this->tableName/".serialize($this->remoteMap));
  943.         }
  944.     }
  945.  
  946.  
  947.     //--------------------------------------end methods to maniuplate loadfile strings---------------------------
  948.      
  949.  
  950.     //------------------------------------------Utility Methods---------------------------------------------------
  951.     /**
  952.      * utility function to reduce an array of arrays to a 'simple' array
  953.      * that is: each value in the returned $values array is not an array but one string value
  954.      * if the second dimension array contains multiple values these will be imploded to
  955.      * a comma delimited string
  956.      *
  957.      * @param two dimensional array $data
  958.      * @return one dimensional array $values
  959.      */
  960.     private function reduce($data$AddQuotes FALSE)
  961.     {
  962.         $values array();
  963.         foreach ($data as $value)
  964.         {
  965.             array_push($valuesimplode(','$value));
  966.         }
  967.         if ($AddQuotes){
  968.             array_walk($valuesarray($this'surroundValues')"'");
  969.         }
  970.         return $values;
  971.     }
  972.  
  973.     function surroundValues(&$item1$key$char)
  974.     {
  975.         $item1 "$char$item1$char";
  976.     }
  977.      
  978. }
  979.  
  980. ?>

Documentation generated on Mon, 18 May 2009 11:22:01 +0200 by phpDocumentor 1.4.1