Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
digitalascetic/entity-associations / Service / EntityAssociationsService.php
Size: Mime:
<?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;
    }
}