Allow to automatically find or create entities from database based on info providers

This commit is contained in:
Jan Böhmer 2023-07-12 23:43:16 +02:00
parent f9bce3dfdb
commit 6cd9640b30
4 changed files with 118 additions and 1 deletions

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
*/
namespace App\Form\Type\Helper;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Repository\StructuralDBElementRepository;
use App\Services\Trees\NodesListBuilder;
use Doctrine\ORM\EntityManagerInterface;
@ -32,13 +33,21 @@ class StructuralEntityChoiceLoader extends AbstractChoiceLoader
{
private ?string $additional_element = null;
private ?AbstractStructuralDBElement $starting_element = null;
public function __construct(private readonly Options $options, private readonly NodesListBuilder $builder, private readonly EntityManagerInterface $entityManager)
{
}
protected function loadChoices(): iterable
{
//If the starting_element is set and not persisted yet, add it to the list
if ($this->starting_element !== null && $this->starting_element->getID() === null) {
$tmp = [$this->starting_element];
} else {
$tmp = [];
}
if ($this->additional_element) {
$tmp = $this->createNewEntitiesFromValue($this->additional_element);
$this->additional_element = null;
@ -86,4 +95,23 @@ class StructuralEntityChoiceLoader extends AbstractChoiceLoader
return $this->additional_element;
}
/**
* @return AbstractStructuralDBElement|null
*/
public function getStartingElement(): ?AbstractStructuralDBElement
{
return $this->starting_element;
}
/**
* @param AbstractStructuralDBElement|null $starting_element
* @return StructuralEntityChoiceLoader
*/
public function setStartingElement(?AbstractStructuralDBElement $starting_element): StructuralEntityChoiceLoader
{
$this->starting_element = $starting_element;
return $this;
}
}

View file

@ -122,6 +122,11 @@ class StructuralEntityType extends AbstractType
public function modelTransform($value, array $options)
{
$choice_loader = $options['choice_loader'];
if ($choice_loader instanceof StructuralEntityChoiceLoader) {
$choice_loader->setStartingElement($value);
}
return $value;
}

View file

@ -185,4 +185,69 @@ class StructuralDBElementRepository extends NamedDBElementRepository
return $result;
}
/**
* Finds the element with the given name for the use with the InfoProvider System
* The name search is a bit more fuzzy than the normal findByName, because it is case-insensitive and ignores special characters.
* Also, it will try to find the element using the additional names field, of the elements.
* @param string $name
* @return AbstractStructuralDBElement|null
*/
public function findForInfoProvider(string $name): ?AbstractStructuralDBElement
{
//First try to find the element by name
$qb = $this->createQueryBuilder('e');
//Use lowercase conversion to be case-insensitive
$qb->where($qb->expr()->like('LOWER(e.name)', 'LOWER(:name)'));
$qb->setParameter('name', $name);
$result = $qb->getQuery()->getResult();
if (count($result) === 1) {
return $result[0];
}
/*//If we have no result, try to find the element by additional names
$qb = $this->createQueryBuilder('e');
//Use lowercase conversion to be case-insensitive
$qb->where($qb->expr()->like('LOWER(e.additional_names)', 'LOWER(:name)'));
$qb->setParameter('name', '%'.$name.'%');
$result = $qb->getQuery()->getResult();
if (count($result) === 1) {
return $result[0];
}*/
//If we find nothing, return null
return null;
}
/**
* Similar to findForInfoProvider, but will create a new element with the given name if none was found.
* @param string $name
* @return AbstractStructuralDBElement
*/
public function findOrCreateForInfoProvider(string $name): AbstractStructuralDBElement
{
$entity = $this->findForInfoProvider($name);
if (null === $entity) {
//Try to find if we already have an element cached for this name
$entity = $this->getNewEntityFromCache($name, null);
if ($entity) {
return $entity;
}
$class = $this->getClassName();
/** @var AbstractStructuralDBElement $entity */
$entity = new $class;
$entity->setName($name);
$this->setNewEntityToCache($entity);
}
return $entity;
}
}

View file

@ -25,13 +25,16 @@ namespace App\Services\InfoProviderSystem;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Entity\Parameters\AbstractParameter;
use App\Entity\Parameters\PartParameter;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\ManufacturingStatus;
use App\Entity\Parts\Part;
use App\Services\InfoProviderSystem\DTOs\FileDTO;
use App\Services\InfoProviderSystem\DTOs\ParameterDTO;
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use Doctrine\ORM\EntityManagerInterface;
/**
* This class converts DTOs to entities which can be persisted in the DB
@ -39,6 +42,10 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
class DTOtoEntityConverter
{
public function __construct(private readonly EntityManagerInterface $em)
{
}
public function convertParameter(ParameterDTO $dto, PartParameter $entity = new PartParameter()): PartParameter
{
$entity->setName($dto->name);
@ -79,6 +86,8 @@ class DTOtoEntityConverter
$entity->setDescription($dto->description ?? '');
$entity->setComment($dto->notes ?? '');
$entity->setManufacturer($this->getOrCreateEntity(Manufacturer::class, $dto->manufacturer));
$entity->setManufacturerProductNumber($dto->mpn ?? '');
$entity->setManufacturingStatus($dto->manufacturing_status ?? ManufacturingStatus::NOT_SET);
@ -95,4 +104,14 @@ class DTOtoEntityConverter
return $entity;
}
private function getOrCreateEntity(string $class, ?string $name): ?AbstractStructuralDBElement
{
//Fall through to make converting easier
if ($name === null) {
return null;
}
return $this->em->getRepository($class)->findOrCreateForInfoProvider($name);
}
}