Index: trunk/core/kernel/utility/temp_handler.php =================================================================== diff -u -r2501 -r2791 --- trunk/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 2501) +++ trunk/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 2791) @@ -2,7 +2,7 @@ class kTempTablesHandler extends kBase { var $Tables = Array(); - + /** * Master table name for temp handler * @@ -18,31 +18,33 @@ * @access private */ var $MasterIDs = Array(); - + var $AlreadyProcessed = Array(); - + + var $DroppedTables = Array(); + /** * Description * * @var kDBConnection * @access public */ var $Conn; - + function kTempTablesHandler() { parent::kBase(); $this->Conn =& $this->Application->GetADODBConnection(); } - + function SetTables($tables) { // set tablename as key for tables array $ret = Array(); $this->Tables = $tables; $this->MasterTable = $tables['TableName']; } - + /** * Get temp table name * @@ -54,12 +56,12 @@ // function is sometimes called as static, so we CAN'T use $this->GetTempTablePrefix() here return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_'.$table; } - + function GetTempTablePrefix() { return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_'; } - + /** * Return live table name based on temp table name * @@ -77,12 +79,12 @@ return $temp_table; } } - + function IsTempTable($table) { return strpos($table, TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_') !== false; } - + /** * Return temporary table name for master table * @@ -93,15 +95,15 @@ { return $this->GetTempName($this->MasterTable); } - + function CreateTempTable($table) { $query = sprintf("CREATE TABLE %s SELECT * FROM %s WHERE 0", $this->GetTempName($table), $table); $this->Conn->Query($query); } - + function BuildTables($prefix, $ids) { $tables = Array( @@ -110,16 +112,16 @@ 'IDs' => $ids, 'Prefix' => $prefix, ); - + $SubItems = $this->Application->getUnitOption($prefix,'SubItems'); if (is_array($SubItems)) { foreach ($SubItems as $prefix) { - $this->AddTables($prefix, $tables); + $this->AddTables($prefix, $tables); } } $this->SetTables($tables); } - + function AddTables($prefix, &$tables) { $tmp = Array( @@ -131,10 +133,10 @@ 'AutoClone' => $this->Application->getUnitOption($prefix,'AutoClone'), 'AutoDelete' => $this->Application->getUnitOption($prefix,'AutoDelete'), ); - + $constrain = $this->Application->getUnitOption($prefix,'Constrain'); if ($constrain) $tmp['Constrain'] = $constrain; - + $SubItems = $this->Application->getUnitOption($prefix,'SubItems'); $same_sub_counter = 1; if (is_array($SubItems)) { @@ -153,25 +155,25 @@ } } } - + if ( !is_array(getArrayValue($tables, 'SubTables')) ) { $tables['SubTables'] = array(); } - + $tables['SubTables'][] = $tmp; } - + function CloneItems($prefix, $special, $ids, $master=null, $foreign_key=null, $parent_prefix=null) { if (!isset($master)) $master = $this->Tables; if( strpos($prefix,'.') !== false ) list($prefix,$special) = explode('.', $prefix, 2); - + $prefix_special = rtrim($prefix.'.'.$special, '.'); - + //recalling by different name, because we may get kDBList, if we recall just by prefix $recall_prefix = $prefix_special.($special ? '' : '.').'-item'; $this->Application->setUnitOption($prefix, 'AutoLoad', false); - + $object =& $this->Application->recallObject($recall_prefix, $prefix); foreach ($ids as $id) @@ -185,106 +187,106 @@ $mode = 'update'; } } - + $object->Load($id); $original_values = $object->FieldValues; - + $object->NameCopy($master, $foreign_key); - + if (isset($foreign_key)) { $master_foreign_key_field = is_array($master['ForeignKey']) ? $master['ForeignKey'][$parent_prefix] : $master['ForeignKey']; $object->SetDBField($master_foreign_key_field, $foreign_key); } - + if ($mode == 'create') { $this->RaiseEvent('OnBeforeClone', $master['Prefix'], Array($object->GetId()) ); } - + $res = $mode == 'update' ? $object->Update() : $object->Create(); - + if( $res ) { if ( $mode == 'create' && is_array( getArrayValue($master, 'ForeignKey')) ) { - // remember original => clone mapping for dual ForeignKey updating + // remember original => clone mapping for dual ForeignKey updating $this->AlreadyProcessed[$master['TableName']][$id] = $object->GetId(); } if($object->mode == 't') $object->setTempID(); if ($mode == 'create') { $this->RaiseEvent('OnAfterClone', $master['Prefix'], Array($object->GetId()) ); } - + if ( is_array(getArrayValue($master, 'SubTables')) ) { foreach($master['SubTables'] as $sub_table) { if (!getArrayValue($sub_table, 'AutoClone')) continue; $sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName']; - + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; $parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; - - $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' + + $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field]; - + $sub_ids = $this->Conn->GetCol($query); - + if ( is_array(getArrayValue($sub_table, 'ForeignKey')) ) { // $sub_ids could containt newly cloned items, we need to remove it here // to escape double cloning - + $cloned_ids = getArrayValue($this->AlreadyProcessed, $sub_table['TableName']); if ( !$cloned_ids ) $cloned_ids = Array(); $new_ids = array_values($cloned_ids); $sub_ids = array_diff($sub_ids, $new_ids); } - + $parent_key = $object->GetDBField($parent_key_field); - + $this->CloneItems($sub_table['Prefix'], '', $sub_ids, $sub_table, $parent_key, $master['Prefix']); } } } } } - + function DeleteItems($prefix, $special, $ids, $master=null, $foreign_key=null) { if (!isset($master)) $master = $this->Tables; if( strpos($prefix,'.') !== false ) list($prefix,$special) = explode('.', $prefix, 2); - + $prefix_special = rtrim($prefix.'.'.$special, '.'); - + //recalling by different name, because we may get kDBList, if we recall just by prefix $recall_prefix = $prefix_special.($special ? '' : '.').'-item'; $this->Application->setUnitOption($prefix,'AutoLoad',false); $object =& $this->Application->recallObject($recall_prefix, $prefix); - + foreach ($ids as $id) { $object->Load($id); $original_values = $object->FieldValues; if( !$object->Delete($id) ) continue; - + if ( is_array(getArrayValue($master, 'SubTables')) ) { foreach($master['SubTables'] as $sub_table) { if (!getArrayValue($sub_table, 'AutoDelete')) continue; $sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName']; - + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; $parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; - - $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' + + $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.' WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field]; - + $sub_ids = $this->Conn->GetCol($query); - + $parent_key = $object->GetDBField($sub_table['ParentTableKey']); - + $this->DeleteItems($sub_table['Prefix'], '', $sub_ids, $sub_table, $parent_key); } } - - } + + } } - + function DoCopyLiveToTemp($master, $ids, $parent_prefix=null) { // when two tables refers the same table as sub-sub-table, and ForeignKey and ParentTableKey are arrays @@ -294,11 +296,11 @@ $this->DropTempTable($master['TableName']); $this->CreateTempTable($master['TableName']); } - + if (is_array($ids)) { $ids = join(',', $ids); } - + if ($ids != '') { if ( getArrayValue($master, 'ForeignKey') ) { if ( is_array($master['ForeignKey']) ) { @@ -311,26 +313,26 @@ else { $key_field = $master['IdField']; } - + $query = 'INSERT INTO '.$this->GetTempName($master['TableName']).' SELECT * FROM '.$master['TableName'].' WHERE '.$key_field.' IN ('.$ids.')'; if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; $this->Conn->Query($query); - + $query = 'SELECT '.$master['IdField'].' FROM '.$master['TableName'].' WHERE '.$key_field.' IN ('.$ids.')'; if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; $this->RaiseEvent( 'OnAfterCopyToTemp', $master['Prefix'], $this->Conn->GetCol($query) ); } - + if ( getArrayValue($master, 'SubTables') ) { foreach ($master['SubTables'] as $sub_table) { - + $parent_key = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey']; - + if ( $ids != '' && $parent_key != $key_field ) { - $query = 'SELECT '.$parent_key.' FROM '.$master['TableName'].' + $query = 'SELECT '.$parent_key.' FROM '.$master['TableName'].' WHERE '.$key_field.' IN ('.$ids.')'; $sub_foreign_keys = join(',', $this->Conn->GetCol($query)); } @@ -341,16 +343,16 @@ } } } - - function GetForeignKeys($master, $sub_table, $live_id, $temp_id=null) + + function GetForeignKeys($master, $sub_table, $live_id, $temp_id=null) { $mode = 1; //multi if (!is_array($live_id)) { $live_id = Array($live_id); $mode = 2; //single } if (isset($temp_id) && !is_array($temp_id)) $temp_id = Array($temp_id); - + if ( isset($sub_table['ParentTableKey']) ) { if ( is_array($sub_table['ParentTableKey']) ) { $parent_key_field = $sub_table['ParentTableKey'][$master['Prefix']]; @@ -362,7 +364,7 @@ else { $parent_key_field = $master['IdField']; } - + if ( $cached = getArrayValue($this->FKeysCache, $master['TableName'].'.'.$parent_key_field) ) { if ( array_key_exists(serialize($live_id), $cached) ) { list($live_foreign_key, $temp_foreign_key) = $cached[serialize($live_id)]; @@ -376,12 +378,12 @@ } if ($parent_key_field != $master['IdField']) { - $query = 'SELECT '.$parent_key_field.' FROM '.$master['TableName'].' + $query = 'SELECT '.$parent_key_field.' FROM '.$master['TableName'].' WHERE '.$master['IdField'].' IN ('.join(',', $live_id).')'; $live_foreign_key = $this->Conn->GetCol($query); - + if (isset($temp_id)) { - $query = 'SELECT '.$parent_key_field.' FROM '.$this->GetTempName($master['TableName']).' + $query = 'SELECT '.$parent_key_field.' FROM '.$this->GetTempName($master['TableName']).' WHERE '.$master['IdField'].' IN ('.join(',', $temp_id).')'; $temp_foreign_key = $this->Conn->GetCol($query); } @@ -393,62 +395,62 @@ $live_foreign_key = $live_id; $temp_foreign_key = $temp_id; } - + $this->FKeysCache[$master['TableName'].'.'.$parent_key_field][serialize($live_id)] = Array($live_foreign_key, $temp_foreign_key); - + if ($mode == 1) { return $live_foreign_key; } else { return Array($live_foreign_key[0], $temp_foreign_key[0]); } } - - function DoCopyTempToOriginal($master, $parent_prefix=null) + + function DoCopyTempToOriginal($master, $parent_prefix=null) { $query = 'SELECT '.$master['IdField'].' FROM '.$this->GetTempName($master['TableName']); if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain']; $current_ids = $this->Conn->GetCol($query); - + if ($current_ids) { // delete all ids from live table - for MasterTable ONLY! // because items from Sub Tables get deteleted in CopySubTablesToLive !BY ForeignKey! if ($master['TableName'] == $this->MasterTable) { $this->RaiseEvent( 'OnBeforeDeleteFromLive', $master['Prefix'], $current_ids ); - + $query = 'DELETE FROM '.$master['TableName'].' WHERE '.$master['IdField'].' IN ('.join(',', $current_ids).')'; $this->Conn->Query($query); } - + if ( getArrayValue($master, 'SubTables') ) { foreach ($current_ids AS $id) { $this->RaiseEvent( 'OnBeforeCopyToLive', $master['Prefix'], Array($id) ); - + //reset negative ids to 0, so autoincrement in live table works fine if ($id < 0) { - $query = 'UPDATE '.$this->GetTempName($master['TableName']).' - SET '.$master['IdField'].' = 0 + $query = 'UPDATE '.$this->GetTempName($master['TableName']).' + SET '.$master['IdField'].' = 0 WHERE '.$master['IdField'].' = '.$id; $this->Conn->Query($query); $id_to_copy = 0; } else { $id_to_copy = $id; } - + //copy current id_to_copy (0 for new or real id) to live table - $query = 'INSERT INTO '.$master['TableName'].' - SELECT * FROM '.$this->GetTempName($master['TableName']).' + $query = 'INSERT INTO '.$master['TableName'].' + SELECT * FROM '.$this->GetTempName($master['TableName']).' WHERE '.$master['IdField'].' = '.$id_to_copy; $this->Conn->Query($query); $insert_id = $id_to_copy == 0 ? $this->Conn->getInsertID() : $id_to_copy; - + $this->RaiseEvent( 'OnAfterCopyToLive', $master['Prefix'], Array($insert_id) ); - - $this->UpdateForeignKeys($master, $insert_id, $id); - + + $this->UpdateForeignKeys($master, $insert_id, $id); + //delete already copied record from master temp table - $query = 'DELETE FROM '.$this->GetTempName($master['TableName']).' + $query = 'DELETE FROM '.$this->GetTempName($master['TableName']).' WHERE '.$master['IdField'].' = '.$id_to_copy; $this->Conn->Query($query); } @@ -457,33 +459,33 @@ } else { //If current master doesn't have sub-tables - we could use mass operations // We don't need to delete items from live here, as it get deleted in the beggining of the method for MasterTable - // or in parent table processing for sub-tables - + // or in parent table processing for sub-tables + $this->RaiseEvent('OnBeforeCopyToLive', $master['Prefix'], $current_ids); - + // reset ALL negative IDs to 0 so it get inserted into live table with autoincrement - $query = 'UPDATE '.$this->GetTempName($master['TableName']).' - SET '.$master['IdField'].' = 0 + $query = 'UPDATE '.$this->GetTempName($master['TableName']).' + SET '.$master['IdField'].' = 0 WHERE '.$master['IdField'].' < 0'; if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain']; $this->Conn->Query($query); - + // copy ALL records to live table - $query = 'INSERT INTO '.$master['TableName'].' + $query = 'INSERT INTO '.$master['TableName'].' SELECT * FROM '.$this->GetTempName($master['TableName']); if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain']; $this->Conn->Query($query); - + /* - + !!! WE NEED TO FIND A WAY TO DETERMINE IF OnAfterCopyToLive is not an empty method, and do on-by-one insert and pass Ids to OnAfterCopyToLive, otherwise it's not smart to do on-by-one insert for any object OR WE COULD FIND A WAY TO GET ALL INSERTED IDS as an array and iterate them !!! - + $this->RaiseEvent('OnAfterCopyToLive', IDS ??? ); - + */ - + // no need to clear temp table - it will be dropped by next statement } } @@ -494,26 +496,26 @@ } $this->DropTempTable($master['TableName']); } - - function UpdateForeignKeys($master, $live_id, $temp_id) { + + function UpdateForeignKeys($master, $live_id, $temp_id) { foreach ($master['SubTables'] as $sub_table) { list ($live_foreign_key, $temp_foreign_key) = $this->GetForeignKeys($master, $sub_table, $live_id, $temp_id); - + $foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey']; - + //Update ForeignKey in sub TEMP table - if ($live_foreign_key != $temp_foreign_key) { - $query = 'UPDATE '.$this->GetTempName($sub_table['TableName']).' - SET '.$foreign_key_field.' = '.$live_foreign_key.' + if ($live_foreign_key != $temp_foreign_key) { + $query = 'UPDATE '.$this->GetTempName($sub_table['TableName']).' + SET '.$foreign_key_field.' = '.$live_foreign_key.' WHERE '.$foreign_key_field.' = '.$temp_foreign_key; $this->Conn->Query($query); } } } - + function CopySubTablesToLive($master, $current_ids) { foreach ($master['SubTables'] as $sub_table) { - + // delete records from live table by foreign key, so that records deleted from temp table // get deleted from live if (count($current_ids) > 0) { @@ -523,21 +525,21 @@ $query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_table['TableName'].' WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')'; if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain']; - + $this->RaiseEvent( 'OnBeforeDeleteFromLive', $sub_table['Prefix'], $this->Conn->GetCol($query) ); - - $query = 'DELETE FROM '.$sub_table['TableName'].' + + $query = 'DELETE FROM '.$sub_table['TableName'].' WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')'; if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain']; $this->Conn->Query($query); } } - + //sub_table passed here becomes master in the method, and recursively updated and copy its sub tables $this->DoCopyTempToOriginal($sub_table, $master['Prefix']); } } - + function RaiseEvent($name, $prefix, $ids) { if ( !is_array($ids) ) return; @@ -547,25 +549,27 @@ $this->Application->HandleEvent($event); } } - + function DropTempTable($table) { + if (in_array($table, $this->DroppedTables)) return; $query = sprintf("DROP TABLE IF EXISTS %s", $this->GetTempName($table) ); + $this->DroppedTables = array_unique(array_push($this->DroppedTables, $table)); $this->Conn->Query($query); } - + function PrepareEdit() { $this->DoCopyLiveToTemp($this->Tables, $this->Tables['IDs']); } - + function SaveEdit($skip_master=0) { $this->DoCopyTempToOriginal($this->Tables); } - + function CancelEdit($master=null) { if (!isset($master)) $master = $this->Tables;