/**
* Method to store a node in the database table.
*
* @param boolean $updateNulls True to update null values as well.
*
* @return boolean True on success.
*
* @since 1.7.0
*/
public function store($updateNulls = false)
{
$k = $this->_tbl_key;
// Pre-processing by observers
$event = AbstractEvent::create('onTableBeforeStore', ['subject' => $this, 'updateNulls' => $updateNulls, 'k' => $k]);
$this->getDispatcher()->dispatch('onTableBeforeStore', $event);
if ($this->_debug) {
echo "\n" . \get_class($this) . "::store\n";
$this->_logtable(true, false);
}
/*
* If the primary key is empty, then we assume we are inserting a new node into the
* tree. From this point we would need to determine where in the tree to insert it.
*/
if (empty($this->{$k})) {
/*
* We are inserting a node somewhere in the tree with a known reference
* node. We have to make room for the new node and set the left and right
* values before we insert the row.
*/
if ($this->_location_id >= 0) {
// Lock the table for writing.
if (!$this->_lock()) {
// Error message set in lock method.
return false;
}
// We are inserting a node relative to the last root node.
if ($this->_location_id == 0) {
// Get the last root node as the reference node.
$query = $this->_db->getQuery(true)->select($this->_tbl_key . ', parent_id, level, lft, rgt')->from($this->_tbl)->where('parent_id = 0')->order('lft DESC');
$query->setLimit(1);
$this->_db->setQuery($query);
$reference = $this->_db->loadObject();
if ($this->_debug) {
$this->_logtable(false);
}
} else {
// Get the reference node by primary key.
if (!($reference = $this->_getNode($this->_location_id))) {
// Error message set in getNode method.
$this->_unlock();
return false;
}
}
// Get the reposition data for shifting the tree and re-inserting the node.
if (!($repositionData = $this->_getTreeRepositionData($reference, 2, $this->_location))) {
// Error message set in getNode method.
$this->_unlock();
return false;
}
// Create space in the tree at the new location for the new node in left ids.
$query = $this->_db->getQuery(true)->update($this->_tbl)->set('lft = lft + 2')->where($repositionData->left_where);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
// Create space in the tree at the new location for the new node in right ids.
$query->clear()->update($this->_tbl)->set('rgt = rgt + 2')->where($repositionData->right_where);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
// Set the object values.
$this->parent_id = $repositionData->new_parent_id;
$this->level = $repositionData->new_level;
$this->lft = $repositionData->new_lft;
$this->rgt = $repositionData->new_rgt;
} else {
// Negative parent ids are invalid
$e = new \UnexpectedValueException(sprintf('%s::store() used a negative _location_id', \get_class($this)));
$this->setError($e);
return false;
}
} else {
// If the location has been set, move the node to its new location.
if ($this->_location_id > 0) {
// Skip recursiveUpdatePublishedColumn method, it will be called later.
if (!$this->moveByReference($this->_location_id, $this->_location, $this->{$k}, false)) {
// Error message set in move method.
return false;
}
}
// Lock the table for writing.
if (!$this->_lock()) {
// Error message set in lock method.
return false;
}
}
// We do not want parent::store to update observers since tables are locked and we are updating it from this
// level of store():
$oldDispatcher = clone $this->getDispatcher();
$blankDispatcher = new Dispatcher();
$this->setDispatcher($blankDispatcher);
$result = parent::store($updateNulls);
// Restore previous callable dispatcher state:
$this->setDispatcher($oldDispatcher);
if ($result) {
if ($this->_debug) {
$this->_logtable();
}
}
// Unlock the table for writing.
$this->_unlock();
if ($result && $this->hasField('published')) {
$this->recursiveUpdatePublishedColumn($this->{$k});
}
// Post-processing by observers
$event = AbstractEvent::create('onTableAfterStore', ['subject' => $this, 'result' => &$result]);
$this->getDispatcher()->dispatch('onTableAfterStore', $event);
return $result;
}