laminas/laminas-validator

Validator plugin manager requires a `MvcTranslator` for translation - in any context

Closed this issue · 2 comments

Bug Report

Q A
Version(s) 2.33.0

Summary

The validator plugin manager tries to inject a translator with name MvcTranslator but outside of a laminas-mvc based application there is no MvcTranslator. This means it will fail in a Mezzio based application or any other context.

Current behavior

The validator plugin manager searches for a service with name MvcTranslator:

if ($validator instanceof Translator\TranslatorAwareInterface) {
if ($container && $container->has('MvcTranslator')) {
$validator->setTranslator($container->get('MvcTranslator'));
}
}

A registered service with name Laminas\I18n\Translator\TranslatorInterface::class will not be used and an alias can not be used because the AbstractValidator expected an implementation of Laminas\Validator\Translator\TranslatorInterface:

interface TranslatorInterface
{
/**
* @param string $message
* @param string $textDomain
* @param string $locale
* @return string
*/
public function translate($message, $textDomain = 'default', $locale = null);
}

public function setTranslator(?Translator\TranslatorInterface $translator = null, $textDomain = null)

How to reproduce

// Service manager
$serviceManager = new Laminas\ServiceManager\ServiceManager();
$serviceManager->setService('config', []); // for translator factory
$serviceManager->configure(
    (new Laminas\I18n\ConfigProvider())->getDependencyConfig()
);

// Translator
/** @var Laminas\I18n\Translator\TranslatorInterface $translator */
$translator = $serviceManager->get(
    Laminas\I18n\Translator\TranslatorInterface::class
);
$translator->addTranslationFilePattern(
    Laminas\I18n\Translator\Loader\PhpArray::class,
    Laminas\I18n\Translator\Resources::getBasePath(),
    Laminas\I18n\Translator\Resources::getPatternForValidator()
);
Locale::setDefault('de');

// Test
$validatorManager = new Laminas\Validator\ValidatorPluginManager(
    $serviceManager
);
$validator = $validatorManager->get(Laminas\Validator\NotEmpty::class);
$validator->isValid('');

// Is not translated!
echo $validator->getMessages()['isEmpty']; // Value is required and can't be empty

A service must be added manually:

$serviceManager->setService(
    'MvcTranslator',
    new class ($translator)
        implements Laminas\Validator\Translator\TranslatorInterface {
        public function __construct(
            private readonly Laminas\I18n\Translator\TranslatorInterface $translator
        ) {
        }

        public function translate(
            $message,
            $textDomain = 'default',
            $locale = null
        ) {
            return $this->translator->translate($message, $textDomain, $locale);
        }
    }
);

Or laminas-mvc-i18n must be used:

$serviceManager->configure(
    (new Laminas\Mvc\I18n\ConfigProvider())->getDependencyConfig()
);
echo $validator->getMessages()['isEmpty']; // Es wird eine Eingabe benötigt

Expected behavior

Seamless integration should also be available outside of laminas-mvc based application without the user having to write anything manually or have to use an MVC package in a Mezzio application.

Yah, laminas-mvc actually does the whole wiring stuff. mainly due to the modulemanager as it provides those provider interfaces.
Not sure if @Xerkus wanted to drop modulemanager in some way, but these provider interfaces are actually the source of the problem. But I do not see issues with having additional provider features in MVC but as you say, the config key should be handled by each component on its own imho.