Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
90.00% covered (success)
90.00%
9 / 10
CRAP
95.45% covered (success)
95.45%
63 / 66
DefaultEntityValidator
0.00% covered (danger)
0.00%
0 / 1
90.00% covered (success)
90.00%
9 / 10
23
95.45% covered (success)
95.45%
63 / 66
 validate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
10 / 10
 getEntityName
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
 validateClassname
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
6 / 6
 validateNamespace
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
6 / 6
 validateTable
0.00% covered (danger)
0.00%
0 / 1
2.50
50.00% covered (warning)
50.00%
3 / 6
 validateAttributeReferenceCollectionUnique
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
14 / 14
 validateAttributeDBNamesUnique
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
8 / 8
 validateIndexNameUnique
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
8 / 8
 checkDuplicate
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
<?php
declare(strict_types = 1);
namespace Siesta\Validator;
use Siesta\Contract\EntityValidator;
use Siesta\Model\DataModel;
use Siesta\Model\Entity;
use Siesta\Model\ValidationLogger;
use Siesta\Util\ArrayUtil;
/**
 * @author Gregor Müller
 */
class DefaultEntityValidator implements EntityValidator
{
    const ERROR_INVALID_CLASS_NAME = "Entity '%s' has invalid class name '%s'";
    const ERROR_INVALID_CLASS_NAME_CODE = 1100;
    const ERROR_INVALID_NAMESPACE = "Entity '%s' has invalid class namespace '%s'";
    const ERROR_INVALID_NAMESPACE_CODE = 1101;
    const ERROR_INVALID_TABLE = "Entity '%s' has invalid table name '%s'";
    const ERROR_INVALID_TABLE_CODE = 1102;
    const ERROR_DUPLICATE_ATT_REF_COL = "Entity '%s' has duplicate attribute/reference/collection '%s'";
    const ERROR_DUPLICATE_ATT_REF_COL_CODE = 1103;
    const ERROR_DUPLICATE_DB_NAME = "Entity '%s' has duplicate database column '%s'";
    const ERROR_DUPLICATE_DB_NAME_CODE = 1104;
    const ERROR_DUPLICATE_INDEX = "Entity '%s' has duplicate index '%s'";
    const ERROR_DUPLICATE_INDEX_CODE = 1105;
    /**
     * @var DataModel
     */
    protected $datamodel;
    /**
     * @var Entity
     */
    protected $entity;
    /**
     * @var ValidationLogger
     */
    protected $logger;
    /**
     * @param DataModel $dataModel
     * @param Entity $entity
     * @param ValidationLogger $logger
     */
    public function validate(DataModel $dataModel, Entity $entity, ValidationLogger $logger)
    {
        $this->datamodel = $dataModel;
        $this->entity = $entity;
        $this->logger = $logger;
        $this->validateClassname();
        $this->validateNamespace();
        $this->validateTable();
        $this->validateAttributeReferenceCollectionUnique();
        $this->validateAttributeDBNamesUnique();
        $this->validateIndexNameUnique();
    }
    /**
     * @return string
     */
    protected function getEntityName()
    {
        return $this->entity->getClassShortName();
    }
    /**
     * @param string $text
     * @param int $code
     */
    protected function error(string $text, int $code)
    {
        $this->logger->error($text, $code);
    }
    /**
     *
     */
    protected function validateClassname()
    {
        $className = $this->getEntityName();
        if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $className)) {
            return;
        }
        $error = sprintf(self::ERROR_INVALID_CLASS_NAME, $className, $className);
        $this->error($error, self::ERROR_INVALID_CLASS_NAME_CODE);
    }
    /**
     *
     */
    protected function validateNamespace()
    {
        $namespace = $this->entity->getNamespaceName();
        if (preg_match('/^((?:\\\\{0,1}\\w+|\\w+\\\\{1,2})(?:\\w+\\\\{0,2})+)$/', $namespace) === 1) {
            return;
        }
        $error = sprintf(self::ERROR_INVALID_NAMESPACE, $this->getEntityName(), $namespace);
        $this->error($error, self::ERROR_INVALID_NAMESPACE_CODE);
    }
    /**
     *
     */
    protected function validateTable()
    {
        $tableName = $this->entity->getTableName();
        if ($tableName !== null) {
            return;
        }
        $error = sprintf(self::ERROR_INVALID_TABLE, $this->getEntityName(), $tableName);
        $this->error($error, self::ERROR_INVALID_TABLE_CODE);
    }
    protected function validateAttributeReferenceCollectionUnique()
    {
        $duplicateNameList = [];
        $nameList = [];
        foreach ($this->entity->getAttributeList() as $attribute) {
            $this->checkDuplicate($nameList, $duplicateNameList, $attribute->getPhpName());
        }
        foreach ($this->entity->getReferenceList() as $reference) {
            $this->checkDuplicate($nameList, $duplicateNameList, $reference->getName());
        }
        foreach ($this->entity->getCollectionList() as $collection) {
            $this->checkDuplicate($nameList, $duplicateNameList, $collection->getName());
        }
        foreach ($this->entity->getCollectionManyList() as $collectionMany) {
            $this->checkDuplicate($nameList, $duplicateNameList, $collectionMany->getName());
        }
        foreach ($duplicateNameList as $duplicateName) {
            $error = sprintf(self::ERROR_DUPLICATE_ATT_REF_COL, $this->getEntityName(), $duplicateName);
            $this->error($error, self::ERROR_DUPLICATE_ATT_REF_COL_CODE);
        }
    }
    /**
     *
     */
    protected function validateAttributeDBNamesUnique()
    {
        $duplicateNameList = [];
        $nameList = [];
        foreach ($this->entity->getAttributeList() as $attribute) {
            $this->checkDuplicate($nameList, $duplicateNameList, $attribute->getDBName());
        }
        foreach ($duplicateNameList as $duplicateName) {
            $error = sprintf(self::ERROR_DUPLICATE_DB_NAME, $this->getEntityName(), $duplicateName);
            $this->error($error, self::ERROR_DUPLICATE_DB_NAME_CODE);
        }
    }
    /**
     *
     */
    protected function validateIndexNameUnique()
    {
        $duplicateNameList = [];
        $nameList = [];
        foreach ($this->entity->getIndexList() as $index) {
            $this->checkDuplicate($nameList, $duplicateNameList, $index->getName());
        }
        foreach ($duplicateNameList as $duplicateName) {
            $error = sprintf(self::ERROR_DUPLICATE_INDEX, $this->getEntityName(), $duplicateName);
            $this->error($error, self::ERROR_DUPLICATE_INDEX_CODE);
        }
    }
    /**
     * @param array $nameList
     * @param array $duplicateNameList
     * @param $name
     */
    protected function checkDuplicate(array &$nameList, array &$duplicateNameList, $name)
    {
        $existing = ArrayUtil::getFromArray($nameList, $name);
        if ($existing) {
            $duplicateNameList[] = $name;
        }
        $nameList[$name] = true;
    }
}