I try to get the databaseadapter with the following statement:
$dbAdapter = $this->getServiceLocator()->get('db');
I get an error
"A plugin by the name "getServiceLocator" was not found in the plugin manager Zend\Mvc\Controller\PluginManager"
While searching in my module.php (is this the right one file to look at?) I think it might be an understanding problem. I read the article from Ralph Eggert and the zend documentation. I understood that I can get any config info with the servicemanager. But all documentations I found are always for Zend2.
So in my module.php I see something like this (snippet):
public function getServiceConfig()
{
return [
'factories' => [
Model\ImportTable::class => function($container) {
$tableGateway = $container->get(Model\ImportTableGateway::class);
return new Model\ImportTable($tableGateway);
},
Model\ImportTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Import());
return new TableGateway('t_dcl', $dbAdapter, null, $resultSetPrototype);
},
Model\DclimportTable::class => function($container) {
$tableGateway = $container->get(Model\DclimportTableGateway::class);
return new Model\DclimportTable($tableGateway);
},
Model\DclimportTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Dclimport());
return new TableGateway('t_dcl_import', $dbAdapter, null, $resultSetPrototype);
},
There I see a variable $dbAdapter, but how can I get this variable? The error above might be because I use ZEND3 now? Is this method deprecated? I couldn't find any migration info.
Anyway, can somebody explain to me, how to get these keys out of the module.php and in which case create own factories there? I know it is a really basic question, but I think if I won't get this right it will always overtake me again.
The service locator inside controllers was deprecated in version 2.7 and removed in version 3.0.
To fix your code:
Find all cases where you call getServiceLocator(), and identify the services they retrieve.
Update your controller to accept these services via the constructor.
If you have not already, create a factory class for your controller.
In the factory, pull the appropriate services and pass them to the controller's constructor.
More detailed info can be found in the migration docs.
Related
First off, I am building using Symfony components. I am using 3.4. I was following the form tutorial https://symfony.com/doc/3.4/components/form.html which lead me to this page
https://symfony.com/doc/current/forms.html#usage
I noticed that Symfony added a Form directory to my application.
This was great! I thought I was on my way. So, in my controller, I added this line.
$form = Forms::createFormFactory();
When I tried loading the page, everything went well with no error messages until I added the next two lines.
->addExtension(new HttpFoundationExtension())
->getForm();
I removed the ->addExtension(new HttpFoundationExtension()) line and left the ->getForm() thinking it would process without the add method call. It did not. So, I backed up to see if the IDE would type hint for me.
In the IDE PHPStorm, these are the methods that I have access to but not getForm per the tutorial
Every tutorial I have tried ends with not being able to find some method that does not exist. What do I need to install in order to have access to the ->getForm() method?
UPDATE:
I have made a couple of steps forward.
$form = Forms::createFormFactory()
->createBuilder(TaskType::class);
The code above loads with no errors. (Why is still fuzzy). But next stop is the createView(). None existant also. I only get hinted with create().
Reading between the lines in this video help with the last two steps. https://symfonycasts.com/screencast/symfony3-forms/render-form-bootstrap#play
UPDATE 2:
This is what I have now.
$session = new Session();
$csrfManager = new CsrfTokenManager();
$help = new \Twig_ExtensionInterface();
$formFactory = Forms::createFormFactoryBuilder()
->getFormFactory();
$form = $formFactory->createBuilder(TaskType::class)
->getForm();
//$form->handleRequest();
$loader = new FilesystemLoader('../../templates/billing');
$twig = new Environment($loader, [
'debug' => true,
]);
$twig->addExtension(new HeaderExtension());
$twig->addExtension(new DebugExtension());
$twig->addExtension($help, FormRendererEngineInterface::class);
return $twig->render('requeueCharge.html.twig', [
'payments' => 'Charge',
'reportForm' => $form->createView()
]);
Does anyone know of an update standalone for example? The one that everyone keeps pointing two is 6 years old. There have been many things deprecated in that time period. So, it is not an example to follow.
Your Form class and method createFormFactory must return object that implement FormBuilderInterface then getForm method will be available. You need create formBuilder object.
But this can't be called from static method because formBuilder object need dependency from DI container. Look at controller.
If you want you need register your own class in DI and create formBuilder object with dependencies and return that instance of object.
EDIT
You don't need to use abstract controller. You can create your own class which is registered in DI for geting dependencies. In that class you create method which create new FormBuilder(..dependencies from DI from your class ...) and return instance of that FormBuilder. Then you can inject your class in controller via DI.
Example (not tested)
// class registered in DI
class CustomFormFactory
{
private $_factory;
private $_dispatcher;
public CustomFormFactory(EventDispatcherInterface $dispatcher, FormFactoryInterface $factory)
{
$_dispatcher = $dispatcher;
$_factory = $factory;
}
public function createForm(?string $name, ?string $dataClass, array $options = []): FormBuilderInterface
{
// create instance in combination with DI dependencies (factory..) and your parameters
return new FormBuilder($name, $dataClass, $_dispatcher, $_factory, $options);
}
}
Usage
$factory = $this->container->get('CustomFormFactory');
$fb = $factory->createForm();
$form = $fb->getForm();
I started using zend 3 a few months ago for a project and now I'm stuck.
I have a customized authentication (not using zend authentication module) which is working fine but I need to validate every time i access a redirected page.
Because on every page's url goes a token that is used to check in database, and I'm trying to do inside the function onBootStrap().
I learned to use factories, models, mappers and I'm currently using them in some controllers, but i can't find a way to achieve, at least if i could get the dbAdapter from the bootstrap event to use, it will be enough.
Any thoughts?
You can use lazy event for this job. here's you are a simple example.
public function onBootstrap(EventInterface $e)
{
/** #var \Interop\Container\ContainerInterface $container */
$container = $e->getApplication()->getserviceManager();
$events = $e->getApplication()->getEventManager();
$events->attach(MvcEvent::EVENT_ROUTE, new LazyListener([
'listener' => Listener::class,
'method' => 'onRoute'
], $container));
}
So you can check you auth on Listener class' "onRoute" method. If MvcEvent::ROUTE event is too early for you, you can use other MvcEvents too.
Hope this can solve your problem.
To get the adapter and use it in tablegateway
public function onBootstrap(EventInterface $event){
$container = $event->getApplication()->getServiceManager();
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new TableObjectClass());
$tableGateway = new TableGateway("Table name", $dbAdapter, null, $resultSetPrototype);
$mapper = new TableObjectClassMapper($tableGateway);
//and use $mapper to get data from the table and store it as TableObjectClass
$data = $mapper->fetch()->current();
}
I am new to ZF2 and I want to test the login method in a legacy application. Or introduce Unit tests in old code :).
The code that I have is not done according to the manual; it seems super strange if I compare it to the manual examples or even best practices.
I the login method like this:
http://pastebin.com/ZzvuBcGe
in this case the legacy is that Helper, Carts, Users, Userslogs and Usertests are models .... all of them extend DB.
In the module.config.php I have this code:
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
'AuthService' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$dbTableAuthAdapter = new DbTableAuthAdapter(
$dbAdapter,
'tbl_user',
'USER_LOGIN',
'USER_PASSWORD',
'MD5(?)'
);
$authService = new AuthenticationService();
$authService->setAdapter($dbTableAuthAdapter);
$authService->setStorage(new StorageSession('session'));
return $authService;
},
'Helper' => function ($sm) {
return new Helper($sm);
},
'Users' => function ($sm) {
return new Users($sm);
},
'Carts' => function ($sm) {
return new Carts($sm);
}
...
I know that the DbTableAuthAdapter is deprecated but I have to understand how to modify this in order to change it in the best way possible. I have the feeling if I change this all the User, Carts etc models will crash.
My Unit test is like this for the moment:
<?php namespace ApplicationTest\Controller;
use Application\Controller\LoginController;
use Zend\Stdlib\ArrayUtils;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class LoginControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
parent::setUp();
// The module configuration should still be applicable for tests.
// You can override configuration here with test case specific values,
// such as sample view templates, path stacks, module_listener_options,
// etc.
$configOverrides = [];
$this->setApplicationConfig(ArrayUtils::merge(
// Grabbing the full application configuration:
include __DIR__ . '/../../../../../config/application.config.php',
$configOverrides
));
}
public function loginCredentialsProvider()
{
return [
['userDev', '12345'],
];
}
/**
* #covers LoginController::loginAction()
* #dataProvider loginCredentialsProvider
* #param $username
* #param $password
*/
public function testLogin($username, $password)
{
// prepare request
//$this->getRequest()
//->setMethod('POST')
//->setPost(new Parameters(array(
//'user_login' => $username,
//'user_password' => $password
//)));
$helperMock = $this->getMockBuilder('Application\Model\Helper')
->disableOriginalConstructor()
->getMock();
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Application\Model\Helper', $helperMock);
// send request
$this->dispatch('/login', 'POST', $this->loginCredentialsProvider());
$this->assertEquals('userDev12345', $username . $password);
// $this->markTestIncomplete('login incomplete');
}
/**
* #depends testLogin
*/
public function testLogout()
{
$this->markTestIncomplete('logout incomplete');
}
}
I tried different ways to test but no succes and of course that I get errors:
Zend\ServiceManager\Exception\ServiceNotCreatedException: An exception was raised while creating "Helper"; no instance returned
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:930
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:1057
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:633
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:593
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:525
/project/module/Application/src/Application/Controller/LoginController.php:38
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractActionController.php:83
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:116
/project/vendor/zendframework/zendframework/library/Zend/Mvc/DispatchListener.php:113
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php:313
/project/vendor/zendframework/zendframework/library/Zend/Test/PHPUnit/Controller/AbstractControllerTestCase.php:282
/project/module/Application/test/ApplicationTest/Controller/LoginControllerTest.php:69
/project/vendor/phpunit/phpunit/phpunit:47
Caused by
Zend\ServiceManager\Exception\ServiceNotCreatedException: An exception was raised while creating "Zend\Db\Adapter\Adapter"; no instance returned
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:930
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:1055
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:633
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:593
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:525
/project/module/Application/src/Application/Model/DB.php:17
/project/module/Application/config/module.config.php:1324
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:923
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:1057
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:633
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:593
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:525
/project/module/Application/src/Application/Controller/LoginController.php:38
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractActionController.php:83
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:116
/project/vendor/zendframework/zendframework/library/Zend/Mvc/DispatchListener.php:113
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php:313
/project/vendor/zendframework/zendframework/library/Zend/Test/PHPUnit/Controller/AbstractControllerTestCase.php:282
/project/module/Application/test/ApplicationTest/Controller/LoginControllerTest.php:69
/project/vendor/phpunit/phpunit/phpunit:47
Caused by
PHPUnit_Framework_Error_Notice: Undefined index: db
/project/vendor/zendframework/zendframework/library/Zend/Db/Adapter/AdapterServiceFactory.php:26
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:923
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:1055
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:633
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:593
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:525
/project/module/Application/src/Application/Model/DB.php:17
/project/module/Application/config/module.config.php:1324
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:923
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:1057
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:633
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:593
/project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:525
/project/module/Application/src/Application/Controller/LoginController.php:38
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractActionController.php:83
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/AbstractController.php:116
/project/vendor/zendframework/zendframework/library/Zend/Mvc/DispatchListener.php:113
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:468
/project/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php:207
/project/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php:313
/project/vendor/zendframework/zendframework/library/Zend/Test/PHPUnit/Controller/AbstractControllerTestCase.php:282
/project/module/Application/test/ApplicationTest/Controller/LoginControllerTest.php:69
/project/vendor/phpunit/phpunit/phpunit:47
The issues that I have are first how to get the test to pass with this code? I know that normally you do the test and after that the code but I need a starting point to understand the mess that I have in the application. Second, what is the easy or the best way to modify the "models" to not be a dependency for each method and then pass the test? How to modify the deprecated DbTableAuthAdapter in order not to brake all things?
Like i said I am new to ZF2 and Phpunit and I am stuck over this messy code and I have the best practices in my mind but I don't know how to put them in action in this code. Thank you for all the info that I will receive for this.
LATER EDIT
the solution is to add this line in the test, foreach model:
// access via application object..
$bla = $this->getApplication()->getServiceManager()->get('Tests');
the solution is to add this line in the test, foreach model:
$bla = $this->getApplication()->getServiceManager()->get('Tests');
Thank you i336_ :)
I have factories for Doctrine in Module.php method getServiceConfig() :
public function getServiceConfig()
{
return array(
'factories' => array(
'doctrine.entitymanager.orm_cst' => new \DoctrineORMModule\Service\EntityManagerFactory('orm_cst'),
'doctrine.connection.orm_cst' => function ($sm) {
$config = $sm->get('config');
return new \DoctrineORMModule\Service\DBALConnectionFactory('doctrine.entitymanager.' . $config['connection']);
},
'doctrine.configuration.orm_cst' => new \DoctrineORMModule\Service\ConfigurationFactory('orm_cst'),
'doctrine.driver.orm_cst' => new \DoctrineModule\Service\DriverFactory('orm_cst'),
'doctrine.eventmanager.orm_cst' => new \DoctrineModule\Service\EventManagerFactory('orm_cst'),
),
);
}
I'm trying to get connection value from config and I'm getting the following error:
Catchable fatal error: Object of class DoctrineORMModule\Service\DBALConnectionFactory could not be converted to string in W:\domains\zf\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php on line 939
It's ok if I'm not using function as array value:
'doctrine.connection.orm_cst' => new \DoctrineORMModule\Service\DBALConnectionFactory('orm_cst'),
What am I doing wrong? Please help.
The doctrine.connection should return a configured \Doctrine\DBAL\Connection.
At the moment, you are incorrectly returning the actual ZF2 factory instance (\DoctrineORMModule\Service\DBALConnectionFactory) rather than using it to create the connection.
If you wish to keep the closure, you can just manually call the createService() method and it should work.
'doctrine.connection.orm_cst' => function ($sm) {
$config = $sm->get('config');
$key = 'doctrine.entitymanager.' . $config['connection'];
$factory = new DBALConnectionFactory($key);
// Manually call the createService method and the factory will then
// return the Connection instance
return $factory->createService($sm);
},
The other (preferred) option would be to extend the default Doctrine factory and define the configuration key within the factory itself, this way you have everything needed to create the connection in one place (which is really the idea behind using a factory).
If you are not using the above closure, it is also worth noting that by creating your service factories with new you are recreating every service factory on every request - This will have an unnecessary negative performance impact. The solution again would be to extend and/or wrap the Doctrine factories in your own custom factory and just use a string to reference them. The service manager will then be able to lazy load them.
'doctrine.connection.orm_cst' => 'MyModule\Factory\CstConnectionFactory',
I'm using ZFCUser and need to develop a German project.
Unfortunately the login form is in English and I couldn't find a way to translate the form fields or especially the error messages to English.
Is there maybe a global way for the module to either overwrite the messages or switch the language?
Thanks!
Edit:
This is my translator call in my bootstrap:
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
AbstractValidator::setDefaultTranslator($translator);
Edit II:
My custom translation file:
<?php
return array(
// ZFCUser
"Authentication failed. Please try again." => "test"
);
My factory:
<?php
class CustomTranslatorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/.../TranslationTable.php',
'default',
'de_DE'
);
return $translator;
}
}
Module.php from my User module:
public function getServiceConfig()
{
return array(
'factories' => array(
'translator' => 'Path\To\Translator\CustomTranslatorFactory',
),
);
}
Edit III:
My Translator looks like this. Even when using a PHP file instead of the array just nothing happens. No error, no translation. Any ideas what I'm doing wrong?
$custom_translations = array(
"Authentication failed. Please try again." => "test",
);
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
$translator->addTranslationFile(
'phpArray',
$custom_translations
);
return $translator;
Just for Your knowledge - since yesterday there's a modular de_DE translation available (pl_PL and ja_JP are available too). It covers only ZfcUser messages, so not all validators, but form labels etc. https://github.com/websafe/zf-mod-zfc-user-i18n-de-de Easy installable and available via Packagist too.
I'm creating an answer for that because it's becoming too complex for the comment section.
The validator library does translation of messages on its own. As you've assigned the translator to it, your validation messages are fine.
However, form label translation belongs to another piece of library. Accordingly, they also need a translator assigned to them. As stated above in the comments, you can either do that manually (by invoking $viewHelper->setTranslator($translator)) or let the ViewHelperManager do that for you.
You can easily refactor your code to support the second case.
Create a factory class for your translator.
Register that factory to SM. Use the "translator" key.
If you need it, retrieve your $translation var through SM in future.
Example (uses skipped):
/** One of your modules, should be a base module that's always loaded */
class Module
{
// ...
public function getServiceConfig()
{
return array(
'factories' => array(
'translator' => 'Your\Translator\Factory', // could also be a closure (anonymous function)
)
);
}
}
-
class YourTranslatorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
return $translator;
}
}
If you need to access it, simpyl retrieve it from SM as you're used to ($translator = $sm->get('translator');).
Note: Validators implement the TranslatorAwareInterface. This means that, if you've registered the translator key to SM, they also should be injected into the validators automatically. Thus, you can even skip the static method call.
Note also: This is just an example of how you can do it without changing much. You could also reach this goal just by configuration.