/** * @param NodeRef $nodeRef * @param $metaID * @param int $value * * @throws NodeException * @throws SQLException * @throws Exception */ public function decrementMeta(NodeRef $nodeRef, $metaID, $value = 1) { $metaID = ltrim($metaID, '#'); $this->NodeEvents->fireNodeEvents(__FUNCTION__, '', $nodeRef, $metaID); $db = $this->getConnectionForWrite($nodeRef); $id = $this->getRecordIDFromNodeRef($nodeRef); // determine the meta storage $metaDef = $nodeRef->getElement()->getSchema()->getMetaDef($metaID); $datatype = $metaDef->getDatatype(); $table = $db->quoteIdentifier($this->NodeDBMeta->getMetaTable($nodeRef, $datatype)); $tableid = $this->NodeDBMeta->getPrimaryKey($nodeRef); $datatypeCol = $this->NodeDBMeta->getMetaDatatypeColumn($datatype); $value = intval($value); $newValue = false; $oldValue = false; $originalMeta = null; $maxValue = $this->getMaxIntForDatatype($datatype); $minValue = $this->getMinIntForDatatype($datatype); try { $affectedRows = $db->write("UPDATE {$table} SET {$datatypeCol}Value = LAST_INSERT_ID({$datatypeCol}Value-{$value}) WHERE {$tableid} = {$db->quote($id)} AND Name = {$db->quote($metaID)}", DatabaseInterface::AFFECTED_ROWS); if ($affectedRows == 1) { $newValue = intval($db->readField("SELECT LAST_INSERT_ID()")); $oldValue = $newValue + $value; } } catch (SQLException $e) { if ($e->getCode() != 22003 || strpos($e->getMessage(), 'Numeric value out of range') === false) { throw $e; } // must ask the DB for its current value $oldValue = intval($db->readField("SELECT {$datatypeCol}Value FROM {$table} WHERE {$tableid} = {$db->quote($id)} AND Name = {$db->quote($metaID)}")); $newValue = $oldValue - $value; // ensure the new values don't overflow (note that $value can be positive or negative and isn't necessarily 1) if ($newValue > $maxValue) { $newValue = $maxValue; } elseif ($newValue < $minValue) { $newValue = $minValue; } if ($newValue === $oldValue) { $affectedRows = 1; } else { $affectedRows = $db->write("UPDATE {$table} SET {$datatypeCol}Value = {$newValue} WHERE {$tableid} = {$db->quote($id)} AND Name = {$db->quote($metaID)}", DatabaseInterface::AFFECTED_ROWS); } } catch (Exception $e) { throw $e; } // new value didn't get set because the meta record hasn't been created yet. if ($newValue === false) { $newValue = $value * -1; // ensure the new values don't overflow (note that $value can be positive or negative and isn't necessarily 1) if ($newValue > $maxValue) { $newValue = $maxValue; } elseif ($newValue < $minValue) { $newValue = $minValue; } } if ($affectedRows == 0) { $affectedRows = $db->write("INSERT INTO {$table} ({$tableid}, Name, {$datatypeCol}Value) Values ({$db->quote($id)}, {$db->quote($metaID)}, {$newValue}) ON DUPLICATE KEY UPDATE {$datatypeCol}Value = LAST_INSERT_ID({$datatypeCol}Value-{$value})", DatabaseInterface::AFFECTED_ROWS); // new record inserted if ($affectedRows == 1) { $metaEvent = 'add'; $newMeta = new Meta($metaID, $newValue); $newMeta->setMetaStorageDatatype($metaDef->Datatype); $originalMeta = null; // duplicate key update encountered } else { if ($affectedRows == 2) { $metaEvent = 'update'; $newValue = intval($db->readField("SELECT LAST_INSERT_ID()")); $oldValue = $newValue + $value; $newMeta = new Meta($metaID, $newValue); $newMeta->setMetaStorageDatatype($metaDef->Datatype); $originalMeta = new Meta($metaID, $oldValue); $originalMeta->setMetaStorageDatatype($metaDef->Datatype); } else { throw new NodeException('Unable to decrement meta ID [' . $metaID . '] on record [' . $nodeRef . ']'); } } } else { $metaEvent = 'update'; $newMeta = new Meta($metaID, $newValue); $newMeta->setMetaStorageDatatype($metaDef->Datatype); $originalMeta = new Meta($metaID, $oldValue); $originalMeta->setMetaStorageDatatype($metaDef->Datatype); } $this->NodeEvents->fireMetaEvents('meta', $metaEvent, $nodeRef, $newMeta, $originalMeta); }