Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer - zend-framework

I am getting below error message while using service manager.
How can i resolve this via different approach like constuct....
Deprecated: You are retrieving the service locator from within the
class Users\Controller\LoginController. Please be aware that
ServiceLocatorAwareInterface is deprecated and will be removed in
version 3.0, along with the ServiceLocatorAwareInitializer. You will
need to update your class to accept all dependencies at creation,
either via constructor arguments or setters, and use a factory to
perform the injections. in
C:\wamp64\www\ZendSkeletonApplication-master\vendor\zendframework\zend-mvc\src\Controller\AbstractController.php
on line 258
Below code i have added in module.php
public function getServiceConfig() {
return array(
'abstract_factories' => array(),
'aliases' => array(),
'factories' => array(
// FORMS
'LoginForm' => function ($sm) {
$form = new \Users\Form\LoginForm();
$form->setInputFilter($sm->get('LoginFilter'));
return $form;
},
)
)
}
and from login controller, index action i calling below code
$form = $this->getServiceLocator()->get('LoginForm');
$viewModel = new ViewModel(array('form' => $form));
return $viewModel;
Any help is highly appreciated.
Currently i am using Zend framework 2.5.1 Version
In Zend framework 2.3 Version it was working fine.
Update
Now i am using below code in my controller
// Add this property:
private $table;
// Add this constructor:
public function __construct(LoginForm $table) {
$this->table = $table;
}
and in module.php
// FORMS
Model\AlbumTable::class => function ($sm) {
$form = new \Users\Form\LoginForm();
$form->setInputFilter($sm->get('LoginFilter'));
return Model\AlbumTable;
},
But still i am getting below error
Catchable fatal error: Argument 1 passed to
Users\Controller\LoginController::__construct() must be an instance of
Users\Form\LoginForm, none given, called in
C:\wamp64\www\ZendSkeletonApplication-master\vendor\zendframework\zend-servicemanager\src\AbstractPluginManager.php
on line 252 and defined in
C:\wamp64\www\ZendSkeletonApplication-master\module\Users\src\Users\Controller\LoginController.php
on line 22

There was a lot of problem in the use of serviceLocator in ZF2, Zend tech' did a great job by removing the serviceLocatorAware from the framework, and remove the serviceManager from controllers.
Why ?
Just because some entry and experienced developpers used it in an ugly way, and way too much.
From my point of view, the serviceLocator is meant to be used only in factories.
That's why i keep advising other developper to create factories, without using anonymous function.
Here an example of a controller's factory (not the same as service's factories) : https://github.com/Grafikart/BlogMVC/blob/master/ZendFramework2/module/Blog/src/Blog/Factory/PostControllerFactory.php
And its config line https://github.com/Grafikart/BlogMVC/blob/master/ZendFramework2/module/Blog/config/module.config.controllers.php#L8
And here a Service's factory
<?php
namespace Blog\Factory;
use Blog\Service\CategoryService;
use Doctrine\Common\Persistence\ObjectManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class CategoryServiceFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return CategoryService
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
/** #var ObjectManager $em */
$em = $serviceLocator->get('orm_em');
return new CategoryService($em);
}
}
You can do a factory for almost all of your component, even form, you just need to declare those as factories in your config like this :
You can replace the key form_elements by :
controllers
service_manager
view_helpers
validators
It will works the same way :
'form_elements' => array(
'factories' => array(
'Application\Item\Form\Fieldset\ProfileFieldset' =>
'Application\Item\Factory\ProfileFieldsetFactory',
),
'invokables' => array(
'EntityForm' => 'Application\Entities\Form\EntityForm',
'PropertyForm' => 'Application\Item\Form\PropertyForm',
'ProfileForm' => 'Application\Item\Form\ProfileForm',
),
'initializers' => array(
'ObjectManagerInitializer' => 'Application\Initializers\ObjectManagerInitializer',
),
),
Your last error means that your controller is not correctly instanciated, you not give the LoginForm instance, maybe because you didn't create a factory ? Is your controller declared as an invokables ?

For an in depth discussion on deprecating the ServiceLocatorAwareInterface, please read this article by Matthew Weier O'Phinney. Basically, you should avoid hidden dependencies in your controllers by simply setter injecting them through factories as mentioned previously by Hooli.

Related

field array type in entity for form choice type field symfony

I would like to create a UserForm for create user in my system backend.
I use a entity with a 'role' field as type array
I want use a select choice field type Form with that entity field.
I use a transformer class system for convert data between Entity and form.
but I turn around in my head and nothing run correctly.
When I use options 'multiple' of choice type, my field display correctly but I don't want to display and select multiple value for this field.
I have Notice: Undefined offset: 0 error
or
I have ContextErrorException: Notice: Array to string conversion
Here few essential code :
UserForm class
$builder->add($builder->create('roles', 'choice', array(
'label' => 'I am:',
'mapped' => true,
'expanded' => false,
'multiple' => false,
'choices' => array(
'ROLE_NORMAL' => 'Standard',
'ROLE_VIP' => 'VIP',
)
))->addModelTransformer($transformer));
transformer Class
class StringToArrayTransformer implements DataTransformerInterface
{
public function transform($array)
{
return $array[0];
}
public function reverseTransform($string)
{
return array($string);
}
}
controller method
$user = new User(); //init entity
$form = $this->createForm(new UserForm(), $user);
$form->handleRequest($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($form);
$em->flush();
return $this->redirect($this->generateUrl('task_success'));
}
entity part
/**
* #ORM\Column(name="roles", type="array")
*/
protected $roles;
public function getRoles()
{
return $this->roles;
}
public function setRoles(array $roles)
{
$this->roles = $roles;
return $this;
}
My field roles entity must be a array for run correctly the security component Symfony
can you help me to understand why this field form refuse to display ?
I already readed others questions in same issue but there is anything that I don't understand because nothing help me to resolve my problem.
If you can help me with MY particular context...
Thank for support
because security symfony component integration
If you only need the "getRoles" method because of the interface you are implementing, it is simpler (and cleaner) to do the following:
Change the entities field again to role with type string
Rename your getter and setter to getRole() and setRole()
and add a getRoles method like this:
public function getRoles()
{
return array($this->role);
}
In your form type, change the field name to "role" and 'multiple' => false
Remove your model transformer
This should be the solution ;)

unit testing legacy code

I'm new to unit testing and trying to unit test the model validation of an old zend application which is using forms.
Inside one of the forms it creates an instance of a second class and I'm struggling to understand how I can mock the dependent object. The form reads as follows :
class Default_Form_Timesheet extends G10_Form {
public function init() {
parent::init();
$this->addElement( 'hidden', 'idTimesheet', array( 'filters' => array ('StringTrim' ), 'required' => false, 'label' => false ) );
$this->addElement('checkbox', 'storyFilter', array('label' => 'Show my stories'));
$user = new Default_Model_User();
$this->addElement('select', 'idUser', array('filters' => array('StringTrim'), 'class' => 'idUser', 'required' => true, 'label' => 'User'));
$this->idUser->addMultiOption("","");
$this->idUser->addMultiOptions($user->fetchDeveloper());
...
......
My problem occurs when the call is made to $user->fetchDeveloper(). I suspect it has something todo with mocking objects and dependency injection but any guidence would be appreciated. My Failing unit test reads as follows...
require_once TEST_PATH . '/ControllerTestCase.php';
class TimesheetValidationTest extends ControllerTestCase {
public $Timesheet;
public $UserStub;
protected function setUp()
{
$this->Timesheet = new Default_Model_Timesheet();
parent::setUp();
}
/**
* #dataProvider timesheetProvider
*/
public function testTimesheetValid( $timesheet ) {
$UserStub = $this->getMock('Default_Model_User', array('fetchDeveloper'));
$UserStub->expects( $this->any() )
->method('fetchDeveloper')
->will( $this->returnValue(array(1 => 'Mickey Mouse')));
$Timesheet = new Default_Model_Timesheet();
$this->assertEquals(true, $Timesheet->isValid( $timesheet ) );
}
My data provider is in a separate file.
It is terminating at the command line with no output and I'm a bit stumped. Any help would be greatly appreciated.
You can't mock the Default_Model_User class in your test for the form. Because your code is instantiating the class internally you are not able to replace it with a mock.
You have a couple of options for testing this code.
You look into what fetchDeveloper is doing and control what it is returning. Either via a mock object that you can inject somewhere (looks unlikely) or by setting some data so that you know what the data will be. This will make your test a little brittle in that it could break when the data you are using changes.
The other option is to refactor the code so that you can pass the mock into your form. You can set a constructor that would allow you to set the Default_Model_User class and then you would be able to mock it with your test as written.
The constructor would like like this:
class Default_Form_Timesheet extends G10_Form {
protected $user;
public function __construct($options = null, Default_Model_User $user = null){
if(is_null($user)) {
$user = new Default_Model_User();
}
$this->user = $user;
parent::__construct($options);
}
Zend Framework allows options to be passed to forms constructor which I am not sure if you use in your code anywhere so this should not break any of your current functionality. When can then pass an optional Default_Model_User again so as to not break your current functionality. You need to set the values for $this->user before calling parent::__construct otherwise Zend will throw an error.
Now your init function will have to change from:
$user = new Default_Model_User();
to
$user = $this->user;
In your test you can now pass in your mock object and it will be used.
public function testTimesheetValid( $timesheet ) {
$UserStub = $this->getMock('Default_Model_User', array('fetchDeveloper'));
$UserStub->expects( $this->any() )
->method('fetchDeveloper')
->will( $this->returnValue(array(1 => 'Mickey Mouse')));
$Timesheet = new Default_Model_Timesheet(null, $UserStub);
$this->assertEquals(true, $Timesheet->isValid( $timesheet ) );
}
Creating a mock doesn't replace the object so that when new is called that your mock object is created. It creates a new object that extends your class that you can now pass around. new is a death to testability.

Zend\Form: Call to a member function insert() on a non-object in Zend/Form/Fieldset.php

I am learning how to use Zend Framework 2 (2.1.4) forms and running into this error.
Call to a member function insert() on a non-object in ... /Zend/Form/Fieldset.php on line 178
I don't want use the form to automatically connect to a database, in fact I only want to use the form to help validate and will pull from and populate it with an array of values. How do I turn off the database connectivity in the form objects?
I am used to dealing with the ZF1 forms so this new form system is confusing. Once I thought about it though, the way we can use the form elements in our view scripts for formatting is going to be nice. Those old decorators were a pain. Anyway, for me, it would be nice to use the forms without dealing with bound database objects. Is this possible? It just seems so overly complicated to need a model class using InputFilterAwareInterface classes in addition to a simple form. One step at a time though, I can't even get the form to display.
I appreciate any help.
Below are my controller, form, and view scripts:
Form class:
namespace FBWeb\Form;
use Zend\Form\Form;
use Zend\Form\Element;
class ClientForm extends Form
{
public function __construct()
{
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'client',
'type' => 'Zend\Form\Element\Text',
'options' => array(
'label' => 'Client Name',
),
'attributes' => array(
'type' => 'text',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Add'
),
));
}
}
Controller class:
namespace FBWeb\Controller;
use Zend\Debug\Debug;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Session\Container;
use Zend\Http\Request;
use FBWeb\Form\ClientForm;
class ClientController extends AbstractActionController
{
public function indexAction()
{
$clientform = new ClientForm();
return array('form' => $clientform);
}
}
index.phtml view script:
<div id="clientformtable">
<?php
$form = $this->form;
$form->setAttribute('action','/app/client/add');
$form->prepare();
echo $this->form()->openTag($form);
$client = $form->get('client');
echo $this->formRow($client);
echo $this->form()->closeTag();
?>
</div>
This, and similar error messages, happen due to the fact that the form isn't properly set up. As you can see within the code above the __construct() function doesn't call the parents constructor. Therefore the internal "bootstrapping" doesn't happen and the error occurs.
You have to make sure to always call the parents constructor when dealing with Zend\Form\Form and/or Zend\Form\Fieldset.
parent::__construct('client-form');

Why does not work short names when I create custom form elements in Zend Framework 2?

I create custom element like here: ZF2Docs: Advanced use of Forms
1.Create CustomElement class in Application/Form/Element/CustomElement.php
2.Add to my Module.php function
public function getFormElementConfig()
{
return array(
'invokables' => array(
'custom' => 'Application\Form\Element\CustomElement',
),
);
}
If I use FQCN it works fine:
$form->add(array(
'type' => 'Application\Form\Element\CustomElement',
'name' => 'myCustomElement'
));
But if I use short name:
$form->add(array(
'type' => 'Custom',
'name' => 'myCustomElement'
));
throws Exception:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create
an instance for Custom
Problem
The error is probably due to how you are instantiating the $form object. If you just use the new Zend\Form\Form expression or something similar the form will not be set up with the correct service locator.
$form = new \Zend\Form\Form;
$form->add(array(
'type' => 'custom',
'name' => 'foobar',
));
Solution
The trick here is to use the FormElementManager service locator to instantiate the form.
// inside a controller action
$form = $this->getServiceLocator()->get('FormElementManager')->get('Form');
$form->add(array(
'type' => 'custom',
'name' => 'foobar',
));
Better yet, define a form() method in your controller as a shortcut to do this for you:
class MyController extends AbstractActionController
{
public function form($name, $options = array())
{
$forms = $this->getServiceLocator()->get('FormElementManager');
return $forms->get($name, $options);
}
public function createAction()
{
$form = $this->form('SomeForm');
// ...
}
}
Explanation
Each form object is attached to a form factory which is in turn attached to a service locator. This service locator is in charge of fetching all the classes used to instantiate new form/element/fieldset objects.
If you instantiate a new form object (all by itself), a blank service locator is instantiated and used to fetch later classes within that form. But each subsequent object is then attached to that same service locator.
The problem here is that getFormElementConfig configures a very specific instance of this service locator. This is the FormElementManager service locator. Once it's configured, all forms pulled from this service locator will then be attached to this service locator and will be used to fetch other elements/fieldsets etc.
Hope this solves your issue.

symfony override BaseForm class howto

I've installed the sfDoctrineGuard plugin. Everything is working, I can use the /sf_guard_user/edit/:id page to edit a user.
I didn't like the way the permissions were listed as a select list, I wanted to display them as individual checkboxes split up based on the permission name. To do this I created a custom widget that extends sfWidgetFormChoice. This is working the way I want it as well, but my problem is the following:
To use my custom widget, I edited the following lines in this file:
lib/form/doctrine/sfDoctrineGuardPlugin/base/BasesfGuardUserForm.class.php
Before:
'groups_list' => new sfWidgetFormDoctrineChoice(array('multiple' => true, 'model' => 'sfGuardGroup')),
'permissions_list' => new sfWidgetFormDoctrineChoice(array('multiple' => true, 'model' => 'sfGuardPermission')),
After:
'groups_list' => new sfWidgetFormDoctrineChoice(array('multiple' => true, 'model' => 'sfGuardGroup', 'expanded' => true)),
'permissions_list' => new myCustomPermissionWidget(),
That gives the correct outcome.
The problem is that I shouldn't have edited the Base class as any time I build my model the file is overwritten.
So I should edit this file:
lib/form/doctrine/sfDoctrineGuardPlugin/sfGuardUserForm.class.php
class sfGuardUserForm extends PluginsfGuardUserForm
{
public function configure()
{
parent::configure();
$this->setWidgets(array(
'groups_list' => new sfWidgetFormDoctrineChoice(array('multiple' => true, 'model' => 'sfGuardGroup', 'expanded' => true)),
'permissions_list' => new myCustomPermissionWidget(),
));
}
}
But this does not work. I've tried the code inside a new function setup(), with parent::setup() before and after my code but still nothing.
PluginsfGuardUserForm is abstract and extends BasesfGuardUserForm but I don't see why that would stop it from working.
Any ideas?
Thanks
I believe the edit user action uses the class sfGuardUserAdminForm which is in the plugin directory
Copy the file
plugins/sfDoctrineGuardPlugin/lib/form/doctrine/sfGuardUserAdminForm.class.php
into
lib/form/doctrine/
Then add this line to the configure() method
$this->setWidget('permissions_list' => new myCustomPermissionWidget());
You do not need to add a call to parent::configure() it is bad practice to do this in the form framework and you should only do it if you know you need to.
Try editing the
lib/vendor/symfony/lib/plugins/sfDoctrineGuardPlugin/lib/form/doctrine/PluginsfGuardUserForm.class.php
"vendor" and "symfony" will be whatever you have it as on your install. This worked for me when I wanted to remove the remember me checkbox from the signin form:
<?php
/**
* sfGuardFormSignin for sfGuardAuth signin action
*
* #package sfDoctrineGuardPlugin
* #subpackage form
* #author Fabien Potencier <fabien.potencier#symfony-project.com>
* #version SVN: $Id: sfGuardFormSignin.class.php 23536 2009-11-02 21:41:21Z Kris.Wallsmith $
*/
class sfGuardFormSignin extends BasesfGuardFormSignin
{
/**
* #see sfForm
*/
public function configure()
{
$this->widgetSchema->setFormFormatterName('list');
unset($this['remember']);
}
}
Was as simple as that.
Hope it helps
Luke