mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-07-15 12:54:30 +02:00
Show when parts from info provider already exist (#810)
* added button to show existing part with same manufacturer and mpn in provider list * added button to edit existing part in provider list * added docstring and comments * replaced unnecessary double quotes * Introduced a new twig variable localPart to split up the result * Highlight a row, if the part is already existing * Made buttons translatable * Improved styling of the buttons and added a badge to show a hint * Extracted database queries for part matching into its own service and optimized the query reducing the required queries by factor 2 * Allow to find existing parts via the stored providerReference This should allow the database to more quickly find entries * Allow to use part name and manufacturer alternative names for mapping * Added a button to update a local part from the info provider and moved some buttons into dropdown menu --------- Co-authored-by: jona <a@b.c> Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
parent
e9efbff912
commit
92e4976396
4 changed files with 195 additions and 28 deletions
|
@ -23,10 +23,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Form\InfoProviderSystem\PartSearchType;
|
||||
use App\Services\InfoProviderSystem\ExistingPartFinder;
|
||||
use App\Services\InfoProviderSystem\PartInfoRetriever;
|
||||
use App\Services\InfoProviderSystem\ProviderRegistry;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
@ -42,7 +45,9 @@ class InfoProviderController extends AbstractController
|
|||
{
|
||||
|
||||
public function __construct(private readonly ProviderRegistry $providerRegistry,
|
||||
private readonly PartInfoRetriever $infoRetriever)
|
||||
private readonly PartInfoRetriever $infoRetriever,
|
||||
private readonly ExistingPartFinder $existingPartFinder
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -79,14 +84,26 @@ class InfoProviderController extends AbstractController
|
|||
$keyword = $form->get('keyword')->getData();
|
||||
$providers = $form->get('providers')->getData();
|
||||
|
||||
$dtos = [];
|
||||
|
||||
try {
|
||||
$results = $this->infoRetriever->searchByKeyword(keyword: $keyword, providers: $providers);
|
||||
$dtos = $this->infoRetriever->searchByKeyword(keyword: $keyword, providers: $providers);
|
||||
} catch (ClientException $e) {
|
||||
$this->addFlash('error', t('info_providers.search.error.client_exception'));
|
||||
$this->addFlash('error',$e->getMessage());
|
||||
//Log the exception
|
||||
$exceptionLogger->error('Error during info provider search: ' . $e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
|
||||
// modify the array to an array of arrays that has a field for a matching local Part
|
||||
// the advantage to use that format even when we don't look for local parts is that we
|
||||
// always work with the same interface
|
||||
$results = array_map(function ($result) {return ['dto' => $result, 'localPart' => null];}, $dtos);
|
||||
if(!$update_target) {
|
||||
foreach ($results as $index => $result) {
|
||||
$results[$index]['localPart'] = $this->existingPartFinder->findFirstExisting($result['dto']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('info_providers/search/part_search.html.twig', [
|
||||
|
|
77
src/Services/InfoProviderSystem/ExistingPartFinder.php
Normal file
77
src/Services/InfoProviderSystem/ExistingPartFinder.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\InfoProviderSystem;
|
||||
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* This service assists in finding existing local parts for a SearchResultDTO, so that the user
|
||||
* does not accidentally add a duplicate.
|
||||
*
|
||||
* A part is considered to be a duplicate, if the provider reference matches, or if the manufacturer and the MPN of the
|
||||
* DTO and the local part match. This checks also for alternative names of the manufacturer and the part name (as alternative
|
||||
* for the MPN).
|
||||
*/
|
||||
final class ExistingPartFinder
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $em)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first existing local part, that matches the search result.
|
||||
* If no part is found, return null.
|
||||
* @param SearchResultDTO $dto
|
||||
* @return Part|null
|
||||
*/
|
||||
public function findFirstExisting(SearchResultDTO $dto): ?Part
|
||||
{
|
||||
$results = $this->findAllExisting($dto);
|
||||
return count($results) > 0 ? $results[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all existing local parts that match the search result.
|
||||
* If no part is found, return an empty array.
|
||||
* @param SearchResultDTO $dto
|
||||
* @return Part[]
|
||||
*/
|
||||
public function findAllExisting(SearchResultDTO $dto): array
|
||||
{
|
||||
$qb = $this->em->getRepository(Part::class)->createQueryBuilder('part');
|
||||
$qb->select('part')
|
||||
->leftJoin('part.manufacturer', 'manufacturer')
|
||||
->Orwhere($qb->expr()->andX(
|
||||
'part.providerReference.provider_key = :providerKey',
|
||||
'part.providerReference.provider_id = :providerId',
|
||||
))
|
||||
|
||||
//Or the manufacturer (allowing for alternative names) and the MPN (or part name) must match
|
||||
->OrWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->orX(
|
||||
"ILIKE(manufacturer.name, :manufacturerName) = TRUE",
|
||||
"ILIKE(manufacturer.alternative_names, :manufacturerAltNames) = TRUE",
|
||||
),
|
||||
$qb->expr()->orX(
|
||||
"ILIKE(part.manufacturer_product_number, :mpn) = TRUE",
|
||||
"ILIKE(part.name, :mpn) = TRUE",
|
||||
)
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
$qb->setParameter('providerKey', $dto->provider_key);
|
||||
$qb->setParameter('providerId', $dto->provider_id);
|
||||
|
||||
$qb->setParameter('manufacturerName', $dto->manufacturer);
|
||||
$qb->setParameter('manufacturerAltNames', '%'.$dto->manufacturer.'%');
|
||||
$qb->setParameter('mpn', $dto->mpn);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue