Move multi insert functionality to a special repository
[MAILPOET-3378]
This commit is contained in:
@ -246,6 +246,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
|||||||
$container->autowire(\MailPoet\Subscribers\SubscriberListingRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Subscribers\SubscriberListingRepository::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Subscribers\SubscriberSegmentRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Subscribers\SubscriberSegmentRepository::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Subscribers\SubscriberCustomFieldRepository::class)->setPublic(true);
|
$container->autowire(\MailPoet\Subscribers\SubscriberCustomFieldRepository::class)->setPublic(true);
|
||||||
|
$container->autowire(\MailPoet\Subscribers\ImportExport\ImportExportRepository::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Subscribers\Statistics\SubscriberStatisticsRepository::class);
|
$container->autowire(\MailPoet\Subscribers\Statistics\SubscriberStatisticsRepository::class);
|
||||||
// Segments
|
// Segments
|
||||||
$container->autowire(\MailPoet\Segments\WooCommerce::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\WooCommerce::class)->setPublic(true);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace MailPoet\Doctrine;
|
namespace MailPoet\Doctrine;
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
use MailPoetVendor\Doctrine\ORM\EntityRepository as DoctrineEntityRepository;
|
use MailPoetVendor\Doctrine\ORM\EntityRepository as DoctrineEntityRepository;
|
||||||
use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
|
use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
@ -78,7 +77,7 @@ abstract class Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function truncate() {
|
public function truncate() {
|
||||||
$tableName = $this->getTableName();
|
$tableName = $this->classMetadata->getTableName();
|
||||||
$connection = $this->entityManager->getConnection();
|
$connection = $this->entityManager->getConnection();
|
||||||
$connection->query('SET FOREIGN_KEY_CHECKS=0');
|
$connection->query('SET FOREIGN_KEY_CHECKS=0');
|
||||||
$q = "TRUNCATE $tableName";
|
$q = "TRUNCATE $tableName";
|
||||||
@ -108,52 +107,4 @@ abstract class Repository {
|
|||||||
* @return class-string<T>
|
* @return class-string<T>
|
||||||
*/
|
*/
|
||||||
abstract protected function getEntityClassName();
|
abstract protected function getEntityClassName();
|
||||||
|
|
||||||
protected function getTableName(): string {
|
|
||||||
return $this->entityManager->getClassMetadata($this->getEntityClassName())->getTableName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertOrUpdateMultiple(array $columns, array $data, ?DateTime $updatedAt = null): int {
|
|
||||||
$tableName = $this->getTableName();
|
|
||||||
$entityColumns = $this->entityManager->getClassMetadata($this->getEntityClassName())->getColumnNames();
|
|
||||||
|
|
||||||
if (!$columns || !$data) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = [];
|
|
||||||
$parameters = [];
|
|
||||||
foreach ($data as $key => $item) {
|
|
||||||
$paramNames = array_map(function (string $parameter) use ($key): string {
|
|
||||||
return ":{$parameter}_{$key}";
|
|
||||||
}, $columns);
|
|
||||||
|
|
||||||
foreach ($item as $columnKey => $column) {
|
|
||||||
$parameters[$paramNames[$columnKey]] = $column;
|
|
||||||
}
|
|
||||||
$rows[] = "(" . implode(', ', $paramNames) . ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
$updateColumns = array_map(function (string $column): string {
|
|
||||||
return "{$column} = VALUES($column)";
|
|
||||||
}, array_diff($columns, $this->ignoreColumnsForUpdate));
|
|
||||||
|
|
||||||
if ($updatedAt && in_array('updated_at', $entityColumns, true)) {
|
|
||||||
$parameters['updated_at'] = $updatedAt;
|
|
||||||
$updateColumns[] = "updated_at = :updated_at";
|
|
||||||
}
|
|
||||||
|
|
||||||
// we want to reset deleted_at for updated rows
|
|
||||||
if (in_array('deleted_at', $entityColumns, true)) {
|
|
||||||
$updateColumns[] = 'deleted_at = NULL';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $this->entityManager->getConnection()->executeUpdate("
|
|
||||||
INSERT INTO {$tableName} (`" . implode("`, `", $columns) . "`) VALUES
|
|
||||||
" . implode(", \n", $rows) . "
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
" . implode(", \n", $updateColumns) . "
|
|
||||||
", $parameters);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
155
lib/Subscribers/ImportExport/ImportExportRepository.php
Normal file
155
lib/Subscribers/ImportExport/ImportExportRepository.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Subscribers\ImportExport;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use MailPoet\Entities\SubscriberCustomFieldEntity;
|
||||||
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
|
use MailPoet\Entities\SubscriberSegmentEntity;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Connection;
|
||||||
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
|
use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
|
||||||
|
class ImportExportRepository {
|
||||||
|
private const IGNORED_COLUMNS_FOR_BULK_UPDATE = [
|
||||||
|
SubscriberEntity::class => [
|
||||||
|
'wp_user_id',
|
||||||
|
'is_woocommerce_user',
|
||||||
|
'email',
|
||||||
|
'created_at',
|
||||||
|
'last_subscribed_at',
|
||||||
|
],
|
||||||
|
SubscriberCustomFieldEntity::class => [
|
||||||
|
'created_at',
|
||||||
|
],
|
||||||
|
SubscriberSegmentEntity::class => [
|
||||||
|
'created_at',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
private const KEY_COLUMNS_FOR_BULK_UPDATE = [
|
||||||
|
SubscriberEntity::class => [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
SubscriberCustomFieldEntity::class => [
|
||||||
|
'subscriber_id',
|
||||||
|
'custom_field_id',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var EntityManager */
|
||||||
|
protected $entityManager;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
protected $ignoreColumnsForBulkUpdate = [
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(EntityManager $entityManager) {
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getClassMetadata(string $className): ClassMetadata {
|
||||||
|
return $this->entityManager->getClassMetadata($className);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTableName(string $className): string {
|
||||||
|
return $this->getClassMetadata($className)->getTableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTableColumns(string $className): array {
|
||||||
|
return $this->getClassMetadata($className)->getColumnNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertMultiple(
|
||||||
|
string $className,
|
||||||
|
array $columns,
|
||||||
|
array $data
|
||||||
|
): int {
|
||||||
|
$tableName = $this->getTableName($className);
|
||||||
|
|
||||||
|
if (!$columns || !$data) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
$parameters = [];
|
||||||
|
foreach ($data as $key => $item) {
|
||||||
|
$paramNames = array_map(function (string $parameter) use ($key): string {
|
||||||
|
return ":{$parameter}_{$key}";
|
||||||
|
}, $columns);
|
||||||
|
|
||||||
|
foreach ($item as $columnKey => $column) {
|
||||||
|
$parameters[$paramNames[$columnKey]] = $column;
|
||||||
|
}
|
||||||
|
$rows[] = "(" . implode(', ', $paramNames) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->entityManager->getConnection()->executeUpdate("
|
||||||
|
INSERT IGNORE INTO {$tableName} (`" . implode("`, `", $columns) . "`) VALUES
|
||||||
|
" . implode(", \n", $rows) . "
|
||||||
|
", $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMultiple(
|
||||||
|
string $className,
|
||||||
|
array $columns,
|
||||||
|
array $data,
|
||||||
|
?DateTime $updatedAt = null
|
||||||
|
): int {
|
||||||
|
$tableName = $this->getTableName($className);
|
||||||
|
$entityColumns = $this->getTableColumns($className);
|
||||||
|
|
||||||
|
if (!$columns || !$data) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = [];
|
||||||
|
$parameterTypes = [];
|
||||||
|
$keyColumns = self::KEY_COLUMNS_FOR_BULK_UPDATE[$className] ?? [];
|
||||||
|
if (!$keyColumns) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyColumnsConditions = [];
|
||||||
|
foreach ($keyColumns as $keyColumn) {
|
||||||
|
$columnIndex = array_search($keyColumn, $columns);
|
||||||
|
$parameters[$keyColumn] = array_map(function(array $row) use ($columnIndex) {
|
||||||
|
return $row[$columnIndex];
|
||||||
|
}, $data);
|
||||||
|
$parameterTypes[$keyColumn] = Connection::PARAM_STR_ARRAY;
|
||||||
|
$keyColumnsConditions[] = "{$keyColumn} IN (:{$keyColumn})";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ignoredColumns = self::IGNORED_COLUMNS_FOR_BULK_UPDATE[$className] ?? ['created_at'];
|
||||||
|
$updateColumns = array_map(function($columnName) use ($keyColumns, $columns, $data, &$parameters): string {
|
||||||
|
$values = [];
|
||||||
|
foreach ($data as $index => $row) {
|
||||||
|
$keyCondition = array_map(function($keyColumn) use ($index, $row, $columns, &$parameters): string {
|
||||||
|
$parameters["{$keyColumn}_{$index}"] = $row[array_search($keyColumn, $columns)];
|
||||||
|
return "{$keyColumn} = :{$keyColumn}_{$index}";
|
||||||
|
}, $keyColumns);
|
||||||
|
$values[] = "WHEN " . implode(' AND ', $keyCondition) . " THEN :{$columnName}_{$index}";
|
||||||
|
$parameters["{$columnName}_{$index}"] = $row[array_search($columnName, $columns)];
|
||||||
|
}
|
||||||
|
return "{$columnName} = (CASE " . implode("\n", $values) . " END)";
|
||||||
|
}, array_diff($columns, $ignoredColumns));
|
||||||
|
|
||||||
|
if ($updatedAt && in_array('updated_at', $entityColumns, true)) {
|
||||||
|
$parameters['updated_at'] = $updatedAt;
|
||||||
|
$updateColumns[] = "updated_at = :updated_at";
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to reset deleted_at for updated rows
|
||||||
|
if (in_array('deleted_at', $entityColumns, true)) {
|
||||||
|
$updateColumns[] = 'deleted_at = NULL';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->entityManager->getConnection()->executeUpdate("
|
||||||
|
UPDATE {$tableName} SET
|
||||||
|
" . implode(", \n", $updateColumns) . "
|
||||||
|
WHERE
|
||||||
|
" . implode(' AND ', $keyColumnsConditions) . "
|
||||||
|
", $parameters, $parameterTypes);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user