ZF2: How to catch exceptions thrown by a view helper automatically - zend-framework2

I have a some view helpers throwing exceptions on error. That's ok for development, but for production I would like to configure PhpRenderer to catch and log the exception without braking the hole view file to render - simply return nothing.
The PhpRenderer has the method:
public function __call($method, $argv)
{
if (!isset($this->__pluginCache[$method])) {
$this->__pluginCache[$method] = $this->plugin($method);
}
if (is_callable($this->__pluginCache[$method])) {
return call_user_func_array($this->__pluginCache[$method], $argv);
}
return $this->__pluginCache[$method];
}
Is it enough to overwrite this method?
How can I replace the PhpRenderer with my own?

You can do it by writing your own view strategy.
First, register your new strategy in the configuration.
return array(
'factories' => array(
'MyStrategy' => 'Application\ViewRenderer\StrategyFactory',
)
'view_manager' => array(
'strategies' => array(
'MyStrategy'
),
),
);
Then, create your own strategy
namespace Application\ViewRenderer;
use Zend\View\Strategy\PhpRendererStrategy;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class StrategyFactory implements FactoryInterface
{
public function createService (ServiceLocatorInterface $serviceLocator)
{
$renderer = new Renderer ();
return new Strategy ($renderer);
}
}
and renderer.
namespace Application\ViewRenderer;
use Zend\View\Renderer\PhpRenderer;
class Renderer extends PhpRenderer
{
public function render($nameOrModel, $values = null) {
// this is just an example
// the actual implementation will be very much like in PhpRenderer
return 'x';
}
}

Related

How to include EntityManager in ZendFramework 2 AbstractValidator

I have a custom validator, extending Zend AbstractValidator. The thing is, i want to include Doctrine EntityManager, but i keep failing! I tried to make a Factory for my Validator, but it doesn't seem to work. Help!! What am I doing wrong?
Validator:
$this->objectRepository stays empty, while i expect content.
namespace Rentals\Validator;
use Rentals\Response;
use Zend\Validator\AbstractValidator;
use Zend\Stdlib\ArrayUtils;
class ExistentialQuantification extends AbstractValidator
{
const NO_ENTITY_ID = 'noEntityId';
const ENTITY_NOT_FOUND = 'entityNotFound';
const INVALID_ID = 'invalidId';
protected $messageTemplates = [
self::NO_ENTITY_ID => 'The input does not contain an entity id.',
self::ENTITY_NOT_FOUND => 'The entity could not be found.',
self::INVALID_ID => 'The input does not contain an entity id.',
];
protected $objectRepository;
public function __construct(array $options)
{
$this->objectRepository = $options['object_repository'];
parent::__construct($options);
}
public function isValid($value)
{
if ($value === null) {
return true;
}
if (! isset($value->id)) {
$this->error(self::NO_ENTITY_ID);
return false;
}
$entityClass = $this->getOption('entity_class');
$controller = new Controller();
$entity = (new FactoryInterface)(EntityManager::class)->find($entityClass, $entity->id);
if (! $entity instanceof $entityClass) {
$this->error(self::ENTITY_NOT_FOUND);
return false;
}
if (! $entity->getId()) {
$this->error(self::NO_ENTITY_ID);
return false;
}
return true;
}
}
Factory:
namespace Rentals\Validator;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Stdlib\ArrayUtils;
class ExistentialQuantificationFactory implements FactoryInterface, MutableCreationOptionsInterface
{
protected $options = [];
public function setCreationOptions(array $options)
{
$this->options = $options;
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
if (! isset($this->options['object_manager'])) {
$this->options['object_manager'] = 'doctrine.entitymanager.orm_default';
}
$objectManager = $serviceLocator->get($this->options['object_manager']);
$objectRepository = $objectManager->getRepository($this->options['entity_class']);
return new ExistentialQuantification(ArrayUtils::merge(
$this->options, [
'objectManager' => $objectManager,
'objectRepository' => $objectRepository
]
));
}
}
Module config:
<?php
return [
'service_manager' => [
'factories' => [
'Rentals\\Validator\\ExistentialQuantification' => 'Rentals\\Validator\\ExistentialQuantificationFactory'
]
]
];
?>
What if you change your config entry like the following example?
return [
'validators' => [
'factories' => [
ExistentialQuantification::class => ExistentialQuantificationFactory::class,
],
],
];
This change will result in further changes for your factory, because the service locator for the entity manager differs from the one you injected.
namespace Application\Validator\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\MutableCreationOptionsTrait;
use Zend\ServiceManager\ServiceLocatorInterface;
class ExistentialQuantificationFactory implements FactoryInterface, MutableCreationOptionsInterface
{
use MutableCreatinOptionsTrait;
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();
if (! isset($this->creationOptions['object_manager'])) {
$this->creationOptions['object_manager'] = 'doctrine.entitymanager.orm_default';
}
$objectManager = $parentLocator->get($this->creationOptions['object_manager']);
$objectRepository = $objectManager->getRepository($this->creationOptions['entity_class']);
return new ExistentialQuantification(ArrayUtils::merge(
$this->options, [
'objectManager' => $objectManager,
'objectRepository' => $objectRepository
]
));
}
}
What I 've done here? First I implemented the MutableCreationOptionsTrait class. This trait implements the needed functions for working with creation options. But this is just a little hint for avoiding unnecessary work.
Because of setting the validator class as validator in the config, we have to use the parent service locator for getting the entity manager. The inherited service locator just provides access to validators.
Now you can try to access your validator in your controller like in the following examaple.
$validator = $this->getServiceLocator()
->get('ValidatorManager')
->get(ExistentialQuantification::class, [
'entity_class' => YourEntityClass::class,
]);
\Zend\Debug\Debug::dump($validator, __METHOD__);
The validator manager should return your validator so that you can test it.

Zend Framework 2 : unable to get servicelocator on a custom class

I am unable to load service locatior on a custom class, see my error code below, any advice would be appriciated
namespace Application\Helper;
use Zend\Mail\Message;
use Zend\Mail\Transport\Smtp as SmtpTransport;
use Zend\Mime\Message as MimeMessage;
use Zend\Mime\Part as MimePart;
use Zend\Mail\Transport\SmtpOptions;
use Zend\Mail\Transport\Sendmail;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class EmailHelper implements ServiceLocatorAwareInterface{
protected $serviceLocator;
public function testEmail($email){
$config = $this->serviceLocator->get('Config');
print_r($config); exit;
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Call to a member function get() on null in
/var/www/html/engsvc_dev/module/Application/src/Application/Helper/EmailHelper.php
on line 23
In order to inject the ServiceManager instance which is an implementation of Zend\ServiceManager\ServiceLocatorInterface you need to instantiate the class like this:
$emailHelper = $this->getServiceLocator()->get('EmailHelper');
Also don't forget to register the class in Module.php
public function getServiceConfig()
{
return array(
'invokables' => array(
'EmailHelper' => 'Application\Helper\EmailHelper'
)
);
}

Zend Framework 2 Service Manager Dependency Injection

My application is a collection of POPO's and I'm trying to wire these POPO's up using the Zend Framework 2 Service Manager.
To illustrate my problem, take the following example:
$config = array(
'services' => array(
'my.app.serviceA' => new \My\App\Services\ServiceA(),
'my.app.serviceB' => new \My\App\Services\ServiceB(),
'my.app.manager.task' => new \My\App\Manager\TaskManager(),
),
);
My TaskManager class looks something like this:
class TaskManager {
protected $serviceA;
protected $serviceB;
public function setServiceA( \My\App\Service\ServiceA $serviceA )
{
$this->serviceA = $serviceA;
}
public function setServiceB( \My\App\Service\ServiceB $serviceB )
{
$this->serviceB = $serviceB;
}
}
As you can see, the TaskManager class has dependencies on both ServiceA and ServiceB. How do inject those services into my.app.manager.task using the Service Manager configuration using the service names defined for both ServiceA and ServiceB?
UPDATE:
I'm beginning to believe that I shouldn't be using the ServiceManager component for my purposes at all but that I should be using the Zend DI component instead.
I get the impression that the ServiceManager is a ZF2 "framework" component whereas Zend\DI seems to be more of a generic all purpose DiC. Hence, this might be the reason of ServiceManager's tied relationship with the MVC and ModuleManager components (which also seem to be "framework" components).
Maybe someone could clarify?
in module.config.php The Service Manager can be configured in 7 different ways:
return array(
// instantiate the class for you when needed
'invokables' => array(
'commentController' => '\Comment\Controller\CommentController';
),
// Aliasing a name to a known service name
'aliases' => array(
'Comment\Service' => 'commentService';
),
// configure the instance of the object
'factories' => array(
'commentController' => function ($sm) {
$locator = $sm->getServiceLocator();
$controller = $locator->get('commentController');
$controller->setCommentService($locator->get('Comment\Service'));
return $controller;
}
),
// register already instantiated objects
'services' => array(
'commentController' => new \Comment\Controller\CommentController(),
),
//factory instance that can create multiple services based on the name supplied to the factory.
'abstract_factories' => array(
'SomeModule\Service\FallbackFactory',
),
// initialize the service whenever service created
'initializers' => array(
function ($instance, $sm) {
if ($instance instanceof \Comment\Controller\CommentController){
$instance->setCommentService($sm->get('Comment\Service'));
}
}
),
// indicating whether or not a service should be shared
'shared' => array(
'commentController' => false,
),
);
and in Module.php
public function getControllerConfig() {
return array(
'factories' => array(
'commentController' => function ($sm) {
$controller = new \Comment\Controller\CommentController();
$locator = $sm->getServiceLocator();
$controller->setCommentForm($locator->get('commentForm'));
$controller->setCommentService($locator->get('commentService'));
return $controller;
}
)
);
}
and simple use in controller :
$commentService = $this->serviceLocator->get('Comment\Service');
you put this in getter or in init() method
ZF2's New Controller::init() :: phly, boy, phly
in Controller ;
$yourService = $this->getServiceLocator()->get('your_service_alias');
in View Helper :
you should send in from Module.php by constructor of viewHelper
public function getViewHelperConfig() {
return array(
'factories' => array(
'loginHelper' => function($sm) {
return new LoginHelper($sm);
}
)
}
in a calss
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
public class UseCaseBO implements ServiceLocatorAwareInterface {
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
// now instance of Service Locator is ready to use
public function someMethod() {
$table = $this->serviceLocator->get('your_service_alias');
//...
}
}
for me, the best way is to create a class factory and use the factoryInterface, like this :
return array(
'service_manager' => array(
'factories' => [
'Task' => 'Application\TaskFactory',
],
'invokables' => array(
'Task'=> 'Application\Task',
'ServiceA'=> 'Application\ServiceA',
'ServiceB' => 'Application\ServiceB'
)
),
);
And a factory class :
class TaskFactory implements FactoryInterface {
/** #var ServiceLocatorInterface $serviceLocator */
var $serviceLocator;
public function createService(ServiceLocatorInterface $serviceLocator) {
$sl = $this->serviceLocator = $serviceLocator;
// you can get your registered services
$serviceA = $sl->get('ServiceA');
$serviceB = $sl->get('ServiceB');
// You can build your class using the class loader
$task = new Application\Task();
// Or the Service Locator Again
$task = $sl->get('Task');
return $task;
}
}
You can implement the factory interface on your Task class. I prefer to have control on what I'm building.

How to setup multiple authentication adapters with fallback

I am trying to setup ZfcUser to authenticate with a fallback to LDAP. My zfcuser.global.php contains:
'auth_adapters' => array(
100 => 'ZfcUser\Authentication\Adapter\Db',
99 => 'ZfcUser\Authentication\Adapter\Ldap',
),
I have created an Ldap.php as an AbstractAdapter with the following setup. I have eliminated function code for brevity:
namespace ZfcUser\Authentication\Adapter;
use DateTime;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\Session\Container as SessionContainer;
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthEvent;
use ZfcUser\Mapper\User as UserMapperInterface;
use ZfcUser\Options\AuthenticationOptionsInterface;
class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface
{
protected $mapper;
protected $serviceManager;
protected $options;
public function authenticate(AuthEvent $e)
{
...
}
public function getMapper()
{
...
}
public function setMapper(UserMapperInterface $mapper)
{
...
}
public function getServiceManager()
{
return $this->serviceManager;
}
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
}
public function setOptions(AuthenticationOptionsInterface $options)
{
$this->options = $options;
}
public function getOptions()
{
...
}
}
I have also included it as invokable in the Module.php file:
public function getServiceConfig()
{
return array(
'invokables' => array(
'ZfcUser\Authentication\Adapter\Db' => 'ZfcUser\Authentication\Adapter\Db',
'ZfcUser\Authentication\Adapter\Ldap' => 'ZfcUser\Authentication\Adapter\Ldap',
...
),
...
}
If I flip the priority values it changes who can log in. Whichever adapter is executed first allows logins but the other adapter never gets used. Anyone know how to make ZfcUser fall back to the next adapter if the first one fails?
Issue fixed here https://github.com/SocalNick/ScnSocialAuth/issues/123
For me I just i followed matwright(github) response (See https://github.com/matwright/ScnSocialAuth) so I replace two files :
socalnick/src/ScnSocialAuth/Controller/Plugin/ScnSocialAuthProvider.php by ScnSocialAuthProvider.php
socalnick/src/ScnSocialAuth/Controller/UserController.php by UserController.php
And in zfcuser.global.php :
'auth_adapters' => array(
100 => 'ZfcUser\Authentication\Adapter\Db',
),
Hope this help...

Custom validator, not being executed when used in a form

i have created a custom validator but when I want to use it, it seems that it is never executed!
the validator :
class sfTestUrlValidator extends sfValidatorUrl {
public function initialize($context, $parameters = null) {
// Initialize parent
parent::initialize($context);
}
public function execute(&$value, &$error) {
if($value == "http://www.librosweb.es/")
{
//$error = "noooooooooooooo";
return true;
}
else return false;
}
}
in the configure method of a form, i do like that :
public function configure() {
.....
....
'url' => new sfTestUrlValidator(),
You need to override sfValidatorBase::doClean method and not some not-existent execute method and throw exception intead of returning true/false. Have a look at any validator class, e.g. sfValidatorString. However in your case, I would simply use sfValidatorChoice with one choice
public function configure()
{
'url' => new sfValidatorChoice(array('choices' => array(
'your.website.url',
)));
}

Resources