IniRepository: Add support for triggers

This commit is contained in:
Johannes Meyer 2016-10-27 15:11:21 +02:00
parent f644860529
commit 957ad9361f
1 changed files with 143 additions and 5 deletions

View File

@ -5,6 +5,7 @@ namespace Icinga\Repository;
use Exception; use Exception;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Data\ConfigObject;
use Icinga\Data\Extensible; use Icinga\Data\Extensible;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Updatable; use Icinga\Data\Updatable;
@ -18,6 +19,7 @@ use Icinga\Exception\StatementException;
* Additionally provided features: * Additionally provided features:
* <ul> * <ul>
* <li>Insert, update and delete capabilities</li> * <li>Insert, update and delete capabilities</li>
* <li>Triggers for inserts, updates and deletions</li>
* </ul> * </ul>
*/ */
abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible
@ -29,6 +31,19 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
*/ */
protected $ds; protected $ds;
/**
* The tables for which triggers are available when inserting, updating or deleting rows
*
* This may be initialized by concrete repository implementations and describes for which table names triggers
* are available. The repository attempts to find a method depending on the type of event and table for which
* to run the trigger. The name of such a method is expected to be declared using lowerCamelCase.
* (e.g. group_membership will be translated to onUpdateGroupMembership and groupmembership will be translated
* to onUpdateGroupmembership) The available events are onInsert, onUpdate and onDelete.
*
* @var array
*/
protected $triggers;
/** /**
* Create a new INI repository object * Create a new INI repository object
* *
@ -45,6 +60,119 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
} }
} }
/**
* Return the tables for which triggers are available when inserting, updating or deleting rows
*
* Calls $this->initializeTriggers() in case $this->triggers is null.
*
* @return array
*/
public function getTriggers()
{
if ($this->triggers === null) {
$this->triggers = $this->initializeTriggers();
}
return $this->triggers;
}
/**
* Overwrite this in your repository implementation in case you need to initialize the triggers lazily
*
* @return array
*/
protected function initializeTriggers()
{
return array();
}
/**
* Run a trigger for the given table and row which is about to be inserted
*
* @param string $table
* @param ConfigObject $new
*
* @return ConfigObject
*/
public function onInsert($table, ConfigObject $new)
{
$trigger = $this->getTrigger($table, 'onInsert');
if ($trigger !== null) {
$row = $this->$trigger($new);
if ($row !== null) {
$new = $row;
}
}
return $new;
}
/**
* Run a trigger for the given table and row which is about to be updated
*
* @param string $table
* @param ConfigObject $old
* @param ConfigObject $new
*
* @return ConfigObject
*/
public function onUpdate($table, ConfigObject $old, ConfigObject $new)
{
$trigger = $this->getTrigger($table, 'onUpdate');
if ($trigger !== null) {
$row = $this->$trigger($old, $new);
if ($row !== null) {
$new = $row;
}
}
return $new;
}
/**
* Run a trigger for the given table and row which has been deleted
*
* @param string $table
* @param ConfigObject $old
*
* @return ConfigObject
*/
public function onDelete($table, ConfigObject $old)
{
$trigger = $this->getTrigger($table, 'onDelete');
if ($trigger !== null) {
$this->$trigger($old);
}
}
/**
* Return the name of the trigger method for the given table and event-type
*
* @param string $table The table name for which to return a trigger method
* @param string $event The name of the event type
*
* @return string
*
* @throws ProgrammingError In case the table is registered as having triggers but not any trigger is found
*/
protected function getTrigger($table, $event)
{
if (! in_array($table, $this->getTriggers())) {
return;
}
$identifier = join('', array_map('ucfirst', explode('_', $table)));
if (! method_exists($this, $event . $identifier)) {
throw new ProgrammingError(
'Cannot find any trigger for table "%s". Add a trigger or remove the table from %s::$triggers',
$table,
get_class($this)
);
}
return $event . $identifier;
}
/** /**
* Insert the given data for the given target * Insert the given data for the given target
* *
@ -64,7 +192,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section);
} }
$this->ds->setSection($section, $newData); $this->ds->setSection($section, $this->onInsert($target, new ConfigObject($newData)));
try { try {
$this->ds->saveIni(); $this->ds->saveIni();
@ -98,6 +226,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
$query->addFilter($this->requireFilter($target, $filter)); $query->addFilter($this->requireFilter($target, $filter));
} }
/** @var ConfigObject $config */
$newSection = null; $newSection = null;
foreach ($query as $section => $config) { foreach ($query as $section => $config) {
if ($newSection !== null) { if ($newSection !== null) {
@ -107,25 +236,32 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
); );
} }
$newConfig = clone $config;
foreach ($newData as $column => $value) { foreach ($newData as $column => $value) {
if ($column === $keyColumn) { if ($column === $keyColumn) {
$newSection = $value; $newSection = $value;
} else { } else {
$config->$column = $value; $newConfig->$column = $value;
} }
} }
// This is necessary as the query result set contains the key column. // This is necessary as the query result set contains the key column.
unset($config->$keyColumn); unset($newConfig->$keyColumn);
if ($newSection) { if ($newSection) {
if ($this->ds->hasSection($newSection)) { if ($this->ds->hasSection($newSection)) {
throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection); throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection);
} }
$this->ds->removeSection($section)->setSection($newSection, $config); $this->ds->removeSection($section)->setSection(
$newSection,
$this->onUpdate($target, $config, $newConfig)
);
} else { } else {
$this->ds->setSection($section, $config); $this->ds->setSection(
$section,
$this->onUpdate($target, $config, $newConfig)
);
} }
} }
@ -151,8 +287,10 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
$query->addFilter($this->requireFilter($target, $filter)); $query->addFilter($this->requireFilter($target, $filter));
} }
/** @var ConfigObject $config */
foreach ($query as $section => $config) { foreach ($query as $section => $config) {
$this->ds->removeSection($section); $this->ds->removeSection($section);
$this->onDelete($target, $config);
} }
try { try {