Есть ли способ объединить архивируемое и версионное поведение в Propel 1?

Я использую Propel 1 в довольно крупном проекте, и в настоящее время живая версия использует Archivable поведение. Таким образом, когда строка удаляется, поведение прозрачно перехватывает вызов и перемещает строку в архивную таблицу. Это отлично работает.

Я пытаюсь изменить работу этой таблицы, чтобы все сохранения были версионными. Поэтому в ветви функций я удалил Archivable и добавил Versionable поведение. Это падает (table)_archive автоматически сгенерированная таблица и добавляет (table)_version стол вместо.

Однако, что интересно, таблица версий имеет PK (id, version) с внешним ключом к живому столу из id в id, Это означает, что версии не могут существовать без активной строки, а это не то, чего я хочу: я хочу иметь возможность удалить строку и сохранить версии.

Я думал, что это поведение будет вести себя как Archivable то есть delete() метод будет перехвачен и изменен по сравнению с обычным подходом. К сожалению, что подтверждается документация, этот метод удаляет живую строку а также любые предыдущие версии:

void delete(): Удаляет историю версий объекта

Я пытался смешать оба Archivable а также Versionable, но это, кажется, генерирует код, который падает в Query API: он пытается вызвать archive() метод, который не существует. Я ожидаю, что это сочетание поведения никогда не предназначалось для работы (в идеале оно должно быть перехвачено во время сборки схемы, и, возможно, это будет исправлено в Propel 2).

Одним из решений является попытка SoftDelete поведение вместо Archivable — это просто помечает записи как удаленные, а не перемещает их в другую таблицу. Однако это может быть проблематично, потому что присоединение к таблице с таким поведением может дать неправильный счет для не удаленных строк (и команда Propel решила отказаться от него по этой причине). Это также похоже на кроличью нору, которую я не хочу закрывать, так как количество рефакторинга может выйти из-под контроля.

Таким образом, мне остается искать лучший подход для реализации системы управления версиями, которая не удаляет старые версии при удалении действующей копии. Я могу сделать это вручную, перехватывая методы сохранения и удаления в классе модели, но это кажется пустой тратой, когда Versionable почти делает то, что я хочу. Существуют ли соответствующие параметры, которые я могу настроить, или есть смысл в написании собственного поведения? Беглый взгляд на код генерации шаблонов для поведения ядра заставляет меня убегать от последнего!

3

Решение

Вот решение, которое я придумал. Моя память довольно туманна, но похоже, что я взял VersionableBehaviour и вывел из него новое поведение, которое я назвал HistoryVersionableBehaviour, Таким образом, он использует все особенности поведения ядра, а затем просто переопределяет сгенерированное удаление своим собственным кодом.

Вот само поведение:

<?php

// This is how the versionable behaviour works
require_once dirname(__FILE__) . '/HistoryVersionableBehaviorObjectBuilderModifier.php';

class HistoryVersionableBehavior extends VersionableBehavior
{
/**
* Reset the FKs from CASCADE ON DELETE to no action
*
* (I expect all future migration diffs will incorrectly try to re-add the constraint
* I manually removed from the migration that introduced versioning, may try to fix
* that another time. 'Tis fine for now).
*/
public function addVersionTable()
{
parent::addVersionTable();

$this->swapAllForeignKeysToNoDeleteAction();
$this->addVersionArchivedColumn();
}

protected function swapAllForeignKeysToNoDeleteAction()
{
$versionTable = $this->lookupVersionTable();
$fks = $versionTable->getForeignKeys();
foreach ($fks as $fk)
{
$fk->setOnDelete(null);
}
}

protected function addVersionArchivedColumn()
{
$versionTable = $this->lookupVersionTable();
$versionTable->addColumn(array(
'name' => 'archived_at',
'type' => 'timestamp',
));
}

protected function lookupVersionTable()
{
$table = $this->getTable();
$versionTableName = $this->getParameter('version_table') ?
$this->getParameter('version_table') :
($table->getName() . '_version');
$database = $table->getDatabase();

return $database->getTable($versionTableName);
}

/**
* Point to the custom object builder class
*
* @return HistoryVersionableBehaviorObjectBuilderModifier
*/
public function getObjectBuilderModifier()
{
if (is_null($this->objectBuilderModifier)) {
$this->objectBuilderModifier = new HistoryVersionableBehaviorObjectBuilderModifier($this);
}

return $this->objectBuilderModifier;
}
}

Для этого требуется нечто под названием модификатор, который запускается во время генерации для создания базовых классов экземпляров:

<?php

class HistoryVersionableBehaviorObjectBuilderModifier extends \VersionableBehaviorObjectBuilderModifier
{
/**
* Don't do any version deletion after the main deletion
*
* @param \PHP5ObjectBuilder $builder
*/
public function postDelete(\PHP5ObjectBuilder $builder)
{
$this->builder = $builder;
$script = "// Look up the latest version
\$latestVersion = {$this->getVersionQueryClassName()}::create()->
filterBy{$this->table->getPhpName()}(\$this)->
orderByVersion(\Criteria::DESC)->
findOne(\$con);
\$latestVersion->
setArchivedAt(time())->
save(\$con);
";

return $script;
}
}

Родительский класс имеет 798 строк, поэтому мой подход, похоже, сохранил много кода, а не создавал его с нуля!

Вам нужно будет указать поведение в вашем XML-файле для каждой таблицы, для которой вы хотите его активировать:

<table name="job">
<!--- your columns... -->
<behavior name="timestampable" />
<behavior name="history_versionable" />
</table>

Я не уверен, требует ли мое поведение присутствия timestampable поведение — мое предположение нет, так как похоже, что родительское поведение просто добавляет столбцы в версионную таблицу, а не в саму таблицу. Если вы можете попробовать это без timestampable поведение, дайте мне знать, как вы поживаете, так что я могу обновить этот пост.

Наконец, вам нужно указать местоположение вашего класса, чтобы пользовательский автозагрузчик Propel 1 знал, где его найти. Я использую это в моем build.properties:

# Declare a custom behaviour
propel.behavior.history_versionable.class = ${propel.php.dir}.WebScraper.Behaviours.HistoryVersionable.HistoryVersionableBehavior
1

Другие решения

Других решений пока нет …