cforge-2.0.x-dev/modules/cforge_import/src/Plugin/CsvParser/ImportBase.php
modules/cforge_import/src/Plugin/CsvParser/ImportBase.php
<?php
namespace Drupal\cforge_import\Plugin\CsvParser;
use Drupal\user\Entity\User;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Logger\LoggerChannel;
use Symfony\Component\DependencyInjection\ContainerInterface;
ini_set('max_execution_time', 150);
/**
* Base class for importing entities from csv files.
*/
abstract class ImportBase extends PluginBase implements CsvParserInterface, ContainerFactoryPluginInterface {
use MessengerTrait;
protected $entity;
protected $logger;
protected array $fields;
public function __construct($configuration, $plugin_id, $plugin_definition, LoggerChannel $logger_channel) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->logger = $logger_channel;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('logger.channel.cforge')
);
}
/**
* {@inheritDoc}
*
* The sandbox seems to be empty every time, unfortunately.
*/
public static function saveEntities(string $plugin_id, array $rows, bool $save, array &$sandbox = []) : void {
$plugin = \Drupal::service('cforge.csv_importer')->createInstance($plugin_id);
$accountSwitcher = \Drupal::service('account_switcher');
$accountSwitcher->switchTo(User::load(1));
foreach ($rows as $row) {
$plugin->saveEntity($row, $save);
}
$accountSwitcher->switchBack();
}
/**
* {@inheritDoc}
*/
function saveEntity($row, $save = FALSE) {
$this->fields = $row;
$this->buildEntity($row);
foreach ($this->entity->validate() as $violation) {
$this->logger->error(
"Failed validation @parents: '@message' in <pre>@row</pre><br /><pre>@array</pre>",
array(
'@parents' => $violation->getPropertyPath(),
'@message' => $violation->getMessage(),
'@row' => print_r($row, 1),
'@array' => print_r($this->entity->toArray(), 1),
)
);
}
if ($save) {
$this->entity->save();
if ($postCols = array_keys($this->columns())) {
foreach ($postCols as $col) {
$methodname = $col . 'PostProcess';
if (isset($row[$col]) and method_exists($this, $methodname)) {
$this->{$methodname}($row[$col], $row);
}
}
$this->entity->save();
}
}
}
/**
* {@inheritDoc}
*/
public function buildEntity(array $fields) {
$entity_type_manager = \Drupal::entityTypeManager();
$defaults = $this->getDefaults();
$def = $this->getPluginDefinition();
$entity_type = $def['entity_type'];
if (isset($def['bundle'])) {
$bundle_key = $entity_type_manager->getDefinition($entity_type)->getKey('bundle');
$defaults[$bundle_key] = $def['bundle'];
}
$entity_type = $this->getPluginDefinition()['entity_type'];
$this->entity = $entity_type_manager->getStorage($entity_type)
->create($defaults);
foreach ($fields as $fieldname => $value) {
$error = '';
// Fields with multiple columns.
if (strpos($fieldname, '.')) {
list($field, $col) = explode('.', $fieldname);
$method = $field . '_' . $col . 'Process';
if (method_exists($this, $method)) {
$error = $this->{$method}($fields[$fieldname]);
}
elseif ($value) {
$this->entity->{$field}->{$col} = $value;
}
}
// Time fields.
elseif (in_array($fieldname, ['created', 'changed', 'access'])) {
$this->entity->{$fieldname}->setValue($this->toUnixtime($fields[$fieldname]));
}
else {// Normal fields
$method = $fieldname . 'Process';
if (method_exists($this, $method)) {
$error = $this->{$method}($fields[$fieldname]);
}
elseif ($this->entity->hasField($fieldname) and $value !== "") {
$this->entity->set($fieldname, $value);
}
else {
// It must need postprocessing.
}
}
if ($error) {
\Drupal::messenger()->addWarning($error);
}
}
}
/**
* {@inheritDoc}
*/
protected function getDefaults() {
return [];
}
/**
* {@inheritDoc}
*/
protected function toUnixtime($val) {
if ($val) {
if (!is_int($val)) {
$val = strtotime($val);
}
return $val;
}
}
/**
* {@inheritDoc}
*/
protected function langcodeProcess($val) {
$this->entity->set('langcode', $val);
$this->entity->set('preferred_langcode', $val);
}
/**
* {@inheritDoc}
*/
public function ready() :bool {
return FALSE;
}
/**
* {@inheritDoc}
*/
function makeBatch($rows, $delete = FALSE, $save = FALSE) {
// This could be overwritten.
$batch = ['title' => 'Creating entities'];
if ($delete) {
$batch['operations'][] = [[get_class($this), 'deleteAll'], []];
}
foreach (array_chunk($rows, 25) as $chunk) {
$batch['operations'][] = [[get_class($this), 'saveEntities'], [$this->getPluginId(), $chunk, $save]];
}
return $batch;
}
/**
* {@inheritDoc}
*/
public function getCsvRows(array $csv) {
$headings = $csv[0];
array_walk($csv, function(&$a) use ($headings) {
if (count($headings) != count($a)) {
throw new \Exception('Wrong number of columns in csv row. '. count($headings) .' header fields, '.count($a).' fields in row: '.implode(',', $a));
}
$a = array_combine($headings, $a);
});
// Remove first row.
$headings = array_shift($csv);
$output = [];
foreach ($csv as $key => $row) {
$fields = array_combine($headings, $row);
foreach (array_keys($this->columns()) as $col_name) {
if (isset($fields[$col_name])) {
$output[$key][$col_name] = $fields[$col_name];
}
else $missing[] = $col_name;
}
}
if (isset($missing)) {
\Drupal::messenger()->addWarning('Missing fields: '.implode(', ', array_unique($missing)));
}
return $output;
}
}
