I want to put the doctrine entity manager into many different classes in my ZF2 project. Thus. I have setup the following initializer in my Module.php:
'initializers' => array(
function ($instance, $services) {
if (is_object($instance)) { // just for debugging
var_dump(get_class($instance));
}
if (!$instance instanceof EntityManagerAwareInterface) {
return;
}
$entityManager = $services->get('doctrine.entitymanager.orm_default');
$instance->setEntityManager($entityManager);
},
),
)
Yet, it never gets called on my AuthController even though I am visiting a site of that controller (and get a null pointer exception, because the entity-manager was not set). Of course, the controller does implement the required interface:
class AuthController extends AbstractActionController implements EntityManagerAwareInterface
Is there anything else I have to configure so that my AuthController is checked against the initializer closure?
At the moment I have it under invokables in module.config.php.
'controllers' => array(
'invokables' => array(
'Auth\Controller\Auth' => 'Auth\Controller\AuthController',
),
),
When I remove it from there, the application cannot find the class anymore.
My debugging output lists other classes which are checked against the initializers, many managers and services. A small excerpt:
string(37) "Zend\\Mvc\\Controller\\ControllerManager"
string(33) "Zend\\Mvc\\Controller\\PluginManager"
string(29) "Zend\\View\\HelperPluginManager"
[...]
string(24) "Doctrine\\DBAL\\Connection"
string(26) "Doctrine\\ORM\\EntityManager"
string(41) "Zend\\Authentication\\AuthenticationService"
Try adding an initializer for the controller manager too, judging by your debug output, the one you posted appears to be for the service manager. You configure other managers the same way, the method to use for controller manager is getControllerConfig
public function getControllerConfig()
{
return array(
'initializers' => array(
// controller initializers here...
),
);
}
Related
Here is my service configuration:
public function getServiceConfig()
{
return array(
'factories' => array(
'Squiddle\Designpackage' => function($sm){
$designPkg = new Designpackage($sm);
return $designPkg;
}
)
);
}
It's being used within a function that gets triggered on the dispatch event.
Here is it's usage:
$e->getApplication()->getServiceManager()->get('Squiddle\Designpackage');
My error is that the designpackage is being constructed with null instead of a service manager.
Not sure whats wrong here;
It could be a problem in the Designpackage constructor
I'm new in Silex and I can not find how to change SecurityServiceProvider to restrict access for 24 hours after 3 bad connections.
The authentication works perfectly.
Thank you very much for your help and ideas.
You can create a CustomAuthenticationFailureHandler which extends the DefaultAuthenticationFailureHandler.
Create a class:
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
class CustomAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{
/**
* (non-PHPdoc)
* #see \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler::onAuthenticationFailure()
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// create a failure counter for the access restriction
return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']);
}
}
share this class:
$app['security.authentication.failure_handler.general'] = $app->share(function() use ($app) {
return new CustomAuthenticationFailureHandler($app['security.http_utils'], array(), $app);
});
where this failure handler is matching to the firewall named general:
// init the firewall
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'general' => array(
'pattern' => '^/',
'anonymous' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login_check'
),
...
)
)
);
you will also need a CustomAuthenticationSuccessHandler which extends the DefaultAuthenticationSuccessHandler:
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
class CustomAuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
/**
* (non-PHPdoc)
* #see \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler::onAuthenticationSuccess()
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
// handle the 24 hour restriction for the user ...
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
}
}
and share this class:
$app['security.authentication.success_handler.general'] = $app->share(function () use ($app) {
return new CustomAuthenticationSuccessHandler($app['security.http_utils'], array(), $app);
});
hope this helpful for you ...
Thanks to Ralf Hertsch for giving a clever answer, however as tekilatexee points, the Failure Handler does not accept an HttpUtils instance as first parameter to the constructor, if it extends DefaultAuthenticationFailureHandler without overriding the constructor.
Also, you don't need to write both Custom Handlers (one for failure, one for success), you could link only a Failure Handler, or only a Success Handler.
To instance correctly your Failure Handler extending the Symfony's default, the correct definition of the handler service is like this:
$app['security.authentication.failure_handler.general'] = $app->share(function() use ($app) {
return new CustomAuthenticationFailureHandler($app['kernel'], $app['security.http_utils']);
});
This passes the Symfony\Component\HttpKernel\HttpKernelInterface as first argument to the constructor.
However, if your Custom Handler instead of extending Symfony's default, implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface it just needs to implement onAuthenticationFailure that receives a Symfony's Request object and an AuthenticationException, and you could pass in the constructor, an instance of Doctrine\DBAL\Connection to log and check previous authentication failures, or any other custom set of services.
It looks like it has been touched several times already, but i still can't get it work. I set up an JSON-RPC server in a separate module, it works fine. Its functionality is in a new class Rpcapi. Now I want reuse DB related functions that already implemented in another module from that class. According to ZF2 docs my Rpcapi class has to be ServiceLocator-aware and it looks like I made it that way. Unfortunatelly still can't get it working. Please help keeping in mind that I'm new with ZF2 :)
Rpccontroller.php
namespace Rpc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Server\Server;
use Zend\Json\Server\Smd;
use Rpc\Model\Rpcapi;
class RpcController extends AbstractActionController
{
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass(new Rpcapi);
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
}
module.config.php for Rpc module
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
),
),
Rpcapi.php
namespace Rpc\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Rpcapi implements ServiceLocatorAwareInterface
{
protected $services;
protected $siteTable;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getSiteTable()
{
if (!$this->siteTable) {
$sm = $this->getServiceLocator();
$this->siteTable = $sm->get('rpcapi');
}
return $this->siteTable;
}
/**
* Returns list of all sites
*
*
* #return array
*/
public function getAllSites()
{
$results = $this->getSiteTable()->fetchAll();
$r = array ('1' => '1', '2' => 2); //Just to return something for now
return $r;
}
}
All I could get out is: Fatal error: Call to a member function get() on a non-object in /var/www/html/AmeriFluxZF2/module/Rpc/src/Rpc/Model/Rpcapi.php on line 28. Line 28 is:
$this->siteTable = $sm->get('rpcapi');
Any help is much appreciated!
Making the class service locator aware tells the ZF2 that the service locator should be injected into your class upon instantiation. However, you still need to use the service locator to instantiate this class, rather than creating an instance of it yourself, or this will never happen.
Your probably want to add a new entry to invokables for your Rpcapi class, and then grab this from the service locator instead of doing new Rpcapi in your controller.
PS: The naming of your classes is very confusing - you have an Rpcapi class, and an invokable called rpcapi, yet this invokable creates an instance of a completely different class?
If you want serviceLocator to be injected by the service manager in your Rpcapi, you must get it via the service manager itself :
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
'Rpc\Model\Rpcapi' => 'Rpc\Model\Rpcapi',
),
),
the action :
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass($this->getServiceLocator()->get('Rpc\Model\Rpcapi'));
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
And this is where you can see that your 'rcpai' name for SiteTable is not a good choice... ;)
In the album example of the Zend Framework 2 User Guide the model is configured like this:
<?php
namespace Album;
// Add these import statements:
use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module
{
// getAutoloaderConfig() and getConfig() methods here
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Album\Model\AlbumTable' => function($sm) {
$tableGateway = $sm->get('AlbumTableGateway');
$table = new AlbumTable($tableGateway);
return $table;
},
'AlbumTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
The variable $sm is a Zend\ServiceManager\ServiceManager object. But how/when/where is it created/initialized?
EDIT:
What I want to know, is: How/where does $sm get its value (and become a ServiceManager object).
When you retrieve a service from a service manager, if it's a factory, it passes an instance of itself as the first parameter to whichever callable is responsible for creating the service, that will usually be either a closure (as in your example), or the createService method of a factory class.
This is done, in the case of factories, by the code here https://github.com/zendframework/zf2/blob/master/library/Zend/ServiceManager/ServiceManager.php#L859
Basically, in your module you're telling the ServiceManager that those services are created by calling the closures you provided. When you ask the ServiceManager to get() one of them the first time, it'll determine it's a factory (it was supplied in the factories key in config), then figure out if it's a closure or an instance of FactoryInterface (a factory class), and finally call it appropriately to instantiate your service.
I'm looking for something similar to: http://knplabs.com/blog/give-your-projects-a-gaufrette
Thanks.
Just use Gaufrette, I do it too (even in ZF2 projects)!
php composer.phar require knplabs/gaufrette:0.1.*
You can then use it anywhere in your application and eventually use it as a service by defining it in your YourNamespace\Module#getServiceConfig:
namespace YourNamespace;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Gaufrette\Filesystem;
use Gaufrette\Adapter\Local as LocalFs;
class Module implements ServiceProviderInterface, ConfigProviderInterface
{
public function getConfig()
{
return array(
'your_namespace' => array(
'filesystem' => array(
'base_path' => 'data/files',
),
),
);
}
public function getServiceConfig()
{
return array(
'factories' => array(
'YourNamespace\Filesystem' => function (ServiceLocatorInterface $sl) {
$config = $sl->get('Config');
$basePath = $config['your_namespace']['filesystem']['base_path'];
return new Filesystem(new LocalFs($basePath, true));
},
),
);
}
}
You can then use service YourNamespace\Filesystem across all your application.
I also use it in conjunction with Symfony\Filesystem to handle file moving/copying/checking operations. Not everything must come from Zend Framework 2 to be used in a ZF2 application.