-
Notifications
You must be signed in to change notification settings - Fork 990
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NEXT-25734 - Allow foreign key resolving by different unique constrai…
…nt in sync api calls
- Loading branch information
1 parent
7ab5b44
commit cc5c28a
Showing
14 changed files
with
443 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Content\Product\Api; | ||
|
||
use Doctrine\DBAL\ArrayParameterType; | ||
use Doctrine\DBAL\Connection; | ||
use Shopware\Core\Defaults; | ||
use Shopware\Core\Framework\Api\Sync\AbstractFkResolver; | ||
use Shopware\Core\Framework\Api\Sync\FkReference; | ||
use Shopware\Core\Framework\Log\Package; | ||
use Shopware\Core\Framework\Uuid\Uuid; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Package('core')] | ||
class ProductNumberFkResolver extends AbstractFkResolver | ||
{ | ||
public function __construct(private readonly Connection $connection) | ||
{ | ||
} | ||
|
||
public static function getName(): string | ||
{ | ||
return 'product.number'; | ||
} | ||
|
||
/** | ||
* @param array<FkReference> $map | ||
* | ||
* @return array<FkReference> | ||
*/ | ||
public function resolve(array $map): array | ||
{ | ||
$numbers = \array_map(fn ($id) => $id->value, $map); | ||
|
||
$numbers = \array_filter(\array_unique($numbers)); | ||
|
||
if (empty($numbers)) { | ||
return $map; | ||
} | ||
|
||
$hash = $this->connection->fetchAllKeyValue( | ||
'SELECT product_number, LOWER(HEX(id)) FROM product WHERE product_number IN (:numbers) AND version_id = :version', | ||
['numbers' => $numbers, 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)], | ||
['numbers' => ArrayParameterType::STRING] | ||
); | ||
|
||
foreach ($map as $reference) { | ||
$reference->resolved = $hash[$reference->value]; | ||
} | ||
|
||
return $map; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Framework\Api\Sync; | ||
|
||
use Shopware\Core\Framework\Log\Package; | ||
|
||
#[Package('core')] | ||
abstract class AbstractFkResolver | ||
{ | ||
/** | ||
* Returns the unique name for the resolver which is used to identify for fk resolving hash map | ||
*/ | ||
abstract public static function getName(): string; | ||
|
||
/** | ||
* @param array<FkReference> $map | ||
* | ||
* @return array<FkReference> | ||
*/ | ||
abstract public function resolve(array $map): array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Framework\Api\Sync; | ||
|
||
use Shopware\Core\Framework\Log\Package; | ||
|
||
/** | ||
* @final | ||
*/ | ||
#[Package('core')] | ||
class FkReference | ||
{ | ||
public ?string $resolved = null; | ||
|
||
public function __construct(public mixed $value) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Framework\Api\Sync; | ||
|
||
use Shopware\Core\Framework\Api\ApiException; | ||
use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToOneAssociationField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToManyAssociationField; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField; | ||
use Shopware\Core\Framework\Log\Package; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[Package('core')] | ||
class SyncFkResolver | ||
{ | ||
/** | ||
* @internal | ||
* | ||
* @param iterable<AbstractFkResolver> $resolvers | ||
*/ | ||
public function __construct( | ||
private readonly DefinitionInstanceRegistry $registry, | ||
private readonly iterable $resolvers | ||
) { | ||
} | ||
|
||
/** | ||
* @param array<int, array<string, mixed>> $payload | ||
* | ||
* @return array<int, array<string, mixed>> | ||
*/ | ||
public function resolve(string $entity, array $payload): array | ||
{ | ||
$map = $this->collect($entity, $payload); | ||
|
||
if (empty($map)) { | ||
return $payload; | ||
} | ||
|
||
foreach ($map as $key => &$values) { | ||
$values = $this->getResolver($key)->resolve($values); | ||
} | ||
|
||
\array_walk_recursive($payload, function (&$value): void { | ||
$value = $value instanceof FkReference ? $value->resolved : $value; | ||
}); | ||
|
||
return $payload; | ||
} | ||
|
||
/** | ||
* @param array<int, array<string, mixed>> $payload | ||
* | ||
* @return array<string, array<FkReference>> | ||
*/ | ||
private function collect(string $entity, array &$payload): array | ||
{ | ||
$definition = $this->registry->getByEntityName($entity); | ||
|
||
$map = []; | ||
foreach ($payload as &$row) { | ||
foreach ($row as $key => &$value) { | ||
if (\is_array($value) && isset($value['resolver']) && isset($value['value'])) { | ||
$definition = $this->registry->getByEntityName($entity); | ||
|
||
$field = $definition->getField($key); | ||
|
||
$ref = match (true) { | ||
$field instanceof FkField => $field->getReferenceDefinition()->getEntityName(), | ||
$field instanceof IdField => $entity, | ||
default => null | ||
}; | ||
|
||
if ($ref === null) { | ||
continue; | ||
} | ||
|
||
$resolver = (string) $value['resolver']; | ||
|
||
$row[$key] = $reference = new FkReference($value['value']); | ||
|
||
$map[$resolver][] = $reference; | ||
} | ||
|
||
if (\is_array($value)) { | ||
$field = $definition->getField($key); | ||
|
||
if (!$field instanceof AssociationField) { | ||
continue; | ||
} | ||
|
||
$nested = []; | ||
if ($field instanceof ManyToManyAssociationField || $field instanceof OneToManyAssociationField) { | ||
$ref = $field instanceof ManyToManyAssociationField ? $field->getToManyReferenceDefinition()->getEntityName() : $field->getReferenceDefinition()->getEntityName(); | ||
$nested = $this->collect($ref, $value); | ||
} elseif ($field instanceof ManyToOneAssociationField || $field instanceof OneToOneAssociationField) { | ||
$tmp = [$value]; | ||
$nested = $this->collect($field->getReferenceDefinition()->getEntityName(), $tmp); | ||
$value = \array_shift($tmp); | ||
} | ||
|
||
$map = $this->merge($map, $nested); | ||
} | ||
} | ||
} | ||
|
||
return $map; | ||
} | ||
|
||
/** | ||
* @param array<string, array<FkReference>> $map | ||
* @param array<string, array<FkReference>> $nested | ||
* | ||
* @return array<string, array<FkReference>> | ||
*/ | ||
private function merge(array $map, array $nested): array | ||
{ | ||
foreach ($nested as $resolver => $values) { | ||
foreach ($values as $value) { | ||
$map[$resolver][] = $value; | ||
} | ||
} | ||
|
||
return $map; | ||
} | ||
|
||
private function getResolver(string $key): AbstractFkResolver | ||
{ | ||
foreach ($this->resolvers as $resolver) { | ||
if ($resolver::getName() === $key) { | ||
return $resolver; | ||
} | ||
} | ||
|
||
throw ApiException::resolverNotFoundException($key); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.