I have two controllers in my Module and both of them need to see if the user is logged in or not. The Login controllers authenticates the user using DbTable and writes the identity into the storage.
I am using >Zend\Authentication\AuthenticationService; $auth = new AuthenticationService();
inside the controller function but then i instantiate its instance on multiple pageAction()
for this i wrote a function into the Module.php
as follows
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Config\DbAdapter' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
return $dbAdapter;
},
'Admin\Model\PagesTable' => function($sm){
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$pagesTable = new PagesTable(new TableGateway('pages',$dbAdapter) );
return $pagesTable;
},
'Admin\Authentication\Service' => function($sm){
return new AuthenticationService();
}
),
);
}
As you can see i am returning new AuthenticationService() every time which i think is bad. I could not find how to grab the already instantiated instance of the service or
i have to write a singleton class for this. Please advise any sample code snipets with deeper explaination would be highly regarded and appreciated thanks.
Try this instead:
public function getServiceConfig()
{
return array(
'aliases' => array(
'Application\Config\DbAdapter' => 'Zend\Db\Adapter\Adapter',
'Admin\Authentication\Service' => 'Zend\Authentication\AuthenticationService',
),
'factories' => array(
'Admin\Model\PagesTable' => function ($serviceManager) {
$dbAdapter = $serviceManager->get('Application\Config\DbAdapter');
$tableGateway = new TableGateway('pages', $dbAdapter);
$pagesTable = new PagesTable($tableGateway);
return $pagesTable;
},
),
);
}
Note mainly the 'aliases' section of the root array, any other changes are just cosmetic and you may prefer to do the original way you suggested (such as using a factory to retrieve the Zend\Db\Adapter\Adapter instance instead of aliasing that too).
Kind Regards,
ise
Related
I have a ZF3 project and an index controller that accesses a single postgres table. I have the usual factories setup
return array(
'factories' => [
Model\IsdepotstockTable::class => function($container) {
$tableGateway = $container->get(Model\IsdepotstockTableGateway::class);
return new Model\IsdepotstockTable($tableGateway);
},
Model\IsdepotstockTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Isdepotstock());
return new TableGateway('isdepotstock', $dbAdapter, null, $resultSetPrototype);
},
],
I also have my controller constructor thus:
public function __construct(IsdepotstockTable $table)
{
$this->isdepotstockTable = $table;
}
My question is, if I wish to access a second table, how do I modify the construct statement to handle multiple tables? Obviously I have to add the factories for the additional table that much I understand.
I've looked through the ZF3 documentation but cannot find any example.
Thanks
I believe you are looking for a controller factory function which will instantiate the controller and pass the arguments to the constructor.
Add your second table class as the 2nd argument in the __construct() method, then create your factory in module.config.php.
<?php
use Zend\ServiceManager\Factory\InvokableFactory;
return [
// ...
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class
// Put other controllers registration here
],
],
// ...
];
Here is a free open source book on ZF3, I link to the controller registration section for your reference. Good luck! https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Model_View_Controller/Controller_Registration.html
In current state I've got two modules - main module, and admin panel module.
Main module is called "Kreator", admin -> "KreatorAdmin". All the models are located inside the Kreator module (Kreator/Model/UserTable.php etc.).
"KreatorAdmin" is almost empty, there is a configuration for it:
KreatorAdmin/config/module.config.php
<?php
return array(
'controllers' => array(
'invokables' => array(
'KreatorAdmin\Controller\Admin' => 'KreatorAdmin\Controller\AdminController',
),
),
'router' => array(
'routes' => array(
'zfcadmin' => array(
'options' => array(
'defaults' => array(
'controller' => 'KreatorAdmin\Controller\Admin',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view'
),
),
);
KreatorAdmin/src/KreatorAdmin/AdminController.php
<?php
namespace KreatorAdmin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AdminController extends AbstractActionController
{
public function indexAction()
{
//$this->getServiceLocator()->get('Kreator\Model\UserTable');
return new ViewModel();
}
}
KreatorAdmin/Module.php
<?php
namespace KreatorAdmin;
class Module
{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
Simply adding "use" statements in controller and navigating by namespaces results in error
Argument 1 passed to KreatorAdmin\Controller\AdminController::__construct() must be an instance of Kreator\Model\UserTable, none given,
I also tried to play a bit with service manager as described here:
ZF2 Models shared between Modules but no luck so far.
How am I supposed to access UserTable from KreatorAdmin/src/KreatorAdmin/AdminController.php ?
Cheers!
update 1
I've added getServiceConfig to Module.php
public function getServiceConfig()
{
return [
'factories' => [
// 'Kreator\Model\UserTable' => function($sm) {
// $tableGateway = $sm->get('UserTableGateway');
// $table = new UserTable($tableGateway);
// return $table;
// },
// 'UserTableGateway' => function($sm) {
// $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
// $resultSetPrototype = new ResultSet();
// $resultSetPrototype->setArrayObjectPrototype(new User());
// return new TableGateway('user', $dbAdapter, null, $resultSetPrototype);
// },
'DbAdapter' => function (ServiceManager $sm) {
$config = $sm->get('Config');
return new Adapter($config['db']);
},
'UserTable' => function (ServiceManager $sm) {
return new UserTable($sm->get('UserTableGateway'));
},
'UserTableGateway' => function (ServiceManager $sm) {
$dbAdapter = $sm->get('DbAdapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new User());
return new TableGateway('users', $dbAdapter, null, $resultSetPrototype);
},
],
];
}
And updated controller
class AdminController extends AbstractActionController
{
protected $userTable;
public function indexAction()
{
$userTable = $this->getServiceLocator()->get('Kreator\Model\UserTable');
return new ViewModel();
}
}
First error - using commented version:
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter
Second - using uncommented part:
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Kreator\Model\UserTable
Solution
If anyone wonder. Using above configuration there is a correct solution in jobaer answer.
Using commented version, you have to remember to add
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
somewhere in config to service_manager.
May be you messed up with ZF2 and ZF3 configuration. I am not sure but somewhere may be, you tried to create a factory of AdminController by passing an instance of UserTable to make it available inside AdminController's action methods. And later you are not passing that instance of UserTable into the AdminController's constructor while working with it further. The highlighted part from the previous line results in that error.
In ZF2 you do not need to pass that UserTable instance in the controller's constructor for its availability. Just use the following one in any controller's action methods.
$userTable = $this->getServiceLocator()->get('UserTable');
If want to know how this process is done, please, refer to this part of the tutorial.
I was reading Zend 3 documentation on Service Manager and i got this problem.
In documentation it says that if we have some DI in our controller we should update module.config.php file and add controllers key and invoke controller not with InvokableFactory::class but with custom factory class and add another key service_manager that contains array of classes that my first controller uses.
Ok so i do that:
module.config.php
'service_manager' => [
'factories' => [
Controller\Controller2::class => Factory\Controller2Factory::class,
Controller\Controller3::class => Factory\Controller3Factory::class,
],
],
'controllers' => [
'factories' => [
Controller\Controller1::class => Factory\Controller1Factory::class
],
]
Controller1Factory.php
class Controller1Factory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new Controller1(
$container->get(Controller2::class),
$container->get(Controller3::class),
);
}
}
But now i have error that Controller2 and Controller3 also have DI in their constuctors, so i make new custom factories and so on and so on...until i get to my models.
And Models also have Dependency that is injected in their controller which is zend native \Zend\Db\TableGateway\TableGatewayInterface and i now have to edit my conf file again and add TableGatewayInterface.
And that is wrong. I should never be forced to inject native zend classes and services this way.
So what am i doing wrong?
If your Controller has no dependency, it's the best way to declare it in module.config.php as you did.
But if it has dependecies, it's better to do it in Module.php. You first declare your services, then the controller (don't forget to remove it from module.config.php), injecting in it the services it depends :
public function getServiceConfig()
{
return [
'factories' => [
Model\MyObjectTable::class => function($container) {
$tableGateway = $container->get(Model\MyObjectTableGateway::class);
return new Model\MyObjectTable($tableGateway);
},
Model\MyObjectTableGateway::class => function($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\User());
return new TableGateway('myObject', $dbAdapter, null, $resultSetPrototype);
},
]
];
}
public function getControllerConfig()
{
return [
'factories' => [
Controller\MyObjectController::class => function($container) {
return new Controller\MyObjectController(
$container->get(Model\MyObjectTable::class)
);
},
]
];
}
And in your controller:
private $table;
public function __construct(MyObjectTable $table)
{
$this->table = $table ;
}
It is described in This ZF3 tutorial page and following.
How can create a connection to my data/database.db
I get this connection only with one config but don't from another config in the same file module.php
In this code it's ok
public function getServiceConfig()
{
return array(
'factories' => array(
'Music\V1\Rest\Album\AlbumMapper' => function ($sm) {
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
return new \Music\V1\Rest\Album\AlbumMapper($adapter);
},
),
);
}
But if I try with two connection doesn't work:
public function getServiceConfig()
{
return array(
'factories' => array(
'Music\V1\Rest\Album\AlbumMapper' => function ($sm) {
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
return new \Music\V1\Rest\Album\AlbumMapper($adapter);
},
'Music\V1\Rest\Albumjson\AlbumjsonMapper' => function ($sm) {
$adapter = $sm->get('Zend\Db\Adapter\Adapter');
return new \Music\V1\Rest\Albumjson\AlbumjsonMapper($adapter);
},
),
);
}
I get the next error with zf2:
An abstract factory could not create an instance of musicv1restalbumjsoncontroller(alias: Music\\V1\\Rest\\Albumjson\\Controller).
The error has nothing to do with your database setup. It means your controller class cannot be found. Did you properly register a controller called Music\\V1\\Rest\\Albumjson\\Controller?
I'm having a problem with setting up a model table from a view helper. I have used the exact same code that I use within my regular controllers: e.g.:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Application\Model\MenusTable;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
**snipped**
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getMenusTable()
{
if (!$this->menusTable) {
$sm = $this->getServiceLocator();
$this->menusTable = $sm->get('Application\Model\MenusTable');
}
return $this->menusTable;
}
public function allLinks()
{
$all = $this->getMenusTable()->fetchAll();
return $all;
}
However I am met with this error:
Catchable fatal error: Argument 1 passed to Application\Model\MenusTable::__construct() must be an instance of Zend\Db\Adapter\Adapter, none given, called in C:\xampp\**snipped**\zend\library\Zend\ServiceManager\AbstractPluginManager.php on line 177 and defined in C:\xampp\**snipped**\Application\src\Application\Model\MenusTable.php on line 14
Everything works fine from the main controllers, but here I seem to hit a big problem - I'm new to Zend, but it appears to not be getting the factory from the Module.php file - is there any way to get it?
I have this in my Module.php - as said it works fine in a regular controller, but in a view helper it's not processed for some reason:
public function getServiceConfig()
{
return array
(
'factories' => array
(
'Application\Model\MenusTable' => function($sm)
{
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new MenusTable($dbAdapter);
return $table;
},
),
);
}
After re-reading your question I realized that your using ZF2.
here is a tutorial on using ServiceLocators http://framework.zend.com/wiki/display/ZFDEV2/Proposal+for+ServiceLocator+and+DependencyInjector
From the Documentation you need to define your DB connection.
$services = new ServiceLocator();
// Registering an object:
$services->set('db', $db);
// Lazy-loading by registering a closure:
$services->set('db', function() use ($config) {
$db = Db::factory($config->db);
return $db;
});
// Retrieving:
$db = $services->get('db');
First you should use the getServiceConfig() in your module ::
public function getServiceConfig()
{
return array(
'factories' => array(
'MODULE\Model\MenusTable' => function($sm) {
$tableGateway = $sm->get('MenusTableGateway');
$table = new MenusTable($tableGateway);
return $table;
},
'MenusTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Menus());
return new TableGateway('menus', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
THE ADAPTER might be in your /config/autoload/global.php, like this :
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
// CONNECTION DB
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=YOURDBNAME;host=localhost',
'username' => 'root',
'password' => '',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
Next your View helper must extend AbstractHelper but also implement ServiceLocatorAwareInterface
class MyViewHelper extends AbstractHelper implements ServiceLocatorAwareInterface
I'll put the code in my website