/** * Process models * * @param array $options * @return array */ public static function process_models($options = []) { $result = ['success' => false, 'error' => [], 'hint' => [], 'data' => []]; do { // we need to process all dependencies first $dep = self::process_deps_all($options); if (!$dep['success']) { $result = $dep; $result['error'][] = 'You must fix all dependency related errors first before processing models.'; break; } // proccesing models if (empty($dep['data']['model_processed'])) { $result['error'][] = 'You do not have models to process!'; break; } $object_attributes = []; $object_relations = []; $object_forms = []; $flag_relation = application::get('dep.submodule.numbers.data.relations') ? true : false; $object_documentation = []; $object_import = []; $ddl = new numbers_backend_db_class_ddl(); // run 1 to deterine virtual tables $first = true; $virtual_models = $dep['data']['model_processed']; run_again: foreach ($virtual_models as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); foreach (['attributes', 'audit', 'addresses'] as $v0) { if ($model->{$v0}) { $v01 = $v0 . '_model'; $virtual_models[str_replace('_', '.', $model->{$v01})] = 'object_table'; } } } } if ($first) { $first = false; goto run_again; // some widgets have attributes } $dep['data']['model_processed'] = array_merge_hard($dep['data']['model_processed'], $virtual_models); $domains = object_data_domains::get_static(); // run 2 foreach ($dep['data']['model_processed'] as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); $temp_result = $ddl->process_table_model($model); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; // relation if ($flag_relation) { if (!empty($model->relation)) { $domain = $model->columns[$model->relation['field']]['domain'] ?? null; if (!empty($domain)) { $domain = str_replace('_sequence', '', $domain); $type = $domains[$domain]['type']; } else { $type = $model->columns[$model->relation['field']]['type']; } $object_relations[$k2] = ['rn_relattr_code' => $model->relation['field'], 'rn_relattr_name' => $model->title, 'rn_relattr_model' => $k2, 'rn_relattr_domain' => $domain, 'rn_relattr_type' => $type, 'rn_relattr_inactive' => !empty($model->relation['inactive']) ? 1 : 0]; } if (!empty($model->attributes)) { $object_attributes[$k2] = ['rn_attrmdl_code' => $k2, 'rn_attrmdl_name' => $model->title, 'rn_attrmdl_inactive' => 0]; } } } else { if ($v == 'object_sequence') { $temp_result = $ddl->process_sequence_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_function') { $temp_result = $ddl->process_function_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_extension') { $temp_result = $ddl->process_function_extension($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_import') { $object_import[$k2] = ['model' => $k2]; } } } } } } // if we have erros if (!empty($result['error'])) { break; } // db factory $db_factory = factory::get('db'); // we load objects from database $loaded_objects = []; foreach ($ddl->db_links as $k => $v) { $ddl_object = $db_factory[$k]['ddl_object']; $temp_result = $ddl_object->load_schema($k); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } else { $loaded_objects[$k] = $temp_result['data']; } } // if we have erros if (!empty($result['error'])) { break; } // get a list of all db links $db_link_list = array_unique(array_merge(array_keys($ddl->objects), array_keys($loaded_objects))); // if we are dropping schema if ($options['mode'] == 'drop') { $ddl->objects = []; } // compare schemas per db link $schema_diff = []; $total_per_db_link = []; $total = 0; foreach ($db_link_list as $k) { // we need to have a back end for comparison $compare_options['backend'] = $db_factory[$k]['backend']; // comparing $temp_result = $ddl->compare_schemas(isset($ddl->objects[$k]) ? $ddl->objects[$k] : [], isset($loaded_objects[$k]) ? $loaded_objects[$k] : [], $compare_options); if (!$temp_result['success']) { array_merge3($result['hint'], $temp_result['error']); } else { $schema_diff[$k] = $temp_result['data']; if (!isset($total_per_db_link[$k])) { $total_per_db_link[$k] = 0; } $total_per_db_link[$k] += $temp_result['count']; $total += $temp_result['count']; } } // if there's no schema changes if ($total == 0) { if ($options['mode'] == 'commit') { goto import_data; } else { $result['success'] = true; } break; } // we need to provide a list of changes foreach ($total_per_db_link as $k => $v) { $result['hint'][] = ''; $result['hint'][] = "Db link {$k} requires {$v} changes!"; // printing summary $result['hint'][] = ' * Link ' . $k . ': '; foreach ($schema_diff[$k] as $k2 => $v2) { $result['hint'][] = ' * ' . $k2 . ': '; foreach ($v2 as $k3 => $v3) { $result['hint'][] = ' * ' . $k3 . ' - ' . $v3['type']; } } } // if we are in no commit mode we exit if (!in_array($options['mode'], ['commit', 'drop'])) { break; } // generating sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $ddl_object = $db_factory[$k]['ddl_object']; foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { // we need to make fk constraints last to sort MySQL issues if ($k2 == 'new_constraints' && $v3['type'] == 'constraint_new' && $v3['data']['type'] == 'fk') { $schema_diff[$k][$k2 . '_fks'][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3); } else { $schema_diff[$k][$k2][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3, ['mode' => $options['mode']]); } } } } // print_r($schema_diff); // exit; // executing sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $db_object = new db($k); // if we are dropping we need to disable foregn key checks if ($options['mode'] == 'drop') { if ($db_object->backend == 'mysqli') { $db_object->query('SET foreign_key_checks = 0;'); // we also need to unset sequences unset($schema_diff[$k]['delete_sequences']); } } foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($v3['sql'])) { continue; } if (is_array($v3['sql'])) { $temp = $v3['sql']; } else { $temp = [$v3['sql']]; } foreach ($temp as $v4) { $temp_result = $db_object->query($v4); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); goto error; } } } } } // if we got here - we are ok $result['success'] = true; } while (0); import_data: // we need to import data if (!empty($object_import) && $options['mode'] == 'commit') { $result['hint'][] = ''; foreach ($object_import as $k => $v) { $data_object = new $k(); $data_result = $data_object->process(); if (!$data_result['success']) { throw new Exception(implode("\n", $data_result['error'])); } $result['hint'] = array_merge($result['hint'], $data_result['hint']); } } // relation if ($flag_relation && $options['mode'] == 'commit') { $result['hint'][] = ''; $model2 = factory::model('numbers_data_relations_model_relation_attributes'); // insert new models if (!empty($object_relations)) { foreach ($object_relations as $k => $v) { $result_insert = $model2->save($v, ['pk' => ['rn_relattr_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation models!'; } // we need to process forms foreach ($dep['data']['submodule_dirs'] as $v) { $dir = $v . 'model/form/'; if (!file_exists($dir)) { continue; } $files = helper_file::iterate($dir, ['only_extensions' => ['php']]); foreach ($files as $v2) { $model_name = str_replace(['../libraries/vendor/', '.php'], '', $v2); $model_name = str_replace('/', '_', $model_name); $model = new $model_name(['skip_processing' => true]); if (empty($model->form_object->misc_settings['option_models'])) { continue; } // loop though fields foreach ($model->form_object->misc_settings['option_models'] as $k3 => $v3) { $object_forms[$model_name . '::' . $k3] = ['rn_relfrmfld_form_code' => $model_name, 'rn_relfrmfld_form_name' => $model->title, 'rn_relfrmfld_field_code' => $k3, 'rn_relfrmfld_field_name' => $v3['field_name'], 'rn_relfrmfld_relattr_id' => $v3['model'], 'rn_relfrmfld_inactive' => 0]; } } } if (!empty($object_forms)) { // load all relation models $data = $model2->get(['pk' => ['rn_relattr_model']]); $model = factory::model('numbers_data_relations_model_relation_formfields'); foreach ($object_forms as $k => $v) { if (empty($data[$v['rn_relfrmfld_relattr_id']])) { continue; } $v['rn_relfrmfld_relattr_id'] = $data[$v['rn_relfrmfld_relattr_id']]['rn_relattr_id']; $result_insert = $model->save($v, ['pk' => ['rn_relfrmfld_form_code', 'rn_relfrmfld_field_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation form fields!'; } // todo: import models //print_r2($object_attributes); if (!empty($object_attributes)) { $model = factory::model('numbers_data_relations_model_attribute_models'); foreach ($object_attributes as $k => $v) { $result_insert = $model->save($v, ['pk' => ['rn_attrmdl_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported attribute models!'; } } // we need to generate documentation $system_documentation = application::get('system_documentation'); if (!empty($system_documentation) && $options['mode'] == 'commit') { $model = factory::model($system_documentation['model']); /* print_r2($object_documentation); $documentation_result = $model->update($object_documentation, $system_documentation); if (!$documentation_result['success']) { $result['error'] = array_merge($result['error'], $documentation_result['error']); } */ } error: return $result; }