Skip to content

Repository files navigation

BatchEntityImportBundle

Code StyleTestsCode CoveragePHP VersionsSymfony VersionsSymfonyInsight

Importing entities with preview and edit features for Symfony.

  • Data can be viewed and edited before saving to database.
  • Supports inserting new records and updating existing ones.
  • Supported extensions: CSV, XLS, XLSX, ODS.
  • Supports translations from KnpLabs Translatable extension.
  • The code is divided into smaller methods that can be easily replaced if you want to change something.
  • Columns names are required and should be added as header (first row).
  • If column does not have name provided, will be removed from loaded data.

Select FileEdit Matrix

Documentation

Installation

Install package via composer:

composer require jgrygierek/batch-entity-import-bundle

Add entry to bundles.php file:

JG\BatchEntityImportBundle\BatchEntityImportBundle::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.

namespaceApp\Model\ImportConfiguration;
useApp\Entity\User;
useJG\BatchEntityImportBundle\Model\Configuration\AbstractImportConfiguration;
class UserImportConfiguration extends AbstractImportConfiguration
{
publicfunctiongetEntityClassName(): 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.

useJG\BatchEntityImportBundle\Model\Form\FormFieldDefinition;
useSymfony\Component\Form\Extension\Core\Type\IntegerType;
useSymfony\Component\Form\Extension\Core\Type\TextareaType;
useSymfony\Component\Form\Extension\Core\Type\TextType;
useSymfony\Component\Validator\Constraints\Length;
publicfunctiongetFieldsDefinitions(): array
{
return [
'age' => newFormFieldDefinition(
IntegerType::class,
[
'attr' => [
'min' => 0,
'max' => 999,
],
]
),
'name' => newFormFieldDefinition(TextType::class),
'description' => newFormFieldDefinition(
TextareaType::class,
[
'attr' => [
'rows' => 2,
],
'constraints' => [newLength(['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.

useJG\BatchEntityImportBundle\Validator\Constraints\DatabaseEntityUnique;
useJG\BatchEntityImportBundle\Validator\Constraints\MatrixRecordUnique;
publicfunctiongetMatrixConstraints(): array
{
return [
newMatrixRecordUnique(['fields' => ['field_name']]),
newDatabaseEntityUnique(['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.

publicfunction__construct(EntityManagerInterface$em, TestService$service)
{
parent::__construct($em);
$this->testService = $service;
}

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

publicfunctionallowOverrideEntity(): bool
{
returntrue;
}

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.

publicfunctiongetEntityTranslationRelationName(): ?string
{
return'translations';
}

Creating controller

Create controller with some required code.

This is just an example, depending on your needs you can inject services in different ways.

To enable automatic passing configuration service to your controller, please use ImportConfigurationAutoInjectInterface and ImportConfigurationAutoInjectTrait.

namespaceApp\Controller;
useApp\Model\ImportConfiguration\UserImportConfiguration;
useJG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectInterface;
useJG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectTrait;
useJG\BatchEntityImportBundle\Controller\ImportControllerTrait;
useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;
useSymfony\Component\HttpFoundation\RedirectResponse;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\Annotation\Route;
useSymfony\Component\Validator\Validator\ValidatorInterface;
useSymfony\Contracts\Translation\TranslatorInterface;
class ImportController extends AbstractController implements ImportConfigurationAutoInjectInterface
{
use ImportControllerTrait;
use ImportConfigurationAutoInjectTrait;
/** * @Route("/user/import", name="user_import") */publicfunctionimport(Request$request, ValidatorInterface$validator): Response
{
return$this->doImport($request, $validator);
}
/** * @Route("/user/import/save", name="user_import_save") */publicfunctionimportSave(Request$request, TranslatorInterface$translator): Response
{
return$this->doImportSave($request, $translator);
}
protectedfunctionredirectToImport(): RedirectResponse
{
return$this->redirectToRoute('user_import');
}
protectedfunctiongetMatrixSaveActionUrl(): string
{
return$this->generateUrl('user_import_save');
}
protectedfunctiongetImportConfigurationClassName(): string
{
return UserImportConfiguration::class;
}
}

Translations

This bundle supports KnpLabs Translatable behavior.

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

  • name:en
  • description:pl
  • title:ru

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

Global templates

You have two ways to override templates globally:

  • Configuration - just change paths to templates in your configuration file. Values in this example are default ones and will be used if nothing will be change.
batch_entity_import:
templates:
select_file: '@BatchEntityImport/select_file.html.twig'edit_matrix: '@BatchEntityImport/edit_matrix.html.twig'layout: '@BatchEntityImport/layout.html.twig'
  • Bundle directory - put your templates in this directory:
templates/bundles/BatchEntityImportBundle

Controller-specific templates

If you have controller-specific templates, you can override them in controller:

protectedfunctiongetSelectFileTemplateName(): string
{
return'your/path/to/select_file.html.twig';
}
protectedfunctiongetMatrixEditTemplateName(): string
{
return'your/path/to/edit_matrix.html.twig';
}

Main layout

Block name used in templates is batch_entity_import_content, so probably there will be need to override it a bit. You can create a new file with content similar to the given example. Then just use it instead of original layout file.

{% extends path/to/your/layout.html.twig %}
{% blockyour_real_block_name %}
{% blockbatch_entity_import_content %}{% endblock %}
{% endblock %}

Then you just have to override it in bundle directory, or change a path to layout in your configuration.

Additional data

If you want to add some specific data to the rendered view, just override these methods in your controller:

protectedfunctionprepareSelectFileView(FormInterface$form): Response
{
return$this->prepareView(
$this->getSelectFileTemplateName(),
[
'form' => $form->createView(),
]
);
}
protectedfunctionprepareMatrixEditView(FormInterface$form, Matrix$matrix, bool$manualSubmit = false): Response
{
if ($manualSubmit) {
$this->manualSubmitMatrixForm($form, $matrix);
}
$configuration = $this->getImportConfiguration();
return$this->prepareView(
$this->getMatrixEditTemplateName(),
[
'header_info' => $matrix->getHeaderInfo($configuration->getEntityClassName()),
'data' => $matrix->getRecords(),
'form' => $form->createView(),
'importConfiguration' => $configuration,
]
);
}

About

Importing entities with preview and edit features for Symfony.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages