Repository URL to install this package:
|
Version:
6.2.1 ▾
|
<?php
namespace DigitalAscetic\EntityAssociationsBundle\Service;
use DigitalAscetic\EntityAssociationsBundle\Entity\ExceptionEntityAssociation;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class EntityAssociationsService implements EntityAssociationsServiceInterface
{
/**
* @var LoggerInterface $logger
*/
protected $logger;
/**
* @var EntityManager $em
*/
protected $em;
/**
* @var ContainerInterface $container
*/
protected $container;
/**
* EntityAssociationsService constructor.
*
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container, EntityManager $em, LoggerInterface $logger)
{
$this->container = $container;
$this->logger = $logger;
$this->em = $em;
}
public function checkEntityAssociations($entity)
{
$clazz = ClassUtils::getClass($entity);
if ($this->container->hasParameter('digital_ascetic_entity_associations.exclusions')) {
// WE GET EXCLUSIONES ENTITIES
$da_entity_associations_exclusions = $this->container->getParameter('digital_ascetic_entity_associations.exclusions');
// IF CURRENT ENTITY IS AN EXCLUDED ENTITY, WE DO NOTHING
if (in_array($clazz, $da_entity_associations_exclusions)) {
return false;
}
}
$classesMappings = array();
// WE GET PARAMS WITH CHECKING ENTITIES
$da_entity_associations_entities = $this->container->getParameter('digital_ascetic_entity_associations.entities');
$resAssociations = array();
//WE SAVE ON ARRAY THE ASSOCIATIONS MAPPING WITH SPECIFIED ENTITY
// WE ONLY MAP ASSOCIATIONS FOR THOSE CLASS WE HAVE DEFINED INTO CONFIG.YML
foreach ($da_entity_associations_entities as $className => $properties) {
if (array_key_exists('service', $properties)) {
/** @var EntityAssociationsServiceInterface $entityService */
$entityService = $this->container->get($properties['service']);
if ($entityService instanceof EntityAssociationsServiceInterface) {
try {
$entityService->checkEntityAssociations($entity);
} catch (ExceptionEntityAssociation $err) {
$resAssociations = array_merge($resAssociations, $err->getAssociations());
}
} else {
throw new \Exception('Your service: ' . $properties['service'] . ' must implements EntityAssociationsServiceInterface');
}
} else {
$metadata = $this->em->getClassMetadata($className);
$classMappings = $metadata->getAssociationMappings();
foreach ($classMappings as $mapping) {
if ($mapping['targetEntity'] === $clazz) {
$classesMappings[$className][] = $mapping['fieldName'];
}
}
}
}
foreach ($classesMappings as $className => $properties) {
$entityReflectionClass = ClassUtils::newReflectionClass($className);
$entityShortName = $entityReflectionClass->getShortName();
$repo = $this->em->getRepository($className);
// WE NEED TO CHECK IF WE HAVE AT LEAST ONE ENTITY ASSOCIATION ACTIVED
$qb = $repo->createQueryBuilder($entityShortName)
->select('COUNT(' . $entityShortName . '.id)')
->setMaxResults(1);
// IF WE HAVE RULES FOR THE CLASS WE ARE CHECKING,
// WE ONLY CHECK THE PROPERTY_CHECK DEFINED INTO CONFIG.YML
if (array_key_exists('check_rules', $da_entity_associations_entities[$className]) &&
array_key_exists($clazz, $da_entity_associations_entities[$className]['check_rules'])) {
$rules = $da_entity_associations_entities[$className]['check_rules'];
$qb->andWhere($entityShortName . '.' . $rules[$clazz]['property'] . " = :entity")
->setParameter('entity', $entity);
} else {
$or = $qb->expr()->orX();
foreach ($properties as $key => $property) {
$prop = "part" . $key;
$or->add($entityShortName . "." . $property . " = :" . $prop);
$qb->setParameter($prop, $entity);
}
$qb->andWhere($or);
}
$validate_field = array_key_exists('validate_field', $da_entity_associations_entities[$className]) ? $da_entity_associations_entities[$className]['validate_field'] : null;
$activeValue = array_key_exists('is_active_value', $da_entity_associations_entities[$className]) ? $da_entity_associations_entities[$className]['is_active_value'] : null;
if (array_key_exists($className, $da_entity_associations_entities) &&
isset($validate_field)) {
$fields = explode('.', $validate_field);
$fieldsToValidateIterator = new \CachingIterator(new \ArrayIterator($fields));
$currentEntityShortName = $entityShortName;
foreach ($fieldsToValidateIterator as $fieldToValidate) {
if ($fieldsToValidateIterator->hasNext()) {
$newEntityAlias = $currentEntityShortName . strtoupper($fieldToValidate);
// WE NEED TO DO A JOIN QUERY
$qb->leftJoin($currentEntityShortName . '.' . $fieldToValidate, $newEntityAlias);
$currentEntityShortName = $newEntityAlias;
} else {
//IF IS NULL WE NEED TO DECLARE SPECIFIC WHERE CLOSURE
if (is_null($activeValue)) {
$qb->andWhere($currentEntityShortName . '.' . $fieldToValidate . " is NULL");
} else {
$qb->andWhere($currentEntityShortName . '.' . $fieldToValidate . " = :activeValue")
->setParameter('activeValue', $activeValue);
}
}
}
}
$res = $qb->getQuery()->getSingleScalarResult();
if ($res > 0) {
//IF WE NOT HAVE MAPPED ENTITY CLASS, WE DO IT
if (!in_array($className, $resAssociations)) {
$resAssociations[] = $className;
}
}
}
if (count($resAssociations)) {
throw new ExceptionEntityAssociation($resAssociations);
}
return false;
}
public function replaceEntityAssociations($fromEntity, $toEntity)
{
$clazzFromEntity = ClassUtils::getClass($fromEntity);
$clazzToEntity = ClassUtils::getClass($toEntity);
if ($clazzFromEntity != $clazzToEntity) {
throw new \Exception('The Class of two entities must be the same.');
}
if ($this->container->hasParameter('digital_ascetic_entity_associations.exclusions')) {
// WE GET EXCLUSIONES ENTITIES
$da_entity_associations_exclusions = $this->container->getParameter('digital_ascetic_entity_associations.exclusions');
// IF CURRENT ENTITY IS AN EXCLUDED ENTITY, WE DO NOTHING
if (in_array($clazzFromEntity, $da_entity_associations_exclusions)) {
return false;
}
}
$classesMappings = array();
// WE GET PARAMS WITH CHECKING ENTITIES
$da_entity_associations_entities = $this->container->getParameter('digital_ascetic_entity_associations.entities');
//WE SAVE ON ARRAY THE ASSOCIATIONS MAPPING WITH SPECIFIED ENTITY
// WE ONLY MAP ASSOCIATIONS FOR THOSE CLASS WE HAVE DEFINED INTO CONFIG.YML
foreach ($da_entity_associations_entities as $className => $properties) {
if (array_key_exists('service', $properties)) {
/** @var EntityAssociationsServiceInterface $entityService */
$entityService = $this->container->get($properties['service']);
if ($entityService instanceof EntityAssociationsServiceInterface) {
$entityService->replaceEntityAssociations($fromEntity, $toEntity);
} else {
throw new \Exception('Your service: ' . $properties['service'] . ' must implements EntityAssociationsServiceInterface');
}
} else {
$metadata = $this->em->getClassMetadata($className);
$classMappings = $metadata->getAssociationMappings();
foreach ($classMappings as $mapping) {
if ($mapping['targetEntity'] === $clazzFromEntity) {
$classesMappings[$className][] = $mapping['fieldName'];
}
}
}
}
foreach ($classesMappings as $className => $properties) {
$entityReflectionClass = ClassUtils::newReflectionClass($className);
$entityShortName = $entityReflectionClass->getShortName();
$joins = array();
$repo = $this->em->getRepository($className);
// WE NEED TO CHECK IF WE HAVE AT LEAST ONE ENTITY ASSOCIATION ACTIVED
$qb = $repo->createQueryBuilder($entityShortName)
->update();
// IF WE HAVE UPDATE_RULES WE UPDATE ENTITY FOLLOWING CONFIG.YML; ELSE WE UPDATE WITH THE $toEntity PARAMETER
if (array_key_exists('update_rules', $da_entity_associations_entities[$className]) &&
array_key_exists($clazzFromEntity, $da_entity_associations_entities[$className]['update_rules'])) {
$rules = $da_entity_associations_entities[$className]['update_rules'];
if (isset($rules[$clazzFromEntity]['value'])) {
$qb->set($entityShortName . '.' . $rules[$clazzFromEntity]['property'], $rules[$clazzFromEntity]['value']);
} else {
$qb->set($entityShortName . '.' . $rules[$clazzFromEntity]['property'], ':toEntity')
->setParameter('toEntity', $toEntity);
}
} else {
foreach ($properties as $property) {
$qb->set($entityShortName . '.' . $property, ':toEntity')
->setParameter('toEntity', $toEntity);
}
}
// IF WE HAVE UPDATE RULES FOR THE CLASS WE ARE UPDATING,
if (array_key_exists('check_rules', $da_entity_associations_entities[$className]) &&
array_key_exists($clazzFromEntity, $da_entity_associations_entities[$className]['check_rules'])) {
$rules = $da_entity_associations_entities[$className]['check_rules'];
$qb->andWhere($entityShortName . '.' . $rules[$clazzFromEntity]['property'] . " = :fromEntity")
->setParameter('fromEntity', $fromEntity);
} else {
$or = $qb->expr()->orX();
foreach ($properties as $key => $property) {
$prop = "part" . $key;
$or->add($entityShortName . "." . $property . " = :" . $prop);
$qb->setParameter($prop, $fromEntity);
}
$qb->andWhere($or);
}
$validate_field = array_key_exists('validate_field', $da_entity_associations_entities[$className]) ? $da_entity_associations_entities[$className]['validate_field'] : null;
$activeValue = array_key_exists('is_active_value', $da_entity_associations_entities[$className]) ? $da_entity_associations_entities[$className]['is_active_value'] : null;
if (array_key_exists($className, $da_entity_associations_entities) &&
isset($validate_field)) {
$fields = explode('.', $validate_field);
$fieldsToValidateIterator = new \CachingIterator(new \ArrayIterator($fields));
$currentEntityShortName = $entityShortName;
foreach ($fieldsToValidateIterator as $fieldToValidate) {
if ($fieldsToValidateIterator->hasNext()) {
$newEntityAlias = $currentEntityShortName . strtoupper($fieldToValidate);
$joins[] = ' LEFT JOIN ' . $currentEntityShortName . '.' . $fieldToValidate . ' ' . $newEntityAlias;
$currentEntityShortName = $newEntityAlias;
} else {
//IF IS NULL WE NEED TO DECLARE SPECIFIC WHERE CLOSURE
if (is_null($activeValue)) {
$qb->andWhere($currentEntityShortName . '.' . $fieldToValidate . " is NULL");
} else {
$qb->andWhere($currentEntityShortName . '.' . $fieldToValidate . " = :activeValue")
->setParameter('activeValue', $activeValue);
}
}
}
}
if (count($joins) > 0) {
/**
* DOCTRINE NOT ALLOW TO USE JOIN WITH UPDATE METHOD
* SO FIRST WEE NED TO SELECT ENTITIES WITH JOINS AND AFTER WE UPDATE ITS
*
* (Doctrine doesn't allow to select something like r.mytable where 'mytable` is the Entity I am targeting, but, there is a DQL function to do this : IDENTITY)
*/
$sql = 'SELECT IDENTITY(' . $entityShortName . ')'
. ' FROM ' . $entityShortName;
foreach ($joins as $join) {
$sql .= $join;
}
$dql = $this->em->createQuery($sql);
$qb->andWhere($qb->expr()->in($entityShortName . '.id', ':entities'))
->setParameter('entities', $dql->execute());
}
$qb->getQuery()->execute();
}
return true;
}
}