Skip to content

Commit

Permalink
[Array] Adding array on import (#99)
Browse files Browse the repository at this point in the history
* allow with fixed separator

add simple array import + custom separator

use form_help + default help message

fix help on all type + test

fix double check array

space

* add tests

* separator placeholder in translation file

* update test with translate param

* add tests

---------

Co-authored-by: Jarosław Grygierek <jaroslawgrygierek@gmail.com>
  • Loading branch information
rrr63 and jgrygierek authored Sep 26, 2024
1 parent c8f0e75 commit f3902f4
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 34 deletions.
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Importing entities with preview and edit features for Symfony.
* [Controller-specific templates](#controller-specific-templates)
* [Main layout](#main-layout)
* [Additional data](#additional-data)
* [Importing data to array field](#importing-data-to-array-field)
* [Full example of CSV file](#full-example-of-csv-file)

## Installation

Expand Down Expand Up @@ -345,3 +347,82 @@ protected function prepareMatrixEditView(FormInterface $form, Matrix $matrix, bo
}
```

## Importing data to array field

If your entity has an array field, and you want to import data from CSV file to it, this is how you can do it.

- Default separator is set to `|`.
- Only one-dimensional arrays are allowed.
- Keys are not allowed.
- **IMPORTANT!** There are limitations:
- There is no possibility to import array with one empty element, for example:
- ['']
- [null]
- But arrays with at least 2 such elements are allowed:
- ['', '']
- [null, null]
- ['', null]
- It is due of mapping CSV data to array:
- Empty value in CSV is equal to `[]`
- If we have default separator, `|` value in CSV is equal to `['', '']`

#### Entity

- Allowed entity field types:
- `json`
- `array`
- `simple_array`

```php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class User
{
#[ORM\Column(type: 'json']
private array $roles = [];
}
```

#### Import configuration

* You have to add a field definition with a custom `ArrayTextType` type. If you skip this configuration, `TextType` will be used as default.
* You can set here your own separator.

```php
use JG\BatchEntityImportBundle\Form\Type\ArrayTextType;
use JG\BatchEntityImportBundle\Model\Form\FormFieldDefinition;

public function getFieldsDefinitions(): array
{
return [
'roles' => new FormFieldDefinition(
ArrayTextType::class,
[
'separator' => '&',
]
),
];
}
```

#### CSV file

```csv
user_name,roles
user_1,USER&ADMIN&SUPER_ADMIN
user_2,USER
user_3,SUPER_ADMIN
```


## Full example of CSV file

```csv
user_name,age,email,roles,country:en,name:pl
user_1,21,user_1@test.com,USER&ADMIN&SUPER_ADMIN,Poland,Polska
user_2,34,user_2@test.com,USER,England,Anglia
user_3,56,user_3@test.com,SUPER_ADMIN,Germany,Niemcy
```
25 changes: 25 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
UPGRADE TO 3.2
=======================

Import data to array
--------------
* If your entity has an array field, and you want to import data from CSV file to it, it is now possible.

```php
use JG\BatchEntityImportBundle\Form\Type\ArrayTextType;
use JG\BatchEntityImportBundle\Model\Form\FormFieldDefinition;

public function getFieldsDefinitions(): array
{
return [
'roles' => new FormFieldDefinition(
ArrayTextType::class,
[
'separator' => '&',
]
),
];
}
```


UPGRADE TO 3.1
=======================

Expand Down
71 changes: 71 additions & 0 deletions src/Form/Type/ArrayTextType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace JG\BatchEntityImportBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
use UnexpectedValueException;

class ArrayTextType extends AbstractType implements DataTransformerInterface
{
public const DEFAULT_SEPARATOR = '|';
private string $separator = self::DEFAULT_SEPARATOR;

public function __construct(private readonly TranslatorInterface $translator)
{
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'compound' => false,
'separator' => self::DEFAULT_SEPARATOR,
]);
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$this->separator = $options['separator'] ?? self::DEFAULT_SEPARATOR;
}

public function transform(mixed $value): string
{
if (!\is_array($value)) {
throw new UnexpectedValueException('Only arrays are allowed');
}

return implode($this->separator, $value);
}

public function reverseTransform(mixed $value): array
{
if (!is_string($value)) {
throw new UnexpectedValueException('Only strings are allowed');
}

return $value
? explode($this->separator ?: self::DEFAULT_SEPARATOR, $value)
: [];
}

public function getBlockPrefix(): string
{
return 'array_text';
}

public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['help'] = $this->translator->trans(
'form.separator',
['%separator%' => $options['separator']],
'BatchEntityImportBundle',
);
}
}
17 changes: 17 additions & 0 deletions src/Model/Configuration/AbstractImportConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use JG\BatchEntityImportBundle\Exception\DatabaseException;
use JG\BatchEntityImportBundle\Exception\DatabaseNotUniqueDataException;
use JG\BatchEntityImportBundle\Exception\MatrixRecordInvalidDataTypeException;
use JG\BatchEntityImportBundle\Form\Type\ArrayTextType;
use JG\BatchEntityImportBundle\Model\Form\FormFieldDefinition;
use JG\BatchEntityImportBundle\Model\Matrix\Matrix;
use JG\BatchEntityImportBundle\Model\Matrix\MatrixRecord;
use JG\BatchEntityImportBundle\Utils\ColumnNameHelper;
Expand Down Expand Up @@ -51,6 +53,7 @@ protected function prepareRecord(MatrixRecord $record, array $headerInfo): void
{
$entity = $this->getEntity($record);
$data = $record->getData();
$fieldDefinitions = $this->getFieldsDefinitions();

foreach ($data as $name => $value) {
if (empty($headerInfo[$name])) {
Expand All @@ -61,6 +64,13 @@ protected function prepareRecord(MatrixRecord $record, array $headerInfo): void
$propertyName = ColumnNameHelper::toCamelCase($name);
$setterName = ColumnNameHelper::getSetterName($name);

if (isset($fieldDefinitions[$name])) {
$fieldDefinition = $fieldDefinitions[$name];
if (ArrayTextType::class === $fieldDefinition->getClass()) {
$value = $this->parseValueForArrayType($fieldDefinition, $value);
}
}

try {
if (\interface_exists(TranslatableInterface::class) && $entity instanceof TranslatableInterface && $locale) {
$translatedEntity = $entity->translate($locale, false);
Expand Down Expand Up @@ -119,4 +129,11 @@ public function allowOverrideEntity(): bool
{
return true;
}

private function parseValueForArrayType(FormFieldDefinition $fieldDefinition, ?string $value): array
{
return $value
? explode($fieldDefinition->getOptions()['separator'] ?? ArrayTextType::DEFAULT_SEPARATOR, $value)
: [];
}
}
4 changes: 4 additions & 0 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ services:
JG\BatchEntityImportBundle\Validator\Constraints\DatabaseEntityUniqueValidator:
arguments: [ '@doctrine.orm.entity_manager' ]
tags: [ validator.constraint_validator ]
JG\BatchEntityImportBundle\Form\Type\ArrayTextType:
arguments:
$translator: '@translator'
tags: ['form.type']
4 changes: 4 additions & 0 deletions src/Resources/translations/BatchEntityImportBundle.en.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
<source>success.import</source>
<target>Data has been imported</target>
</trans-unit>
<trans-unit id="form.separator">
<source>form.separator</source>
<target>Separator: "%separator%"</target>
</trans-unit>
</body>
</file>
</xliff>
4 changes: 4 additions & 0 deletions src/Resources/translations/BatchEntityImportBundle.pl.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
<source>success.import</source>
<target>Dane zostały zaimportowane</target>
</trans-unit>
<trans-unit id="form.separator">
<source>form.separator</source>
<target>Separator: "%separator%"</target>
</trans-unit>
</body>
</file>
</xliff>
1 change: 1 addition & 0 deletions src/Resources/views/edit_matrix.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
{% for elem in record.children %}
<td>
{{ form_widget(elem) }}
{{ form_help(elem) }}
{{ form_errors(elem) }}
</td>
{% endfor %}
Expand Down
Loading

0 comments on commit f3902f4

Please sign in to comment.