/** * Take the module creator form input and apply changes * * @param array $input * @param array $changeGroups * * @todo Break this up into smaller parts */ public function applyModuleChanges(array $input = array(), array $changeGroups = array()) { // Initialize needed variables $tableName = $input['table_name']; // Module table name $moduleId = $input['id']; // Module ID $moduleChangeset = ArrayLib::getKey(array_shift($changeGroups), 0); // Get the module changeset // Initialize the generator map array $generatorMap = array('created_fields' => array(), 'deleted_fields' => array(), 'updated_fields' => array()); // Define artisan generator methods to be used with generator map keys $generatorMethods = array('created_fields' => 'add', 'deleted_fields' => 'remove'); // If there's no module id, spawn a migration for creation of a new module if (!$moduleId) { MigrationGenerator::create($tableName); sleep(1); // Pause a second so succeeding migrations are named (ordered) properly } // Gather fields foreach ($changeGroups as $groupType => $changesets) { // Skip if we're dealing with updates if ($groupType === 'updated_fields') { continue; } foreach ($changesets as $changeset) { // Skip if changeset is empty if (!is_array($changeset['changes']) || empty($changeset['changes'])) { continue; } // Retrieve the column type of a current changeset $columnType = ArrayLib::getKey(ArrayLib::findByKeyValue($changeset['changes'], 'name', 'column_type') ?: array(), 'new'); // Retrieve the column name of a current changeset $columnName = ArrayLib::getKey(ArrayLib::findByKeyValue($changeset['changes'], 'name', 'column_name') ?: array(), 'new'); $generatorMap[$groupType][$columnName] = $columnType; } } // Generate migrations for new fields foreach ($generatorMap as $key => $fields) { // Check if a migration should be generated if (array_key_exists($key, $generatorMethods) && !empty($fields)) { // Bam! MigrationGenerator::$generatorMethods[$key]($tableName, array_filter($fields, function ($value) { return !empty($value); })); } } // Update model if (!empty($moduleChangeset['changes'])) { // Map of field => value entries $changemap = []; // Fill the changemap foreach ($moduleChangeset['changes'] as $change) { $changemap[$change['name']] = $change['new']; } // If module already exists, update it with new entries if ($moduleChangeset['item_id']) { \DB::table(\Orangehill\Photon\Module::getTableName())->where('table_name', $tableName)->update($changemap); } else { // Module doesn't exist yet, it should be created // Create a new module based on an form input entry $newModule = new Module($changemap); // Generate a model file $modelName = ucfirst(camel_case(str_singular($tableName))); \Artisan::call('generate:model', array('modelName' => $modelName)); // Store the path to the model file $pathToModel = app_path('models') . '/' . $modelName . '.php'; // Open the newly created file and make the model extend Baum's Node instead of Eloquent $fileContent = file_get_contents($pathToModel); $replaced = str_replace('extends \\Eloquent', 'extends Baum\\Node', $fileContent); // Try to find a name of first added column, fallback to the id $toStringField = 'id'; if (is_array($changeGroups['created_fields'])) { $head = head($changeGroups['created_fields']); if (is_array($head['changes'])) { foreach ($head['changes'] as $fieldSegment) { if ($fieldSegment['name'] == 'column_name') { $toStringField = $fieldSegment['new']; } } } } // Create a __toString method stub $toStringStub = StubFactory::make('ModelToStringMethod', array('field' => $toStringField))->append("\n}")->get(); // Add the method to the model files $replaced = substr_replace($replaced, $toStringStub, strrpos($replaced, '}')); file_put_contents($pathToModel, $replaced); // Save a model and update the module id $newModule->save(); $moduleId = $newModule->id; } } // Add newborn fields foreach ($changeGroups['created_fields'] as $field) { $fieldData = array('module_id' => $moduleId); foreach ($field['changes'] as $element) { $fieldData[$element['name']] = $element['new']; } $field = FieldCreator::make($fieldData); $field->save(); } // Update fields foreach ($changeGroups['updated_fields'] as $field) { $fieldData = array(); foreach ($field['changes'] as $element) { $fieldData[$element['name']] = $element['new']; } \DB::table(Field::getTableName())->where('id', $field['item_id'])->update($fieldData); } // Delete fields $deletedFieldIds = array_fetch($changeGroups['deleted_fields'], 'item_id') + array(0); $moduleFields = Field::whereIn('id', $deletedFieldIds)->get(); foreach ($moduleFields as $moduleField) { FieldFactory::make($moduleField)->uninstall(); } foreach ($changeGroups['deleted_fields'] as $field) { \DB::table(Field::getTableName())->where('id', $field['item_id'])->delete(); } }
/** * @expectedException Orangehill\Photon\MigrationException */ public function testArgumentPreparationWithInvalidCommandName() { MigrationGenerator::prepareArguments('create', array()); }
/** * Saves submitted and validated data * * @param $verbose boolean $verbose If set to true function will only specify pending changes without making any * * @return array */ protected function save($fieldIds, $verbose = false) { $report = ''; // Instantiate Migration Generator Class (Laravel 4 Generator Module Wrapper) $generator = new MigrationGenerator(); // Check if module_name is new or existing one if (is_numeric(\Input::get('module_id'))) { // Get module data $module = Module::find(\Input::get('module_id')); // Handle Folder module type if (\Input::get('is_folder') == '1') { return $this->updateFolderModuleType($module, $verbose); } // Get id's of fields that are stored in the database $existingFieldIds = array_fetch($module->fields()->get()->toArray(), 'id'); // Check if remove module request is received if (\Input::get('remove_request') == 1) { $report .= $this->reportSectionTitleGenerator('remove', 'module', \Input::get('name')); // If in verbose mode return report array if ($verbose) { return array('report' => $report); } // Populate migrationFieldsDropped array (needed for php artisan migrate:rollback) $migrationFieldsDropped = array(); $existingFieldIds = $module->fields()->get(); foreach ($existingFieldIds as $field) { $migrationFieldsDropped[$field->column_name] = $field->column_type; } // Generate migration file $generator->destroy($module->table_name, $migrationFieldsDropped); // Run the migrations if (\Input::get('run-migrations') == 1) { \Artisan::call('migrate'); } // Remove the module (also removes orphans from fields table) $module->delete(); // Notify about successful data removal \Session::flash('success', 'Data has been removed.'); // Redirect to settings page return \Redirect::to('/admin/settings/'); } // Create an array of deleted fields, if any $deletedFieldIds = array_diff($existingFieldIds, $fieldIds); // Create an array of new fields, if any $newFieldIds = array_diff($fieldIds, $existingFieldIds); // Create an array of edited fields $editedFieldIds = array_diff($fieldIds, $deletedFieldIds, $newFieldIds); if (!$module->get()->isEmpty()) { $moduleHasChanged = false; // Updating existing module $report .= $this->reportSectionTitleGenerator('change', 'module', \Input::get('name')); if ($module->name != \Input::get('module_name')) { $report .= $this->reportGenerator('set', 'name', \Input::get('name'), $module->name); $module->name = \Input::get('name'); $moduleHasChanged = true; } if ($module->parent_module != \Input::get('parent_module')) { $report .= $this->reportGenerator('set', 'parent module', \Input::get('parent_module'), $module->parent_module); $module->parent_module = \Input::get('parent_module'); $moduleHasChanged = true; } if ($module->nestable != \Input::get('nestable')) { $report .= $this->reportGenerator('set', 'nestable', \Input::get('nestable'), $module->nestable); $module->nestable = \Input::get('nestable'); $moduleHasChanged = true; } $report .= $moduleHasChanged ? '' : '<br/>'; // Save changes to module table if (!$verbose) { $module->save(); } // Process new fields for an existing module if (count($newFieldIds) > 0) { $migrationFields = array(); foreach ($newFieldIds as $newFieldId) { // Generate a report line $report .= $this->insertFieldReport($newFieldId); // Insert a new record in fields table if (!$verbose) { $tmp = array(); $tmp['module_id'] = \Input::get('module_id'); $tmp['field_name'] = \Input::get('field_name' . $newFieldId); $tmp['field_type'] = \Input::get('field_type' . $newFieldId); $tmp['relation_table'] = \Input::get('relation_table' . $newFieldId); $tmp['column_name'] = \Input::get('column_name' . $newFieldId); $tmp['column_type'] = \Input::get('column_type' . $newFieldId); $tmp['tooltip_text'] = \Input::get('tooltip_text' . $newFieldId); $newField = Field::create($tmp); // Populate migrationFields array $migrationFields[\Input::get('column_name' . $newFieldId)] = \Input::get('column_type' . $newFieldId); } $moduleHasChanged = true; } // Generate migration files if (!$verbose) { $generator->add($module->table_name, $migrationFields); } } // Process edited fields for an existing module if (count($editedFieldIds) > 0) { $migrationFields = array(); $migrationFieldsDropped = array(); foreach ($editedFieldIds as $fieldId) { $fieldHasChanged = false; $fieldReport = ''; $field = $module->fields()->find($fieldId); // Populate migrationFields and migrationFieldsDropped arrays if ($field->column_name != \Input::get('column_name' . $fieldId) or $field->column_type != \Input::get('column_type' . $fieldId)) { $migrationFields[\Input::get('column_name' . $fieldId)] = \Input::get('column_type' . $fieldId); $migrationFieldsDropped[$field->column_name] = $field->column_type; } if ($field->field_name != \Input::get('field_name' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'field name', \Input::get('field_name' . $fieldId), $field->field_name); $field->field_name = \Input::get('field_name' . $fieldId); $fieldHasChanged = true; } if ($field->field_type != \Input::get('field_type' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'field type', \Input::get('field_type' . $fieldId), $field->field_type); $field->field_type = \Input::get('field_type' . $fieldId); $fieldHasChanged = true; } if ($field->relation_table != \Input::get('relation_table' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'relation table', \Input::get('relation_table' . $fieldId), $field->relation_table); $field->relation_table = \Input::get('relation_table' . $fieldId); $fieldHasChanged = true; } if ($field->column_name != \Input::get('column_name' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'column name', \Input::get('column_name' . $fieldId), $field->column_name); $field->column_name = \Input::get('column_name' . $fieldId); $fieldHasChanged = true; } if ($field->column_type != \Input::get('column_type' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'column type', \Input::get('column_type' . $fieldId), $field->column_type); $field->column_type = \Input::get('column_type' . $fieldId); $fieldHasChanged = true; } if ($field->tooltip_text != \Input::get('tooltip_text' . $fieldId)) { $fieldReport .= $this->reportGenerator('set', 'tooltip text', \Input::get('tooltip_text' . $fieldId), $field->tooltip_text); $field->tooltip_text = \Input::get('tooltip_text' . $fieldId); $fieldHasChanged = true; } if ($fieldHasChanged) { $fieldReport = $this->reportSectionTitleGenerator('change', 'field', $field->field_name) . $fieldReport; $fieldReport .= '<br/>'; $report .= $fieldReport; $moduleHasChanged = true; } if (!$verbose and $fieldHasChanged) { $field->save(); } } // Generate migration files if (!$verbose and $fieldHasChanged) { // First drop fields with old column_type $generator->remove($module->table_name, $migrationFieldsDropped); // Wait 1 second so that remove migration is executed before add (filenaming issue) sleep(1); // Than add fields with changed column_type $generator->add($module->table_name, $migrationFields); } } // Process deleted fields if (count($deletedFieldIds) > 0) { $migrationFieldsDropped = array(); foreach ($deletedFieldIds as $deletedFieldId) { $field = $module->fields()->find($deletedFieldId); $report .= $this->reportSectionTitleGenerator('remove', 'field', $field->field_name); // Populate migrationFieldsDropped array $migrationFieldsDropped[$field->column_name] = $field->column_type; if (!$verbose) { $field->delete(); } $moduleHasChanged = true; } // Generate migration files if (!$verbose) { $generator->remove($module->table_name, $migrationFieldsDropped); } } // This will yield 'no changes detected' message if (!$moduleHasChanged) { return false; } // If in verbose mode return report array if ($verbose) { return array('report' => $report); } // Run the migrations if (\Input::get('run-migrations') == 1) { \Artisan::call('migrate'); } // Notify about successful data entry \Session::flash('success', 'Data has been saved.'); // Redirect to module settings page return \Redirect::to('/admin/settings/' . \Input::get('module_id')); } } // Inserting a new model $report .= $this->reportSectionTitleGenerator('create', 'module'); $report .= $this->reportGenerator('set', 'module name', \Input::get('name')); $report .= $this->reportGenerator('set', 'table name', \Input::get('table_name')); $report .= $this->reportGenerator('set', 'parent module', \Input::get('parent_module')); $report .= $this->reportGenerator('set', 'nestable', \Input::get('nestable')); $report .= '<br/>'; // Insert a new record in modules table, and get it's ID if (!$verbose) { $newModule = Module::create(\Input::all()); } $migrationFields = array(); // Inserting new fields foreach ($fieldIds as $fieldId) { // Generate a report line $report .= $this->insertFieldReport($fieldId); // Insert a new record in fields table if (!$verbose) { $tmp = array(); $tmp['module_id'] = $newModule->id; $tmp['field_name'] = \Input::get('field_name' . $fieldId); $tmp['field_type'] = \Input::get('field_type' . $fieldId); $tmp['relation_table'] = \Input::get('relation_table' . $fieldId); $tmp['column_name'] = \Input::get('column_name' . $fieldId); $tmp['column_type'] = \Input::get('column_type' . $fieldId); $tmp['tooltip_text'] = \Input::get('tooltip_text' . $fieldId); $newField = Field::create($tmp); // Populate migrationFields array $migrationFields[\Input::get('column_name' . $fieldId)] = \Input::get('column_type' . $fieldId); } } // If in verbose mode return report array if ($verbose) { return array('report' => $report); } // Generate migration files (Only if module type is not folder) if (\Input::get('is_folder') !== '1') { $generator->create(\Input::get('table_name'), $migrationFields); } // Run the migrations (Only if module type is not folder) if (\Input::get('run-migrations') == 1 && \Input::get('is_folder') !== '1') { \Artisan::call('migrate'); } // Notify about successful data entry \Session::flash('success', 'Data has been saved.'); // Redirect to new module settings page return \Redirect::to('/admin/settings/' . $newModule->id); }