diff --git a/composer.lock b/composer.lock
index 322a6bc9..47067177 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4955,16 +4955,16 @@
},
{
"name": "symfony/http-client-contracts",
- "version": "v1.1.7",
+ "version": "v1.1.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
- "reference": "353b2a3e907e5c34cf8f74827a4b21eb745aab1d"
+ "reference": "088bae75cfa2ec5eb6d33dce17dbd8613150ce6e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/353b2a3e907e5c34cf8f74827a4b21eb745aab1d",
- "reference": "353b2a3e907e5c34cf8f74827a4b21eb745aab1d",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/088bae75cfa2ec5eb6d33dce17dbd8613150ce6e",
+ "reference": "088bae75cfa2ec5eb6d33dce17dbd8613150ce6e",
"shasum": ""
},
"require": {
@@ -5008,7 +5008,7 @@
"interoperability",
"standards"
],
- "time": "2019-09-26T22:09:58+00:00"
+ "time": "2019-11-07T12:44:51+00:00"
},
{
"name": "symfony/http-foundation",
@@ -6566,16 +6566,16 @@
},
{
"name": "symfony/service-contracts",
- "version": "v1.1.7",
+ "version": "v1.1.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0"
+ "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0",
- "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
+ "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
"shasum": ""
},
"require": {
@@ -6620,7 +6620,7 @@
"interoperability",
"standards"
],
- "time": "2019-09-17T11:12:18+00:00"
+ "time": "2019-10-14T12:27:06+00:00"
},
{
"name": "symfony/stopwatch",
diff --git a/src/Controller/AdminPages/BaseAdminController.php b/src/Controller/AdminPages/BaseAdminController.php
index fe545f1e..f2901087 100644
--- a/src/Controller/AdminPages/BaseAdminController.php
+++ b/src/Controller/AdminPages/BaseAdminController.php
@@ -199,16 +199,21 @@ abstract class BaseAdminController extends AbstractController
if ($mass_creation_form->isSubmitted() && $mass_creation_form->isValid()) {
$data = $mass_creation_form->getData();
- dump($data);
-
//Create entries based on input
- $errors = $importer->massCreation($data['lines'], $this->entity_class, $data['parent']);
+ $errors = [];
+ $results = $importer->massCreation($data['lines'], $this->entity_class, $data['parent'], $errors);
//Show errors to user:
- foreach ($errors as $name => $error) {
- /* @var $error ConstraintViolationList */
- $this->addFlash('error', $name.':'.$error);
+ foreach ($errors as $error) {
+ dump($error);
+ $this->addFlash('error', $error['entity']->getFullPath().':'.$error['violations']);
}
+
+ //Persist valid entities to DB
+ foreach ($results as $result) {
+ $em->persist($result);
+ }
+ $em->flush();
}
return $this->render($this->twig_template, [
diff --git a/src/Services/ElementTypeNameGenerator.php b/src/Services/ElementTypeNameGenerator.php
index fba29366..275554d3 100644
--- a/src/Services/ElementTypeNameGenerator.php
+++ b/src/Services/ElementTypeNameGenerator.php
@@ -111,14 +111,15 @@ class ElementTypeNameGenerator
* @param bool $use_html If set to true, a html string is returned, where the type is set italic
*
* @return string The localized string
+ * @throws EntityNotSupportedException When the passed entity is not supported.
*/
public function getTypeNameCombination(NamedDBElement $entity, bool $use_html = false): string
{
$type = $this->getLocalizedTypeLabel($entity);
if ($use_html) {
- return ''.$type.': '.$entity->getName();
+ return ''.$type.': '.htmlspecialchars($entity->getName());
}
- return $type.': '.htmlspecialchars($entity->getName());
+ return $type.': '.$entity->getName();
}
}
diff --git a/src/Services/EntityImporter.php b/src/Services/EntityImporter.php
index a12b958a..0f056cb7 100644
--- a/src/Services/EntityImporter.php
+++ b/src/Services/EntityImporter.php
@@ -21,6 +21,7 @@
namespace App\Services;
+use App\Entity\Base\DBElement;
use App\Entity\Base\StructuralDBElement;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\File\File;
@@ -44,7 +45,7 @@ class EntityImporter
protected function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
- 'csv_separator' => ';',
+ 'csv_separator' => ';',
'format' => 'json',
'preserve_children' => true,
'parent' => null,
@@ -53,24 +54,37 @@ class EntityImporter
}
/**
- * Creates many entries at once, based on a (text) list of names.
+ * Creates many entries at once, based on a (text) list of name.
+ * The created enties are not persisted to database yet, so you have to do it yourself.
*
* @param string $lines The list of names seperated by \n
* @param string $class_name The name of the class for which the entities should be created
* @param StructuralDBElement|null $parent The element which will be used as parent element for new elements.
+ * @param array $errors An associative array containing all validation errors.
*
- * @return array An associative array containing an ConstraintViolationList and the entity name as key are returned,
- * if an error happened during validation. When everything was successfull, the array should be empty.
+ * @return StructuralDBElement[] An array containing all valid imported entities (with the type $class_name)
*/
- public function massCreation(string $lines, string $class_name, ?StructuralDBElement $parent): array
+ public function massCreation(string $lines, string $class_name, ?StructuralDBElement $parent = null, array &$errors = []): array
{
//Expand every line to a single entry:
$names = explode("\n", $lines);
+ if (!is_a($class_name, StructuralDBElement::class, true)) {
+ throw new \InvalidArgumentException('$class_name must be a StructuralDBElement type!');
+ }
+ if ($parent !== null && !is_a($parent, $class_name)) {
+ throw new \InvalidArgumentException('$parent must have the same type as specified in $class_name!');
+ }
+
$errors = [];
+ $valid_entities = [];
foreach ($names as $name) {
$name = trim($name);
+ if ($name === '') {
+ //Skip empty lines (StrucuralDBElements must have a name)
+ continue;
+ }
/** @var $entity StructuralDBElement */
//Create new element with given name
$entity = new $class_name();
@@ -81,17 +95,13 @@ class EntityImporter
$tmp = $this->validator->validate($entity);
//If no error occured, write entry to DB:
if (0 === \count($tmp)) {
- $this->em->persist($entity);
+ $valid_entities[] = $entity;
} else { //Otherwise log error
- dump($tmp);
- $errors[$entity->getFullPath()] = $tmp;
+ $errors[] = ['entity' => $entity, 'violations' => $tmp];
}
}
- //Save changes to database
- $this->em->flush();
-
- return $errors;
+ return $valid_entities;
}
/**
diff --git a/tests/Services/ElementTypeNameGeneratorTest.php b/tests/Services/ElementTypeNameGeneratorTest.php
new file mode 100644
index 00000000..12b2b968
--- /dev/null
+++ b/tests/Services/ElementTypeNameGeneratorTest.php
@@ -0,0 +1,89 @@
+service = self::$container->get(ElementTypeNameGenerator::class);
+ }
+
+ public function testGetLocalizedTypeNameCombination()
+ {
+ //We only test in english
+ $this->assertEquals('Part', $this->service->getLocalizedTypeLabel(new Part()));
+ $this->assertEquals('Category', $this->service->getLocalizedTypeLabel(new Category()));
+
+ //Test inheritance
+ $this->assertEquals('Attachment', $this->service->getLocalizedTypeLabel(new PartAttachment()));
+
+ //Test exception for unknpwn type
+ $this->expectException(EntityNotSupportedException::class);
+ $this->service->getLocalizedTypeLabel(new class extends DBElement {
+ public function getIDString(): string
+ {
+ return 'Stub';
+ }
+ });
+ }
+
+ public function testGetTypeNameCombination()
+ {
+ $part = new Part();
+ $part->setName('TestassertEquals('Part: Testservice->getTypeNameCombination($part, false));
+
+ $this->assertEquals('Part: Test<Part', $this->service->getTypeNameCombination($part, true));
+
+ //Test exception
+ $this->expectException(EntityNotSupportedException::class);
+ $this->service->getTypeNameCombination(new class extends NamedDBElement {
+ public function getIDString(): string
+ {
+ return 'Stub';
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/tests/Services/EntityImporterTest.php b/tests/Services/EntityImporterTest.php
new file mode 100644
index 00000000..36a4912c
--- /dev/null
+++ b/tests/Services/EntityImporterTest.php
@@ -0,0 +1,84 @@
+service = self::$container->get(EntityImporter::class);
+ }
+
+ public function testMassCreationResults()
+ {
+ $errors = [];
+ $results = $this->service->massCreation('', AttachmentType::class, null, $errors);
+ $this->assertEmpty($results);
+ $this->assertEmpty($errors);
+
+ $errors = [];
+ $lines = "Test 1 \n Test 2 \n Test 3";
+ $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors);
+ $this->assertCount(0, $errors);
+ $this->assertCount(3, $results);
+ //Check type
+ $this->assertInstanceOf(AttachmentType::class, $results[0]);
+ //Check names
+ $this->assertEquals('Test 1', $results[0]->getName());
+ $this->assertEquals('Test 2', $results[1]->getName());
+ //Check parent
+ $this->assertNull($results[0]->getMasterPictureAttachment());
+
+ $parent = new AttachmentType();
+ $results = $this->service->massCreation($lines, AttachmentType::class, $parent, $errors);
+ $this->assertCount(3, $results);
+ $this->assertEquals($parent, $results[0]->getParent());
+ }
+
+ public function testMassCreationErrors()
+ {
+ $errors = [];
+ //Node 1 and Node 2 are created in datafixtures, so their attemp to create them again must fail.
+ $lines = "Test 1 \n Node 1 \n Node 2";
+ $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors);
+ $this->assertCount(1, $results);
+ $this->assertEquals('Test 1', $results[0]->getName());
+ $this->assertCount(2, $errors);
+ $this->assertEquals('Node 1', $errors[0]['entity']->getName());
+ }
+}
\ No newline at end of file