Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
58 / 58
DefaultReferenceValidator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
10 / 10
21
100.00% covered (success)
100.00%
58 / 58
 validate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
9 / 9
 getEntityName
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getReferenceName
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 error
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 validateForeignEntity
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 validateOnX
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 validateReferenceMappingList
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
7 / 7
 validateReferenceMapping
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
14 / 14
 validateMappingDBDataTypes
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
7 / 7
 validateMappingPHPDataTypes
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
7 / 7
<?php
declare(strict_types = 1);
namespace Siesta\Validator;
use Siesta\Contract\ReferenceValidator;
use Siesta\Model\Attribute;
use Siesta\Model\DataModel;
use Siesta\Model\Entity;
use Siesta\Model\Reference;
use Siesta\Model\ReferenceMapping;
use Siesta\Model\ValidationLogger;
use Siesta\XML\XMLReference;
/**
 * @author Gregor Müller
 */
class DefaultReferenceValidator implements ReferenceValidator
{
    const ERROR_INVALID_FOREIGN_TABLE = "Entity '%s' Reference '%s' points to not defined table %s";
    const ERROR_INVALID_FOREIGN_TABLE_CODE = 1300;
    const ERROR_INVALID_ON_X_VALUE = "Entity '%s' Reference '%s' %s has invalid value '%s'";
    const ERROR_INVALID_ON_X_VALUD_CODE = 1301;
    const ERROR_INVALID_LOCAL_REFERENCE = "Entity '%s' Reference '%s' localColumn '%s' can not be resolved";
    const ERROR_INVALID_LOCAL_REFERENCE_CODE = 1302;
    const ERROR_INVALID_FOREIGN_REFERENCE = "Entity '%s' Reference '%s' foreignColumn '%s' can not be resolved";
    const ERROR_INVALID_FOREIGN_REFERENCE_CODE = 1303;
    const ERROR_MAPPING_HAS_NOT_SAME_DB_DATA_TYPE = "Entity '%s' Reference '%s' localColumn and foreignColumn have different datatypes '%s' and '%s'";
    const ERROR_MAPPING_HAS_NOT_SAME_DB_DATA_TYPE_CODE = 1304;
    const ERROR_MAPPING_HAS_NOT_SAME_PHP_DATA_TYPE = "Entity '%s' Reference '%s' localColumn and foreignColumn have different phptypes '%s' and '%s'";
    const ERROR_MAPPING_HAS_NOT_SAME_PHP_DATA_TYPE_CODE = 1305;
    const ERROR_NO_REFERENCE_MAPPING = "Entity '%s' Reference '%s' has no referenceMapping.";
    const ERROR_NO_REFERENCE_MAPPING_CODE = 1306;
    const ALLOWED_ON_X = [
        "restrict",
        "cascade",
        "set null",
        "no action"
    ];
    /**
     * @var DataModel
     */
    protected $datamodel;
    /**
     * @var Entity
     */
    protected $entity;
    /**
     * @var Reference
     */
    protected $reference;
    /**
     * @var ValidationLogger
     */
    protected $logger;
    /**
     * @param DataModel $dataModel
     * @param Entity $entity
     * @param Reference $reference
     * @param ValidationLogger $logger
     */
    public function validate(DataModel $dataModel, Entity $entity, Reference $reference, ValidationLogger $logger)
    {
        $this->datamodel = $dataModel;
        $this->entity = $entity;
        $this->reference = $reference;
        $this->logger = $logger;
        $this->validateForeignEntity();
        $this->validateOnX($reference->getOnUpdate(), XMLReference::ON_UPDATE);
        $this->validateOnX($reference->getOnDelete(), XMLReference::ON_DELETE);
        $this->validateReferenceMappingList();
    }
    /**
     * @return string
     */
    protected function getEntityName()
    {
        return $this->entity->getClassShortName();
    }
    /**
     * @return string
     */
    protected function getReferenceName()
    {
        return $this->reference->getName();
    }
    /**
     * @param string $text
     * @param string $code
     */
    protected function error(string $text, int $code)
    {
        $this->logger->error($text, $code);
    }
    /**
     *
     */
    protected function validateForeignEntity()
    {
        if ($this->reference->getForeignEntity() !== null) {
            return;
        }
        $error = sprintf(self::ERROR_INVALID_FOREIGN_TABLE, $this->getEntityName(), $this->getReferenceName(), $this->reference->getForeignTable());
        $this->error($error, self::ERROR_INVALID_FOREIGN_TABLE_CODE);
    }
    /**
     * @param string $onXValue
     * @param string $onX
     */
    protected function validateOnX(string $onXValue, string $onX)
    {
        if (in_array($onXValue, self::ALLOWED_ON_X)) {
            return;
        }
        $error = sprintf(self::ERROR_INVALID_ON_X_VALUE, $this->getEntityName(), $this->getReferenceName(), $onX, $onXValue);
        $this->error($error, self::ERROR_INVALID_ON_X_VALUD_CODE);
    }
    /**
     *
     */
    protected function validateReferenceMappingList()
    {
        $referenceMappingList = $this->reference->getReferenceMappingList();
        if (sizeof($referenceMappingList) === 0) {
            $error = sprintf(self::ERROR_NO_REFERENCE_MAPPING, $this->getEntityName(), $this->getReferenceName());
            $this->error($error, self::ERROR_NO_REFERENCE_MAPPING_CODE);
        }
        foreach ($referenceMappingList as $referenceMapping) {
            $this->validateReferenceMapping($referenceMapping);
        }
    }
    /**
     * @param ReferenceMapping $referenceMapping
     */
    protected function validateReferenceMapping(ReferenceMapping $referenceMapping)
    {
        if ($this->reference->getForeignEntity() === null) {
            return;
        }
        $localAttribute = $referenceMapping->getLocalAttribute();
        if ($localAttribute === null) {
            $error = sprintf(self::ERROR_INVALID_LOCAL_REFERENCE, $this->getEntityName(), $this->getReferenceName(), $referenceMapping->getLocalAttributeName());
            $this->error($error, self::ERROR_INVALID_LOCAL_REFERENCE_CODE);
        }
        $foreignAttribute = $referenceMapping->getForeignAttribute();
        if ($foreignAttribute === null) {
            $error = sprintf(self::ERROR_INVALID_FOREIGN_REFERENCE, $this->getEntityName(), $this->getReferenceName(), $referenceMapping->getForeignAttributeName());
            $this->error($error, self::ERROR_INVALID_FOREIGN_REFERENCE_CODE);
        }
        if ($foreignAttribute !== null && $localAttribute !== null) {
            $this->validateMappingDBDataTypes($localAttribute, $foreignAttribute);
            $this->validateMappingPHPDataTypes($localAttribute, $foreignAttribute);
        }
    }
    /**
     * @param Attribute $localAttribute
     * @param Attribute $foreignAttribute
     */
    protected function validateMappingDBDataTypes(Attribute $localAttribute, Attribute $foreignAttribute)
    {
        $localType = $localAttribute->getDbType();
        $foreignType = $foreignAttribute->getDbType();
        if ($localType === $foreignType) {
            return;
        }
        $error = sprintf(self::ERROR_MAPPING_HAS_NOT_SAME_DB_DATA_TYPE, $this->getEntityName(), $this->getReferenceName(), $localType, $foreignType);
        $this->error($error, self::ERROR_MAPPING_HAS_NOT_SAME_DB_DATA_TYPE_CODE);
    }
    /**
     * @param Attribute $localAttribute
     * @param Attribute $foreignAttribute
     */
    protected function validateMappingPHPDataTypes(Attribute $localAttribute, Attribute $foreignAttribute)
    {
        $localType = $localAttribute->getPhpType();
        $foreignType = $foreignAttribute->getPhpType();
        if ($localType === $foreignType) {
            return;
        }
        $error = sprintf(self::ERROR_MAPPING_HAS_NOT_SAME_PHP_DATA_TYPE, $this->getEntityName(), $this->getReferenceName(), $localType, $foreignType);
        $this->error($error, self::ERROR_MAPPING_HAS_NOT_SAME_PHP_DATA_TYPE_CODE);
    }
}