jgrygierek / SonataBatchEntityImportBundle

Importing entities with preview and edit features for Sonata Admin.
MIT License
8 stars 3 forks source link
csv-import php sonata sonata-admin symfony symfony-bundle

SonataBatchEntityImportBundle

Code Style Tests Code Coverage PHP Versions Symfony Versions SymfonyInsight

Bundle is built on top of BatchEntityImportBundle.

Importing entities with preview and edit features for Sonata Admin.

Select File Edit Matrix

Documentation

Installation

Install package via composer:

composer require jgrygierek/sonata-batch-entity-import-bundle

Add entry to bundles.php file:

JG\SonataBatchEntityImportBundle\SonataBatchEntityImportBundle::class => ['all' => true],

Configuration class

To define how the import function should work, you need to create a configuration class.

Basic configuration class

In the simplest case it will contain only class of used entity.

namespace App\Model\ImportConfiguration;

use App\Entity\User;
use JG\BatchEntityImportBundle\Model\Configuration\AbstractImportConfiguration;

class UserImportConfiguration extends AbstractImportConfiguration
{
    public function getEntityClassName(): string
    {
        return User::class;
    }
}

Then register it as a service:

services:
  App\Model\ImportConfiguration\UserImportConfiguration: ~

Fields definitions

If you want to change types of rendered fields, instead of using default ones, you have to override method in your import configuration. If name of field contains spaces, you should use underscores instead.

To avoid errors during data import, you can add here validation rules.

use JG\BatchEntityImportBundle\Model\Form\FormFieldDefinition;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;

public function getFieldsDefinitions(): array
{
    return [
        'age' => new FormFieldDefinition(
            IntegerType::class,
            [
                'attr' => [
                    'min' => 0,
                    'max' => 999,
                ],
            ]
        ),
        'name' => new FormFieldDefinition(TextType::class),
        'description' => new FormFieldDefinition(
            TextareaType::class,
            [
                'attr' => [
                    'rows' => 2,
                ],
                'constraints' => [new Length(['max' => 255])],
            ]
        ),
    ];
}

Matrix validation

This bundle provides two new validators.

1) DatabaseEntityUnique validator can be used to check if record data does not exist yet in database. 2) MatrixRecordUnique validator can be used to check duplication without checking database, just only matrix records values.

Names of fields should be the same as names of columns in your uploaded file. With one exception! If name contains spaces, you should use underscores instead.

use JG\BatchEntityImportBundle\Validator\Constraints\DatabaseEntityUnique;
use JG\BatchEntityImportBundle\Validator\Constraints\MatrixRecordUnique;

public function getMatrixConstraints(): array
{
    return [
        new MatrixRecordUnique(['fields' => ['field_name']]),
        new DatabaseEntityUnique(['entityClassName' => $this->getEntityClassName(), 'fields' => ['field_name']]),
    ];
}

Passing services to configuration class

If you want to pass some additional services to your configuration, just override constructor.

public function __construct(EntityManagerInterface $em, TestService $service)
{
    parent::__construct($em);

    $this->testService = $service;
}

Then you will need to define this configuration class as a public service too.

Show & hide entity override column

If you want to hide/show an entity column that allows you to override entity default: true, you have to override this method in your import configuration.

public function allowOverrideEntity(): bool
{
    return true;
}

Optimizing queries

If you use KnpLabs Translatable extension for your entity, probably you will notice increased number of queries, because of Lazy Loading.

To optimize this, you can use getEntityTranslationRelationName() method to pass the relation name to the translation.

public function getEntityTranslationRelationName(): ?string
{
    return 'translations';
}

Creating admin

Your admin class should implement AdminWithImportInterface and should contain one additional method.

namespace App\Admin;

use App\Model\ImportConfiguration\UserImportConfiguration;
use JG\SonataBatchEntityImportBundle\Admin\AdminWithImportInterface;
use Sonata\AdminBundle\Admin\AbstractAdmin;

class UserAdmin extends AbstractAdmin implements AdminWithImportInterface
{
    public function getImportConfigurationClassName(): string
    {
        return UserImportConfiguration::class;
    }
}

Default controller

If you use default controller, no action is needed. Controller will be replaced automatically.

Custom controller

If you use your own custom controller, remember that this controller should:

Additionally, if you want to automatically inject your import configuration class, remember to implement JG\SonataBatchEntityImportBundle\Controller\ImportConfigurationAutoInjectInterface and use method getImportConfiguration() from default controller.

Translations

This bundle supports KnpLabs Translatable behavior.

To use this feature, every column with translatable values should be suffixed with locale, for example:

If suffix will be added to non-translatable entity, field will be skipped.

If suffix will be added to translatable entity, but field will not be found in translation class, field will be skipped.

Overriding templates

You have two ways to override templates globally:

sonata_batch_entity_import:
    templates:
        select_file: '@SonataBatchEntityImport/select_file.html.twig'
        edit_matrix: '@SonataBatchEntityImport/edit_matrix.html.twig'
        button: '@SonataBatchEntityImport/button.html.twig'
templates/bundles/SonataBatchEntityImportBundle