public function testWrapFromPath() { $file = dirname(dirname(dirname(__DIR__))) . DS . 'data' . DS . 'CsvMigrations' . DS . 'migrations' . DS . 'Foo' . DS . 'migration.csv'; $parser = new MigrationParser(); $result = $parser->wrapFromPath($file); $this->assertTrue(is_array($result), "Parser returned a non-array"); $this->assertFalse(empty($result), "Parser returned empty result"); }
/** * Apply changes from the CSV file * * @param string $path Path to the CSV file * @return void */ protected function _handleCsv($path = '') { $tableName = Inflector::pluralize(Inflector::classify($this->_table->getName())); if ('' === trim($path)) { $pathFinder = new MigrationPathFinder(); $path = $pathFinder->find($tableName); } $parser = new MigrationParser(); $csvData = $parser->wrapFromPath($path); $csvData = array_merge($csvData, $this->_requiredFields); $tableFields = $this->_getTableFields(); if (empty($tableFields)) { $this->_createFromCsv($csvData); } else { $this->_updateFromCsv($csvData, $tableFields); } }
/** * Check migration.csv fields * * @param array $modules List of modules to check * @return int Count of errors found */ protected function _checkMigrationFields(array $modules = []) { $errors = []; $warnings = []; $this->out('Checking migration fields:', 2); foreach ($modules as $module => $path) { $moduleErrors = []; $this->out(' - ' . $module . ' ... ', 0); $fields = null; try { $pathFinder = new MigrationPathFinder(); $path = $pathFinder->find($module); $parser = new MigrationParser(); $fields = $parser->parseFromPath($path); } catch (\Exception $e) { // We've already reported this problem in _checkMigrationPresence(); } if ($fields) { $seenFields = []; // Check each field one by one foreach ($fields as $field) { // Field name is required if (empty($field['name'])) { $moduleErrors[] = $module . " migration has a field without a name"; } else { // Check for field duplicates if (in_array($field['name'], $seenFields)) { $moduleErrors[] = $module . " migration specifies field '" . $field['name'] . "' more than once"; } else { $seenFields[] = $field['name']; } // Field type is required if (empty($field['type'])) { $moduleErrors[] = $module . " migration does not specify type for field '" . $field['name'] . "'"; } else { $type = null; $limit = null; // Matches: // * date, time, string, and other simple types // * list(something), related(Others) and other simple limits // * related(Vendor/Plugin.Model) and other complex limits if (preg_match('/^(\\w+?)\\(([\\w\\/\\.]+?)\\)$/', $field['type'], $matches)) { $type = $matches[1]; $limit = $matches[2]; } else { $type = $field['type']; } // Field type must be valid if (!$this->_isValidFieldType($type)) { $moduleErrors[] = $module . " migration specifies invalid type '" . $type . "' for field '" . $field['name'] . "'"; } else { switch ($type) { case 'related': // Only check for simple modules, not the vendor/plugin ones if (preg_match('/^\\w+$/', $limit) && !$this->_isValidModule($limit, array_keys($modules))) { $moduleErrors[] = $module . " migration relates to unknown module '{$limit}' in '" . $field['name'] . "' field"; } // Documents module can be used as `files(Documents)` for a container of the uploaded files, // or as `related(Documents)` as a regular module relationship. It's often easy to overlook // which one was desired. Failing on either one is incorrect, as both are valid. A // warning is needed instead for the `related(Documents)` case instead. // The only known legitimate case is in the Files, which is join table between Documents and FileStorage. if ('Documents' == $limit && 'Files' != $module) { $warnings[] = $module . " migration uses 'related' type for 'Documents' in '" . $field['name'] . "'. Maybe wanted 'files(Documents)'?"; } break; case 'list': case 'money': case 'metric': if (!$this->_isValidList($limit)) { $moduleErrors[] = $module . " migration uses unknown or empty list '{$limit}' in '" . $field['name'] . "' field"; } break; } } } } } // Check for the required fields // TODO: Allow specifying the required fields as the command line argument (for things like trashed) $requiredFields = ['id', 'created', 'modified']; foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $seenFields)) { $moduleErrors[] = $module . " migration is missing a required field '{$requiredField}'"; } } } $result = empty($moduleErrors) ? '<success>OK</success>' : '<error>FAIL</error>'; $this->out($result); $errors = array_merge($errors, $moduleErrors); } $this->_printCheckStatus($errors, $warnings); return count($errors); }
/** * Get all modules data. * * @return array Modules, fields and fields types. */ protected function _csvData() { $result = []; $path = Configure::readOrFail('CsvMigrations.migrations.path'); $csvFiles = $this->_getCsvFiles($path); /* covers case where CsvMigration configuration files reside in a plugin. */ $plugin = $this->_getPluginNameFromPath($path); if (is_null($plugin)) { /* covers case where CsvMigration model and controller reside in a plugin (even if configuration files reside in the APP level). */ $plugin = $this->_getPluginNameFromRegistryAlias(); } $parser = new MigrationParser(); foreach ($csvFiles as $csvModule => $paths) { if (!is_null($plugin)) { $csvModule = $plugin . '.' . $csvModule; } foreach ($paths as $path) { $result[$csvModule] = $parser->wrapFromPath($path); } } return $result; }
/** * Method that retrieves and returns csv migration fields. * * @param Request $request Request object * @return array */ protected function _getMigrationFields(Request $request) { $result = []; try { $pathFinder = new MigrationPathFinder(); $path = $pathFinder->find($request->controller); $parser = new MigrationParser(); $result = $parser->wrapFromPath($path); } catch (InvalidArgumentException $e) { Log::error($e); } return $result; }