getObject(); $field_options = $object->GetFieldOptions('Priority'); $table_name = $event->getUnitConfig()->getTableName(); $sql = 'SELECT COUNT(*) FROM ' . $table_name . ' item_table ' . $joins; if ( $constrain ) { $sql .= ' WHERE ' . $this->normalizeConstrain($constrain); } if ( !$object->isField('OldPriority') ) { // make sure, then OldPriority field is defined $virtual_fields = $object->getVirtualFields(); $virtual_fields['OldPriority'] = Array ('type' => 'int', 'default' => 0); $object->setVirtualFields($virtual_fields); } $items_count = $this->Conn->GetOne($sql); $current_priority = $object instanceof kDBList ? 0 : $object->GetDBField('Priority'); if ( $is_new || $current_priority == -($items_count + 1) || $this->isTempTableOnly($object) ) { $items_count++; } if ( $is_new ) { // add new item to the end of list $object->SetDBField('Priority', -$items_count); $object->SetDBField('OldPriority', -$items_count); } else { // storing priority right after load for comparing when updating $object->SetDBField('OldPriority', $current_priority); } for ($i = 1; $i <= $items_count; $i++) { $field_options['options'][-$i] = $i; } $object->SetFieldOptions('Priority', $field_options); } /** * Determines if an item only exists in temp table. * * @param kDBBase $object Object. * * @return boolean */ protected function isTempTableOnly(kDBBase $object) { if ( !$object->IsTempTable() || ($object instanceof kDBList) ) { return false; } return $object->GetID() <= 0; } /** * Updates priorities for changed items * * @param kEvent $event * @param Array $changes = Array (ID => Array ('constrain' => ..., 'new' => ..., 'old' => ...), ...) * @param Array $new_ids = Array (temp_id => live_id) * @param string $constrain * @param string $joins * @return Array */ function updatePriorities($event, $changes, $new_ids, $constrain = '', $joins = '') { // TODO: no need pass external $constrain, since the one from $pair is used if ( !$changes ) { // no changes to process return Array (); } $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $this->Application->IsTempMode($event->Prefix, $event->Special) ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $ids = Array (); $not_processed = array_keys($changes); foreach ($changes as $id => $pair) { array_push($ids, $id); $constrain = isset($pair['constrain']) ? $this->normalizeConstrain($pair['constrain']) . ' AND ' : ''; if ( $pair['old'] == 'new' ) { // replace 0 with newly created item id (from $new_ids mapping) $not_processed[array_search($id, $not_processed)] = $new_ids[$id]; $id = $new_ids[$id]; $sql = 'SELECT MIN(item_table.Priority) FROM ' . $table_name . ' item_table ' . $joins . ' WHERE ' . $constrain . ' item_table.' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; $min_priority = (int)$this->Conn->GetOne($sql) - 1; if ( $pair['new'] < $min_priority ) { $pair['new'] = $min_priority; } $pair['old'] = $min_priority; } if ( $pair['new'] < $pair['old'] ) { $set = ' SET item_table.Priority = item_table.Priority + 1'; $where = ' WHERE ' . $constrain . ' item_table.Priority >= ' . $pair['new'] . ' AND item_table.Priority < ' . $pair['old'] . ' AND ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } elseif ( $pair['new'] > $pair['old'] ) { $set = ' SET item_table.Priority = item_table.Priority - 1'; $where = ' WHERE ' . $constrain . ' item_table.Priority > ' . $pair['old'] . ' AND item_table.Priority <= ' . $pair['new'] . ' AND ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } else { $set = ' SET item_table.Priority = ' . $pair['new']; $where = ' WHERE ' . $id_field . ' = ' . $id; } $sql = 'SELECT item_table.' . $id_field . ' FROM ' . $table_name . ' item_table ' . $joins . ' ' . $where; $ids = array_merge($ids, $this->Conn->GetCol($sql)); $q = 'UPDATE ' . $table_name . ' item_table ' . $joins . ' ' . $set . $where; $this->Conn->Query($q); unset($not_processed[array_search($id, $not_processed)]); } return $ids; } /** * Recalculates priorities * * @param kEvent $event * @param string $constrain * @param string $joins * @return Array */ function recalculatePriorities($event, $constrain = '', $joins = '') { $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $constrain ) { $constrain = $this->normalizeConstrain($constrain); } if ( $this->Application->IsTempMode($event->Prefix, $event->Special) ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' item_table ' . $joins . ' ' . ($constrain ? ' WHERE ' . $constrain : '') . ' ORDER BY item_table.Priority DESC'; $items = $this->Conn->GetCol($sql); foreach ($items as $item_number => $item_id) { $sql = 'UPDATE ' . $table_name . ' SET Priority = ' . -($item_number + 1) . ' WHERE ' . $id_field . ' = ' . $item_id; $this->Conn->Query($sql); } return $items; } /** * Adds current table name into constrain if doesn't have it already (to prevent ambiguous columns during joins) * * @param string $constrain * @return string */ function normalizeConstrain($constrain) { if ( strpos($constrain, '.') === false ) { return 'item_table.' . $constrain; } return $constrain; } /** * Performs fake kDBItem::Update call, so any OnBefore/OnAfter events would be notified of priority change * * @param string $prefix * @param Array $ids */ function massUpdateChanged($prefix, $ids) { $ids = array_unique($ids); /** @var kDBItem $dummy */ $dummy = $this->Application->recallObject($prefix . '.-dummy', null, Array ('skip_autoload' => true)); $sql = $dummy->GetSelectSQL() . ' WHERE ' . $dummy->TableName . '.' . $dummy->IDField . ' IN (' . implode(',', $ids) . ')'; $records = $this->Conn->Query($sql); foreach ($records as $record) { $dummy->LoadFromHash($record); $dummy->Update(); } } }