class SourceCsvForm in Migrate Tools 8.4
Provides an edit form for CSV source plugin column_names configuration.
This means you can tell the migration which columns your data is in and no longer edit the CSV to fit the column order set in the migration or edit the migration yml itself.
Changes made to the column configuration, or aliases, are stored in the private migrate_tools private store keyed by the migration plugin id. The data stored for each migrations consists of two arrays, the 'original' column aliases and the 'updated' column aliases.
An additional list of all changed migration id is kept in the store, in the key 'migrations_changed'
Private Store Usage: migrations_changed: An array of the ids of the migrations that have been changed: [migration_id]: The original and changed values for this column assignments
Format of the source configuration saved in the store. migration_id original column_index1 property 1 => label 1 column_index2 property 2 => label 2 updated column_index1 property 2 => label 2 column_index2 property 1 => label 1
Example source configuration: custom_migration original 2 title => title 3 body => foo updated 8 title => new_title 9 body => new_body
Hierarchy
- class \Drupal\Core\Form\FormBase implements ContainerInjectionInterface, FormInterface uses DependencySerializationTrait, LoggerChannelTrait, MessengerTrait, LinkGeneratorTrait, RedirectDestinationTrait, UrlGeneratorTrait, StringTranslationTrait
- class \Drupal\migrate_tools\Form\SourceCsvForm
Expanded class hierarchy of SourceCsvForm
1 string reference to 'SourceCsvForm'
File
- src/
Form/ SourceCsvForm.php, line 66
Namespace
Drupal\migrate_tools\FormView source
class SourceCsvForm extends FormBase {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Plugin manager for migration plugins.
*
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
*/
protected $migrationPluginManager;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* Temporary store for column assignment changes.
*
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $store;
/**
* The file object that reads the CSV file.
*
* @var \SplFileObject
*/
protected $file = NULL;
/**
* The migration being examined.
*
* @var \Drupal\migrate\Plugin\MigrationInterface
*/
protected $migration;
/**
* The migration plugin id.
*
* @var string
*/
protected $id;
/**
* The array of columns names from the CSV source plugin.
*
* @var array
*/
protected $columnNames;
/**
* An array of options for the column select form field..
*
* @var array
*/
protected $options;
/**
* An array of modified and original column_name source plugin configuration.
*
* @var array
*/
protected $sourceConfiguration;
/**
* Constructs new SourceCsvForm object.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The plugin manager for config entity-based migrations.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $private_store
* The private store.
*/
public function __construct(Connection $connection, MigrationPluginManagerInterface $migration_plugin_manager, MessengerInterface $messenger, PrivateTempStoreFactory $private_store) {
$this->connection = $connection;
$this->migrationPluginManager = $migration_plugin_manager;
$this->messenger = $messenger;
$this->store = $private_store
->get('migrate_tools');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container
->get('database'), $container
->get('plugin.manager.migration'), $container
->get('messenger'), $container
->get('tempstore.private'));
}
/**
* A custom access check.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
* The $migration.
*
* @return \Drupal\Core\Access\AccessResult
* Allowed or forbidden, neutral if tempstore is empty.
*/
public function access(AccountInterface $account, MigrationInterface $migration) {
try {
$this->migration = $this->migrationPluginManager
->createInstance($migration
->id(), $migration
->toArray());
} catch (PluginException $e) {
return AccessResult::forbidden();
}
if ($this->migration) {
if ($source = $this->migration
->getSourcePlugin()) {
if (is_a($source, CSV::class)) {
return AccessResult::allowed();
}
}
}
return AccessResult::forbidden();
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, MigrationInterface $migration = NULL) {
try {
// @TODO: remove this horrible config work around after
// https://www.drupal.org/project/drupal/issues/2986665 is fixed.
$this->migration = $this->migrationPluginManager
->createInstance($migration
->id(), $migration
->toArray());
/** @var \Drupal\migrate_source_csv\Plugin\migrate\source\CSV $source */
$source = $this->migration
->getSourcePlugin();
$source
->setConfiguration($migration
->toArray()['source']);
} catch (PluginException $e) {
return AccessResult::forbidden();
}
// Get the source file after the properties are initialized.
$source
->initializeIterator();
$this->file = $source
->getFile();
// Set the input field options to the header row values or, if there are
// no such values, use an indexed array.
if ($this->file
->getHeaderRowCount() > 0) {
$this->options = $this
->getHeaderColumnNames();
}
else {
for ($i = 0; $i < $this
->getFileColumnCount(); $i++) {
$this->options[$i] = $i;
}
}
// Set the store key to the migration id.
$this->id = $this->migration
->getPluginId();
// Get the column names from the file or from the store, if updated
// values are in the store.
$this->sourceConfiguration = $this->store
->get($this->id);
if (isset($this->sourceConfiguration['changed'])) {
if ($config = $this->sourceConfiguration['changed']) {
$this->columnNames = $config;
}
}
else {
// Get the calculated column names. This is either the header rows or
// the configuration column_name value.
$this->columnNames = $this->file
->getColumnNames();
if (!isset($this->sourceConfiguration['original'])) {
// Save as the original values.
$this->sourceConfiguration['original'] = $this->columnNames;
$this->store
->set($this->id, $this->sourceConfiguration);
}
}
$form['#title'] = $this
->t('Column Aliases');
$form['heading'] = [
'#type' => 'item',
'#title' => $this
->t(':label', [
':label' => $this->migration
->label(),
]),
'#description' => '<p>' . $this
->t('You can change the columns to be used by this migration for each source property.') . '</p>',
];
// Create a form field for each column in this migration.
foreach ($this->columnNames as $index => $data) {
$property_name = key($data);
$default_value = $index;
$label = $this
->getLabel($this->sourceConfiguration['original'], $property_name);
$description = $this
->t('Select the column where the data for <em>:label</em>, property <em>:property</em>, will be found.', [
':label' => $label,
':property' => $property_name,
]);
$form['aliases'][$property_name] = [
'#type' => 'select',
'#title' => $label,
'#description' => $description,
'#options' => $this->options,
'#default_value' => $default_value,
];
}
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#button_type' => 'primary',
'#value' => $this
->t('Submit'),
];
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => $this
->t('Cancel'),
'#submit' => [
'::cancel',
],
'#limit_validation_errors' => [],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Display an error message if two properties have the same source column.
$values = [];
foreach ($this->columnNames as $index => $data) {
$property_name = key($data);
$value = $form_state
->getValue($property_name);
if (in_array($value, $values)) {
$form_state
->setErrorByName($property_name, $this
->t('Source properties can not share the same source column.'));
}
$values[] = $value;
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Create a new column_names configuration.
$new_column_names = [];
foreach ($this->columnNames as $index => $data) {
// Keep the property name as it is used in the process pipeline.
$property_name = key($data);
// Get the new column number from the form alias field for this property.
$new_index = $form_state
->getValue($property_name);
// Get the new label from the options array.
$new_label = $this->options[$new_index];
// Save using the new column number and new label.
$new_column_names[$new_index] = [
$property_name => $new_label,
];
}
// Update the file columns.
$this->file
->setColumnNames($new_column_names);
// Save as updated in the store.
$this->sourceConfiguration['changed'] = $new_column_names;
$this->store
->set($this->id, $this->sourceConfiguration);
$changed = $this->store
->get('migrations_changed') ? $this->store
->get('migrations_changed') : [];
if (!in_array($this->id, $changed)) {
$changed[] = $this->id;
$this->store
->set('migrations_changed', $changed);
}
}
/**
* Form submission handler for the 'cancel' action.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function cancel(array $form, FormStateInterface $form_state) {
// Restore the file columns to the original settings.
$this->file
->setColumnNames($this->sourceConfiguration['original']);
// Remove this migration from the store.
try {
$this->store
->delete($this->id);
} catch (TempStoreException $e) {
$this->messenger
->addError($e
->getMessage());
}
$migrationsChanged = $this->store
->get('migrations_changed');
unset($migrationsChanged[$this->id]);
try {
$this->store
->set('migrations_changed', $migrationsChanged);
} catch (TempStoreException $e) {
$this->messenger
->addError($e
->getMessage());
}
$form_state
->setRedirect('entity.migration_group.list');
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_tools_source_csv';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.migration_group.list');
}
/**
* Returns the header row.
*
* Use a new file handle so that CSVFileObject::current() is not executed.
*
* @return array
* The header row.
*/
public function getHeaderColumnNames() {
$row = [];
$fname = $this->file
->getPathname();
$handle = fopen($fname, 'r');
if ($handle) {
fseek($handle, $this->file
->getHeaderRowCount() - 1);
$row = fgetcsv($handle);
fclose($handle);
}
return $row;
}
/**
* Returns the count of fields in the header row.
*
* Use a new file handle so that CSVFileObject::current() is not executed.
*
* @return int
* The number of fields in the header row.
*/
public function getFileColumnCount() {
$count = 0;
$fname = $this->file
->getPathname();
$handle = fopen($fname, 'r');
if ($handle) {
$row = fgetcsv($handle);
$count = count($row);
fclose($handle);
}
return $count;
}
/**
* Gets the label for a given property from a column_names array.
*
* @param array $column_names
* An array of column_names.
* @param string $property_name
* The property name to find a label for.
*
* @return string
* The label for this property.
*/
protected function getLabel(array $column_names, $property_name) {
$label = '';
foreach ($column_names as $column) {
foreach ($column as $key => $value) {
if ($key === $property_name) {
$label = $value;
break;
}
}
}
return $label;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormBase:: |
protected | property | The config factory. | 1 |
FormBase:: |
protected | property | The request stack. | 1 |
FormBase:: |
protected | property | The route match. | |
FormBase:: |
protected | function | Retrieves a configuration object. | |
FormBase:: |
protected | function | Gets the config factory for this form. | 1 |
FormBase:: |
private | function | Returns the service container. | |
FormBase:: |
protected | function | Gets the current user. | |
FormBase:: |
protected | function | Gets the request object. | |
FormBase:: |
protected | function | Gets the route match. | |
FormBase:: |
protected | function | Gets the logger for a specific channel. | |
FormBase:: |
protected | function |
Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait:: |
|
FormBase:: |
public | function | Resets the configuration factory. | |
FormBase:: |
public | function | Sets the config factory for this form. | |
FormBase:: |
public | function | Sets the request stack object to use. | |
LinkGeneratorTrait:: |
protected | property | The link generator. | 1 |
LinkGeneratorTrait:: |
protected | function | Returns the link generator. | |
LinkGeneratorTrait:: |
protected | function | Renders a link to a route given a route name and its parameters. | |
LinkGeneratorTrait:: |
public | function | Sets the link generator service. | |
LoggerChannelTrait:: |
protected | property | The logger channel factory service. | |
LoggerChannelTrait:: |
protected | function | Gets the logger for a specific channel. | |
LoggerChannelTrait:: |
public | function | Injects the logger channel factory. | |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
RedirectDestinationTrait:: |
protected | property | The redirect destination service. | 1 |
RedirectDestinationTrait:: |
protected | function | Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url. | |
RedirectDestinationTrait:: |
protected | function | Returns the redirect destination service. | |
RedirectDestinationTrait:: |
public | function | Sets the redirect destination service. | |
SourceCsvForm:: |
protected | property | The array of columns names from the CSV source plugin. | |
SourceCsvForm:: |
protected | property | The database connection. | |
SourceCsvForm:: |
protected | property | The file object that reads the CSV file. | |
SourceCsvForm:: |
protected | property | The migration plugin id. | |
SourceCsvForm:: |
protected | property |
The messenger service. Overrides MessengerTrait:: |
|
SourceCsvForm:: |
protected | property | The migration being examined. | |
SourceCsvForm:: |
protected | property | Plugin manager for migration plugins. | |
SourceCsvForm:: |
protected | property | An array of options for the column select form field.. | |
SourceCsvForm:: |
protected | property | An array of modified and original column_name source plugin configuration. | |
SourceCsvForm:: |
protected | property | Temporary store for column assignment changes. | |
SourceCsvForm:: |
public | function | A custom access check. | |
SourceCsvForm:: |
public | function |
Form constructor. Overrides FormInterface:: |
|
SourceCsvForm:: |
public | function | Form submission handler for the 'cancel' action. | |
SourceCsvForm:: |
public static | function |
Instantiates a new instance of this class. Overrides FormBase:: |
|
SourceCsvForm:: |
public | function | ||
SourceCsvForm:: |
public | function | Returns the count of fields in the header row. | |
SourceCsvForm:: |
public | function |
Returns a unique string identifying the form. Overrides FormInterface:: |
|
SourceCsvForm:: |
public | function | Returns the header row. | |
SourceCsvForm:: |
protected | function | Gets the label for a given property from a column_names array. | |
SourceCsvForm:: |
public | function | ||
SourceCsvForm:: |
public | function |
Form submission handler. Overrides FormInterface:: |
|
SourceCsvForm:: |
public | function |
Form validation handler. Overrides FormBase:: |
|
SourceCsvForm:: |
public | function | Constructs new SourceCsvForm object. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
UrlGeneratorTrait:: |
protected | property | The url generator. | |
UrlGeneratorTrait:: |
protected | function | Returns the URL generator service. | |
UrlGeneratorTrait:: |
public | function | Sets the URL generator service. | |
UrlGeneratorTrait:: |
protected | function | Generates a URL or path for a specific route based on the given parameters. |