Commit 65396881 authored by Ad Schellevis's avatar Ad Schellevis

(mvc) add version to model spec and add initial migration support

parent d46f43b0
......@@ -57,6 +57,17 @@ abstract class BaseModel
*/
private $internal_mountpoint = '';
/**
* this models version number, defaults to 0.0.0 (no version)
* @var string
*/
private $internal_model_version = "0.0.0";
/**
* model version in config.xml
* @var null
*/
private $internal_current_model_version = null;
/**
* If the model needs a custom initializer, override this init() method
......@@ -220,6 +231,10 @@ abstract class BaseModel
}
$this->internal_mountpoint = $model_xml->mount;
if (!empty($model_xml->version)) {
$this->internal_model_version = $model_xml->version;
}
// use an xpath expression to find the root of our model in the config.xml file
// if found, convert the data to a simple structure (or create an empty array)
$tmp_config_data = $internalConfigHandle->xpath($model_xml->mount);
......@@ -231,6 +246,13 @@ abstract class BaseModel
// We've loaded the model template, now let's parse it into this object
$this->parseXml($model_xml->items, $config_array, $this->internalData) ;
// root may contain a version, store if found
if (empty($config_array)) {
// new node, reset
$this->internal_current_model_version = "0.0.0";
} elseif (!empty($config_array->attributes()['version'])) {
$this->internal_current_model_version = (string)$config_array->attributes()['version'];
}
// trigger post loading event
$this->internalData->eventPostLoading();
......@@ -368,6 +390,10 @@ abstract class BaseModel
$xml = new \SimpleXMLElement($xml_root_node);
$this->internalData->addToXMLNode($xml->xpath($this->internal_mountpoint)[0]);
// add this model's version to the newly created xml structure
if (!empty($this->internal_current_model_version)) {
$xml->xpath($this->internal_mountpoint)[0]->addAttribute('version', $this->internal_current_model_version);
}
return $xml;
}
......@@ -482,4 +508,51 @@ abstract class BaseModel
return false;
}
}
/**
* Execute model version migrations
* Every model may contain a migrations directory containing BaseModelMigration descendants, which
* are executed in order of version number.
*
* The BaseModelMigration class should be named with the corresponding version
* prefixed with an M and . replaced by _ for example : M1_0_1 equals version 1.0.1
*
*/
public function runMigrations()
{
if (version_compare($this->internal_current_model_version, $this->internal_model_version, '<')) {
$logger = new Syslog("config", array('option' => LOG_PID, 'facility' => LOG_LOCAL4));
$class_info = new \ReflectionClass($this);
// fetch version migrations
$versions = array();
foreach (glob(dirname($class_info->getFileName())."/migrations/M*.php") as $filename) {
$version = str_replace('_', '.', explode('.', substr(basename($filename),1))[0]);
$versions[$version] = $filename;
}
uksort($versions, "version_compare");
foreach ($versions as $mig_version => $filename) {
if (version_compare($this->internal_current_model_version, $mig_version, '<') &&
version_compare($this->internal_model_version, $mig_version, '>=') ) {
// execute upgrade action
$tmp = explode('.', basename($filename))[0];
$mig_classname = "\\".$class_info->getNamespaceName()."\\migrations\\".$tmp;
$mig_class = new \ReflectionClass($mig_classname);
if ($mig_class->getParentClass()->name == 'OPNsense\Base\BaseModelMigration') {
$migobj = $mig_class->newInstance();
try {
$migobj->run($this);
} catch (\Exception $e) {
$logger->error("failed migrating from version " .
$this->internal_current_model_version .
" to " . $mig_version . " in ".
$class_info->getName() .
" [skipping step]"
);
}
$this->internal_current_model_version = $mig_version;
}
}
}
}
}
}
<?php
/**
* Copyright (C) 2016 Deciso B.V.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\Base;
use OPNsense\Core\Config;
use Phalcon\Logger\Adapter\Syslog;
/**
* @package OPNsense\Base
*/
abstract class BaseModelMigration
{
public function run($model) {}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment