path_redirect_import-8.x-1.0-beta4/src/RedirectExport.php
src/RedirectExport.php
<?php
namespace Drupal\path_redirect_import;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\file\FileRepositoryInterface;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use League\Csv\Writer;
// cspell:ignore redirections
/**
* Service that manages the redirect export batch operations.
*/
class RedirectExport {
use MigratePluginTrait;
const MIGRATE_FOLDER = 'public://path_redirect_import/';
const BATCH_SIZE = 50;
const HEADERS = ['source', 'destination', 'language', 'status_code'];
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The file repository.
*
* @var \Drupal\file\FileRepositoryInterface
*/
protected $fileRepository;
/**
* Constructs a RedirectExport object.
*
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The plugin manager for config entity-based migrations.
* @param \Drupal\file\FileRepositoryInterface $file_repository
* The file repository.
*/
public function __construct(
FileSystemInterface $file_system,
EntityTypeManagerInterface $entity_type_manager,
MigrationPluginManagerInterface $migration_plugin_manager,
FileRepositoryInterface $file_repository,
) {
$this->fileSystem = $file_system;
$this->entityTypeManager = $entity_type_manager;
$this->migrationPluginManager = $migration_plugin_manager;
$this->fileRepository = $file_repository;
}
/**
* Method description.
*/
public function doSomething() {
// @DCG place your code here.
}
/**
* Creates the spreadsheet file to export entries to.
*
* @return \Drupal\file\FileInterface
* File.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function getFile() {
$filename = 'export_' . time() . '.csv';
$uri = self::MIGRATE_FOLDER . $filename;
$directory = self::MIGRATE_FOLDER;
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
return $this->fileRepository->writeData('', $uri, FileExists::Replace);
}
/**
* Get the CSV writer.
*
* @param string $file_path
* Path of the file.
* @param string $mode
* Mode to open the file.
* @param array $configuration
* Array with CSV configuration.
*
* @return \League\Csv\Writer
* The writer.
*
* @throws \Drupal\migrate\MigrateException
* @throws \League\Csv\Exception
*/
protected static function createWriter($file_path, $mode, array $configuration) {
$writer = Writer::createFromPath($file_path, $mode);
$writer->setDelimiter($configuration['delimiter']);
$writer->setEnclosure($configuration['enclosure']);
$writer->setEscape($configuration['escape']);
return $writer;
}
/**
* Load the same configuration of migrate for CSV=>Table import path.
*
* @return array
* Configuration for csv info, separators, etc.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function getConfigurationFromPlugin() {
$migration_plugin = $this->migrationPlugin();
return $migration_plugin->getSourcePlugin()->getConfiguration();
}
/**
* Returns the Export redirect batch operations.
*
* @return array
* The batch operations.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function getBatchOperations() {
// Create file, folder and prepare variables.
$file = $this->getFile();
$configuration = $this->getConfigurationFromPlugin();
// Load a list of all ids to process.
$ids = $this->entityTypeManager
->getStorage('redirect')
->getQuery()
->accessCheck(TRUE)
->execute();
// Breakdown process into small batches.
$operations = [];
$item_start = 0;
foreach (array_chunk($ids, self::BATCH_SIZE) as $batch_data) {
$operations[] = [
get_class($this) . '::batchProcessExport',
[$file, $configuration, $batch_data, $item_start, count($ids)],
];
$item_start += self::BATCH_SIZE;
}
return $operations;
}
/**
* Export into the CSV per batches.
*
* @param \Drupal\file\entity\File $file
* File where information will be stored to.
* @param array $configuration
* Configuration for CSV structure.
* @param array $batch_data
* Ids of redirect entities to process.
* @param int $start
* Next item to process.
* @param int $total
* Total of items to process.
* @param mixed $context
* Context array/iterable.
*/
public static function batchProcessExport(File $file, array $configuration, array $batch_data, int $start, int $total, &$context) {
$file_path = \Drupal::service('file_system')->realpath($file->getFileUri());
$context['results']['failures'] = isset($context['results']['failures']) ?? 0;
$context['results']['file'] = $file;
// Write header in first iteration of first batch.
if (empty($start)) {
// Write header.
$writer = self::createWriter($file_path, 'w', $configuration);
$writer->insertOne(self::HEADERS);
}
$storage_handler = \Drupal::entityTypeManager()->getStorage('redirect');
$entities = $storage_handler->loadMultiple($batch_data);
if (empty($writer)) {
$writer = self::createWriter($file_path, 'a', $configuration);
}
// Now export entities one by one, only with the fields we need.
/** @var \Drupal\redirect\Entity\Redirect $redirect_entity */
foreach ($entities as $redirect_entity) {
try {
$source = $redirect_entity->getSourceUrl();
$redirect = $redirect_entity->getRedirectUrl();
$lang = $redirect_entity->get('language')->value;
$status_code = $redirect_entity->get('status_code')->value;
$writer->insertOne([
$source,
$redirect->toString(),
$lang,
$status_code,
]);
}
catch (\Exception $e) {
$context['results']['failures']++;
}
$start++;
}
$context['finished'] = 1;
if ($start >= $total) {
$context['results']['processed'] = $start;
}
else {
$context['message'] = t('Exporting (@percent%).', [
'@percent' => (int) (($start / $total) * 100),
]);
}
}
/**
* Finished callback for import batches.
*
* @param bool $success
* A boolean indicating whether the batch has completed successfully.
* @param array $results
* The value set in $context['results'] by callback_batch_operation().
* @param array $operations
* If $success is FALSE, contains the operations that remained unprocessed.
*/
public static function batchFinishedExport($success, array $results, array $operations) {
/** @var \Drupal\file\Entity\File $file */
$file = !empty($results['file']) ? $results['file'] : NULL;
if ($success && $file) {
$uri = \Drupal::service('file_url_generator')->generateAbsoluteString($file->getFileUri());
$url = Url::fromUri($uri);
$download = Link::fromTextAndUrl(t('link'), $url);
$return['link'] = $url;
\Drupal::messenger()->addStatus(t('Export process finished. You may download the file through this %link', ['%link' => $download->toString()]));
if (isset($results['failures']) && isset($results['processed'])) {
\Drupal::messenger()->addStatus(t('Processed @processed items. Exported: @correct, failures: @failures',
[
'@processed' => $results['processed'],
'@correct' => $results['processed'] - $results['failures'],
'@failures' => $results['failures'],
]));
}
}
else {
\Drupal::messenger()->addError(t('Export process failed. Please review existing redirections or contact an administrator.'));
}
// In any other case, set file as temporary so that cron deletes it.
if ($file) {
$file->setTemporary();
$file->save();
}
}
}
