runWithRule($rule); } protected function extractVariableNames($string) { if (preg_match_all('/\${([A-Za-z0-9_-]+)}/', $string, $m, PREG_PATTERN_ORDER)) { return $m[1]; } else { return array(); } } protected function fillVariables($string, $row) { $func = function ($match) use ($row) { return $row->{$match[1]}; }; return preg_replace_callback('/\${([A-Za-z0-9_-]+)}/', $func, $string); } protected function runWithRule(SyncRule $rule) { $db = $rule->getConnection(); $properties = $rule->fetchSyncProperties(); $sourceColumns = array(); $sources = array(); // $fieldMap = array(); foreach ($properties as $p) { $sourceId = $p->source_id; if (! array_key_exists($sourceId, $sources)) { $sources[$sourceId] = ImportSource::load($sourceId, $db); $sourceColumns[$sourceId] = array(); } foreach ($this->extractVariableNames($p->source_expression) as $varname) { $sourceColumns[$sourceId][$varname] = $varname; // -> ? $fieldMap[ } } $imported = array(); foreach ($sources as $source) { $sourceId = $source->id; $key = $source->key_column; $sourceColumns[$sourceId][$key] = $key; $rows = $db->fetchLatestImportedRows($sourceId, $sourceColumns[$sourceId]); $imported[$sourceId] = array(); foreach ($rows as $row) { if (! property_exists($row, $key)) { throw new \Exception( sprintf( 'There is no key column "%s" in this row from "%s": %s', $key, $source->source_name, json_encode($row) ) ); } $imported[$sourceId][$row->$key] = $row; } } // TODO: Filter auf object, nicht template $objects = IcingaObject::loadAllByType($rule->object_type, $db); $dummy = IcingaObject::createByType($rule->object_type, array()); $objectKey = $rule->object_type === 'datalistEntry' ? 'entry_name' : 'object_name'; foreach ($sources as $source) { $sourceId = $source->id; foreach ($imported[$sourceId] as $key => $row) { $newProps = array( 'object_type' => 'object', 'object_name' => $key ); if ($rule->object_type === 'datalistEntry') { $newProps = array(); } $newVars = array(); $imports = array(); foreach ($properties as $p) { if ($p->source_id !== $sourceId) continue; $prop = $p->destination_field; $val = $this->fillVariables($p->source_expression, $row); if (substr($prop, 0, 5) === 'vars.') { $newVars[substr($prop, 5)] = $val; } else { if ($prop === 'import') { $imports[] = $val; } else { $newProps[$prop] = $val; } } } if (array_key_exists($key, $objects)) { switch ($rule->update_policy) { case 'override': $objects[$key] = IcingaObject::createByType( $rule->object_type, $newProps, $db ); foreach ($newVars as $prop => $var) { $objects[$key]->vars()->$prop = $var; } if (! empty($imports)) { $objects[$key]->imports()->set($imports); } break; case 'merge': $object = $objects[$key]; foreach ($newProps as $prop => $value) { // TODO: data type? $object->set($prop, $value); } foreach ($newVars as $prop => $var) { // TODO: property merge policy $object->vars()->$prop = $var; } if (! empty($imports)) { // TODO: merge imports ?! $objects[$key]->imports()->set($imports); } break; default: // policy 'ignore', no action } } else { $objects[$key] = IcingaObject::createByType($rule->object_type, $newProps, $db); foreach ($newVars as $prop => $var) { $objects[$key]->vars()->$prop = $var; } if (! empty($imports)) { $objects[$key]->imports()->set($imports); } } } } $dba = $db->getDbAdapter(); $dba->beginTransaction(); foreach ($objects as $object) { if ($object instanceof IcingaObject && $object->isTemplate()) { if ($object->hasBeenModified()) { throw new \Exception( sprintf( 'Sync is not allowed to modify template "%s"', $object->$objectKey ) ); } continue; } if ($object->hasBeenLoadedFromDb() && $rule->purge_existing === 'y') { $found = false; foreach ($sources as $source) { if (array_key_exists($object->$objectKey, $imported[$source->id])) { $found = true; break; } } if (! $found) { $object->delete(); } } if (! $object->$objectKey) { continue; } $object->store($db); } $dba->commit(); return 42; // We have no sync_run history table yet } }