<?php
namespace App\EventSubscriber\Import\EntityMapper\Censorship;
use App\Application\EntityImportBundle\Event\EntityPreInitEvent;
use App\Entity\Product;
use App\Entity\ProductCensorship;
use App\Enum\AgeRatingSymbol;
use App\EventSubscriber\Import\EntityMapper\BasePreInitSubscriber;
/**
* Class CensorshipSubscriber.
*/
class CensorshipSubscriber extends BasePreInitSubscriber
{
/**
* {@inheritdoc}
*
* @uses \App\EventSubscriber\Import\EntityMapper\Censorship\CensorshipSubscriber::onEntityPreInit()
*/
public static function getSubscribedEvents()
{
return [
EntityPreInitEvent::class => 'onEntityPreInit',
];
}
/**
* Public callback responsible for initializing the censorship entity.
*
* @param \App\Application\EntityImportBundle\Event\EntityPreInitEvent $event
* The instance of the dispatched event
*/
public function onEntityPreInit(EntityPreInitEvent $event): void
{
if (ProductCensorship::class !== $event->getTargetClassName()) {
return;
}
$configuredProperties = $event->getConfigurationProperties();
// Look-up the "product" property. We need to find the product
// entity reference first, because it's mandatory in order to
// create or update the censorship entities.
// Since censorship collection can be managed only through
// a product, then this means - "no product = no censorship".
$productProperty = $this->filterConfiguredProperty('product', $configuredProperties, true);
if (null === $productProperty) {
$event->setSkipRowProcessing(true);
return;
}
// The product id cannot be obtained from the data array.
if (!$productId = (int) $this->getValue($productProperty, $event->getData())) {
$event->setSkipRowProcessing(true);
return;
}
// The product reference cannot be found in the database.
if ((!$product = $this->findRelatedProduct($productId)) || !$product instanceof Product) {
$event->setSkipRowProcessing(true);
return;
}
// Unable to process the data.
if (!$this->handleRow($product, $configuredProperties, $event->getData())) {
$event->setSkipRowProcessing(true);
return;
}
// All good, set the entity reference so it's accessible
// in the DataMapper service.
$event->setEntity($product);
}
/**
* Returns the product entity.
*
* @param int $id
* The entity ie
*
* @return Product|object|null
* Either null for invalid ID or the instance of the entity
*/
protected function findRelatedProduct(int $id): ?object
{
return $this->getEntityManager()
->getRepository(Product::class)
->find($id)
;
}
/**
* Responsible for processing the data.
*
* @param \App\Entity\Product $entity
* The product entity containing the censorship collection
* @param \App\Application\EntityImportBundle\ValueObject\ConfigItemPropertiesValue[] $configuredProperties
* The collection with configured properties
* @param array $data
* The parsed data array for the current row
*
* @return \App\Entity\Product|null
* Either the product entity to persist or null if error occurred
*/
protected function handleRow(
Product $entity,
array $configuredProperties,
array $data
): ?Product {
$countryProperty = $this->filterConfiguredProperty('country', $configuredProperties);
$contentProperty = $this->filterConfiguredProperty('content', $configuredProperties);
$ratingProperty = $this->filterConfiguredProperty('rating', $configuredProperties);
$ratingSymbolProperty = $this->filterConfiguredProperty('ratingSymbol', $configuredProperties);
// We need these properties in order to find specific collection element
// or create new one and attach it.
if (!$countryProperty || !$contentProperty || !$ratingProperty) {
return null;
}
$countryValue = $this->getValue($countryProperty, $data);
$contentValue = $this->getValue($contentProperty, $data);
$ratingValue = $this->getValue($ratingProperty, $data);
$ratingSymbolValue = $this->getValue($ratingSymbolProperty, $data);
if (!$countryValue || !$contentValue || !$ratingValue) {
return null;
}
if (!$censorship = $this->filterCensorshipElement($entity, $countryValue, $contentValue)) {
$censorship = new ProductCensorship();
$censorship->setProduct($entity);
$censorship->setContent($contentValue);
$censorship->setCountry($countryValue);
$entity->addCensorship($censorship);
}
$censorship->setRating($ratingValue);
if (!empty($ratingSymbolValue) && ProductCensorship::COUNTRY_FI === $countryValue) {
$symbols = [];
foreach (explode(',', $ratingSymbolValue) as $symbolChoice) {
if (AgeRatingSymbol::accepts($symbolChoice)) {
$symbols[] = $symbolChoice;
}
}
$censorship->setRatingSymbol($symbols);
}
$this->getEntityManager()->persist($censorship);
return $entity;
}
/**
* Responsible for filtering the collection with given values.
*
* @param \App\Entity\Product $entity
* The instance of the product entity
* @param string $countryValue
* The selected country
* @param string $contentValue
* The selected content type
*
* @return \App\Entity\ProductCensorship|null
* Either null of no element match the criteria or the censorship item
*/
protected function filterCensorshipElement(
Product $entity,
string $countryValue,
string $contentValue
): ?ProductCensorship {
// Important - the management form allows for a user to create
// multiple elements with the same options. While, this should
// never be a case, you would never know what a user might do.
// And this is why here, we don't care if such case exists or not.
// THe moment we find an element matching the criteria, we ignore the rest.
if ($entity->getCensorship()->isEmpty()) {
return null;
}
$censorshipElement = null;
/** @var \App\Entity\ProductCensorship $element */
foreach ($entity->getCensorship() as $element) {
if ($countryValue === $element->getCountry() && $contentValue === $element->getContent()) {
$censorshipElement = $element;
break;
}
}
return $censorshipElement;
}
}