1
\$\begingroup\$

I need to execute different validators depending for different countries ($country_id), each validator should return error message variable in case of failure. One validator (CommonValidator) is executed for all countries and others are executed by condition

interface ValidatorInterface {

    public function isValid($validatorParam);

    public function getErrorVariableName();

}

class CommonValidator implements ValidatorInterface
{

    private $errorVariableName = 'invalid_message';


    public function isValid($iban)
    {
        //some validation
        return true;
    }


    public function getErrorVariableName()
    {
        return $this->errorVariableName;
    }

}

class LengthValidator implements ValidatorInterface
{

    protected $errorVariableName = 'some_variable';

    protected $length;

    public function __construct($country_id)
    {
        //add new country_id here if needed
        switch ($country_id) {
            case 204:
                $this->length = 21;
                break;
        }
    }

    public function isValid($validatorParam)
    {
        if (is_null($this->length)) {
            return true;
        }

        return strlen($validatorParam) == $this->length;
    }

    public function getErrorVariableName()
    {
        return $this->errorVariableName;
    }

}

class CountryCodeValidator implements ValidatorInterface
{

    private $country_id;

    private $errorVariableName = 'wrong_country_code';

    private $allowedCountryCodes;

    public function __construct($country_id)
    {
        $this->country_id = $country_id;
        //add new country_id here if needed
        switch ($country_id) {
            case 204:
                $this->allowedCountryCodes = ['CH', 'LI'];;
                break;
        }
    }

    public function isValid($validatorParam)
    {
        if (is_null($this->allowedCountryCodes)) {
            return true;
        }
        $string = strtoupper(substr($validatorParam, 0, 2));

        return in_array($string, $this->allowedCountryCodes);
    }

    public function getErrorVariableName()
    {
        return $this->errorVariableName;
    }

}

class ValidatorStrategyCollection
{

    private $collection;

    public function __construct()
    {
        $this->collection = new \SplObjectStorage();
    }


    public function add(ValidatorInterface $validator)
    {
        if (!$this->collection->contains($validator)) {
            $this->collection->attach($validator);
        }
    }

    public function remove(ValidatorInterface $validator)
    {
        $this->collection->detach($validator);
    }

    public function getCollection()
    {
        return $this->collection;
    }

}

class ValidatorStrategyFactory
{
    public static function get($country_id)
    {
        $validatorCollection = new ValidatorStrategyCollection();
        $validatorCollection->add(new CommonValidator());
        $validatorCollection->add(new LengthValidator($country_id));
        $validatorCollection->add(new CountryCodeValidator($country_id));

        return $validatorCollection;
    }
}

class IbanCheck
{

    protected $errorVariableName;

    private $country_id;

    private $validatorsCollection;


    public function __construct($country_id)
    {
        $this->country_id = $country_id;
        $this->validatorsCollection = ValidatorStrategyFactory::get($this->country_id);
    }

    public function isValid($iban)
    {

        foreach ($this->validatorsCollection->getCollection() as $validator) {
            if (!$validator->isValid($iban)) {
                $this->errorVariableName = $validator->getErrorVariableName();

                return false;
            }
        }

        return true;
    }

    public function getErrorVariableName()
    {
        return $this->errorVariableName;
    }

 }

//usage
$ibanCheck = new IbanCheck($country_id);
if (!$ibanCheck->isValid($iban)) {
    $error = $ibanCheck->getErrorVariableName();
}

Currently I have switch case in both LengthValidator and CountryCodeValidator, originally I wanted to have switch inside ValidatorStrategyFactory and there decide which validators I have to return but after I thought that somebody might want to use CountryCodeValidator class alone and then in this case CountryCodeValidator has no knowledge for which country validation should be executed, so I decided to put switch case inside CountryCodeValidator but then I have to make that by default it returns true for all countries and for 204 it runs validation and return result.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.