How to pass params from controller to Zend_Form? - zend-framework

I know this question is already answered here. But this doesnt work for me.
The Form is generated by using the PluginLoader:
$formClass = Zend_Registry::get('formloader')->load('Payment');
$form = new $formClass(array('someval' => $my_arr));
Payment.php:
class Form_Payment extends Zend_Form
{
protected $_someval = array();
public function init()
{
$this->setAction('payment/save');
//....
$this->addElement('multiCheckbox', 'store_id', array('label' => 'Someval:', 'required' => true, 'multiOptions' => $this->getSomeval()))
}
public function setSomeval($someval) {
$this->_someval = $someval;
}
public function getSomeval() {
return $this->_someval;
}
}
As I can see the load method only returns the class name, so new $formClass(); is equal new Form_Payment() but why this isn't accept params?

Ok I found a way by myself. I was looking for a way to inject some params while my Zend_Form was initialised. It seems the only way for this is to pass the params to the constructor - which is executed before the init method.
class Form_Payment extends Zend_Form
{
private $_someval;
public function __construct(array $params = array())
{
$this->_someval = $params['someval'];
parent::__construct();
}
public function init()
{
$this->setAction('payment/save');
//....
$this->addElement('multiCheckbox', 'store_id',
array('label' => 'Someval:',
'required' => true,
'multiOptions' => $this->_someval // passed params now available
))
}
}

You can add custom function to your form class like
class Form_Payment extends Zend_Form
{
public function init()
{
$this->setAction('payment/save');
// and so on
}
public function doSome()
{
$this->setAction('other/action');
}
}
and call it after instanciating form in controller
$form = new $formClass();
$form->doSome();

Related

How does Symfony controller as service work with __invoke method?

For example, in the following code:
/**
* #Route("/patients", service="bundle1.controller.patient.index")
*/
final class IndexController
{
private $router;
private $formFactory;
private $templating;
private $patientFinder;
public function __construct(RouterInterface $router, FormFactoryInterface $formFactory, EngineInterface $templating, PatientFinder $patientFinder)
{
$this->router = $router;
$this->formFactory = $formFactory;
$this->templating = $templating;
$this->patientFinder = $patientFinder;
}
/**
* #Route("", name="patients_index")
*/
public function __invoke(Request $request) : Response
{
$form = $this->formFactory->create(PatientFilterType::class, null, [
'action' => $this->router->generate('patients_index'),
'method' => Request::METHOD_GET,
]);
$form->handleRequest($request);
$patients = $this->patientFinder->matching($form->getData() ?: []);
return $this->templating->renderResponse('patient/index.html.twig', [
'form' => $form->createView(),
'patients' => $patients,
]);
}
}
Why is there a route annotation for __invoke that is empty?.
What is the lifecycle of this controller? I mean, when does Symfony creates the object and when executes the class to make use of __invoke?
Empty #Route annotation means that there is nothing after main route of class which is /patients. __invoke is a magic PHP method that is executed when you call your class as a function (without providing any method).
So __invoke method is executed when you hit the route /patients or when you call your service from any code.

Pass a value from controller to partial in ZF2

I want to pass some value from controller to view partial, basically, view partial is set using the placeholder in Application\Module.php, here is the code that does it.
<?php
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\View\Renderer\PhpRenderer;
use Zend\View\Resolver;
use Zend\ServiceManager\ServiceManager;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$eventManager->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this, 'initBreadcrumbAndActionButtonWidget'));
$moduleRouteListener->attach($eventManager);
$serviceManager = $e->getApplication()->getServiceManager();
}
public function initBreadcrumbAndActionButtonWidget(\Zend\Mvc\MvcEvent $e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$config = $serviceManager->get('config');
$viewHelperManager = $this->getViewHelperManager($e->getApplication()->getServiceManager());
$partialHelper = $viewHelperManager->get('partial');
$partial = $partialHelper('widgets/breadcrumb', array(
'config' => $config,
'actionButtons' => $config['action-buttons']
));
$placeHolder = $viewHelperManager->get('placeholder');
$placeHolder('widgets/breadcrumb')->set($partial);
}
public function getViewHelperManager($serviceManager)
{
$stack = new Resolver\TemplatePathStack(array('script_paths' => array(__DIR__ . '/view/')));
$resolver = new Resolver\AggregateResolver();
$resolver->attach($stack);
$renderer = new PhpRenderer();
$renderer->setResolver($resolver);
$viewHelperManager = $serviceManager->get('ViewHelperManager');
return $viewHelperManager;
}
public function getConfig()
{
return array_merge_recursive(
include __DIR__ . '/config/module.config.php',
);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
It is all working good till here, however now i want to pass a value from controller to the partial widgets/breadcrumb, after several attempt i thought implementing a ViewHelper could be a solution, and i implemented following view helper.
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class Variable extends AbstractHelper
{
protected $data;
public function __invoke()
{
return $this;
}
public function set($identifier, $data)
{
$this->data[$identifier] = $data;
return $this;
}
public function get($identifier)
{
return $this->data[$identifier];
}
}
In controller i assign the value to ViewHelper like this.
$viewHelperManager = $this->getServiceLocator()->get('ViewHelperManager');
$variable = $viewHelperManager->get('variable');
$variable->set('stack', 'overflow');
And in partial, to access the value i use the get method.
$this->variable()->get('stack');
Now my problem is that get() is rendered first hence the value set in controller has no effect.
My question is how can i make the set() gets rendered first so i can have the value in partial, or if there is any better approach of passing the value to partial please suggest.
Thanks.
Baaah, i called the view helper in wrong action, it worked when i moved the $variable->set('stack', 'overflow'); in the action where i wanted the value to pass.
Not sure if there can be a better solution, but this does the job for me to pass the value after i found no way in ZF2 that satisfies my requirement.

ZF2 (2.1 method) Populating select/drop down element in form

I used Zend Framework 2.1(not 2.0x) method to populate a select/drop down that is described in following links:
http://zf2.readthedocs.org/en/develop/modules/zend.form.advanced-use-of-forms.html#handling-dependencies
http://www.michaelgallego.fr/blog/2012/11/09/discover-whats-coming-for-zendform-in-zf-2-1/
Though it seems I have done as they told I got a error message like:
*... ::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, none given, called in ...*
which seems service locator is not used properly.
My form code that adds my FieldSet SupplierFieldset:
namespace Inventory\Form;
use Zend\Form\Form;
use Inventory\Model;
class ItemForm extends Form
{
public function init()
{
$this->add(array(
'name' => 'sup_code',
'type' => 'Inventory\Form\SupplierFieldset'
));
}
}
My 'SupplierFieldset' class:
namespace Inventory\Form;
use Inventory\Model;
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\InputFilter\InputFilterProviderInterface;
use Inventory\Model\SupplierTable;
use Inventory\Model\Supplier;
class SupplierFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
protected $supplierTable;
public function init()
{
parent::__construct('Suppliers Code');
$this->setLabel('Supplier Code');
$this->setName('supplier_code');
$suppliers = $this->getSupplierTable()->fetchAll();
$select = new Element\Select('supplier_code');
$options = array();
foreach ($suppliers as $supplier) {
$options[$supplier->id] = $supplier->sup_code;
}
$select->setValueOptions($options);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getSupplierTable()
{
if (!$this->supplierTable) {
$sm = $this->getServiceLocator();
$this->supplierTable = $sm->get('Inventory\Model\SupplierTable');
}
return $this->supplierTable;
}
}
My Module.php getFormElementConfig() function:
public function getFormElementConfig()
{
return array(
'factories' => array(
'SupplierFieldset' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
$supplierTable = $serviceLocator->get('Inventory\Model\SupplierTable');
$fieldset = new SupplierFieldset($supplierTable);
return $fieldset;
}
)
);
}
My SupplierTable.php model:
namespace Inventory\Model;
use Zend\Db\TableGateway\TableGateway;
class SupplierTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
}
I know SupplierTable model's constructor needs a TableGateway $tableGateway parameter. But this model is working properly when called from SupplierController.

Why Zend plugin postDispatch method is not called after indexAction

When i go to controllers index URL preDispatch is called before entering indexAction method of that controller but postDispatch is not called after it. Why it is not called and should i force it somehow to call postDispatch?
I have two plugin with postDispach methods whitch are not called.
If i got to URL e.g admin/listperms postDispatch is called and everything works.
AdminController.php
class AdminController extends Application_Controllers_Base {
public function indexAction() {
$this->view->headTitle = $this->alias('TITLE_ADMIN_PANEL');
}
public function listpermsAction() {
$this->view->headTitle = $this->alias('TITLE_PERMISSION_LIST');
$permService = new Application_Services_AdminPermission();
$perms = $permService->getPermissionList();
if(!$perms) {
// TODO:
} else {
$this->view->permList = $perms;
}
}
}
Base.php
class Application_Controllers_Base extends Zend_Controller_Action {
}
DbLogPlugin.php
class Application_Plugin_DbLogPlugin extends Zend_Controller_Plugin_Abstract {
public function postDispatch(Zend_Controller_Request_Abstract $request) {
$view = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
$view->developerLog = $this->getQuerysLogs();
}
}
bootstrap.php
protected function _initAutoload(){
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application_', //'
'basePath' => APPLICATION_PATH
));
$autoloader->addResourceType('dao', 'dao', 'Dao');
$autoloader->addResourceType('services', 'services', 'Services');
$autoloader->addResourceType('plugins', 'plugins', 'Plugins');
$autoloader->addResourceType('models', 'models', 'Models');
$autoloader->addResourceType('controllers', 'controllers', 'Controllers');
require_once('controllers/FrontController.php');
$front = Application_Controllers_FrontController::getInstance();
$front->setControllerDirectory(APPLICATION_PATH.'/controllers')
->setRouter(new Zend_Controller_Router_Rewrite())
->registerPlugin(new Application_Plugin_DbLogPlugin())
->registerPlugin(new Application_Plugin_AuthPlugin())
->registerPlugin(new Application_Plugin_ViewPlugin());
return $autoloader;
}

EmbedMany in Form

I've created a small Symfony2-Website (with Symfony PR11) with MongoDB as DB.
I could create a form, that use a normal document, but how do I use a document with an embeddedDocument?
here are the documents:
/**
* #mongodb:Document(collection="location")
*/
class Location
{
/**
* #mongodb:Id
*/
protected $id;
/**
* #mongodb:String
*/
protected $locationName;
/**
* #mongodb:EmbedMany(targetDocument="LocationTerminal")
*/
protected $terminals = array();
// Setter
public function setTerminals(LocationTerminal $terminal)
{
array_push($this->terminals, $terminal);
}
public function setLocationName($locationName)
{
$this->locationName = $locationName;
}
// Getter
public function getId()
{
return $this->$id;
}
public function getLocationName()
{
return $this->locationName;
}
public function getTerminals()
{
return $this->terminals;
}
}
The EmbeddedDocument:
/**
* #mongodb:EmbeddedDocument
*/
class LocationTerminal
{
/**
* #mongodb:String
*/
protected $terminalName;
/**
* #mongodb:Int
*/
protected $since;
/**
* #mongodb:Int
*/
protected $to;
// Setter
public function setTerminalName($terminalName)
{
$this->terminalName = $terminalName;
}
public function setSince($since)
{
$this->since = $since;
}
public function setTo($to)
{
$this->to = $to;
}
// Getter
public function getTerminalName()
{
return $this->terminalName;
}
public function getSince()
{
return $this->since;
}
public function getTo()
{
return $this->to;
}
}
As you can see $terminals holds an EmbedMany-Document
Here's the form:
class LocationForm extends Form
{
public function configure()
{
$this->add(new TextField('locationName', array('max_length' => 255, 'required' => true)));
}
public function addTerminals($dm)
{
$this->add(new ChoiceField('terminals.terminalName', array('choices' => $dm)));
$this->add(new DateField('terminals.since', array('required' => true)));
$this->add(new DateField('terminals.to', array('required' => false)));
}
}
The used Controller looks like this:
class LocationController extends Controller
{
protected $location;
protected $locationTerminal;
protected function getDm()
{
return $this->get('doctrine.odm.mongodb.document_manager');
}
protected function getLocation($name = null)
{
if ($name != null)
{
$dm = $this->getDm();
$this->location = $dm->getRepository('RalfBundle:Location')->findOneBy(array('locationName' => $name));
if (! $this->location)
{
$this->location = new Location();
$this->locationTerminal = new LocationTerminal();
$this->location->setLocation($name);
$this->location->setTerminals($this->locationTerminal);
}
}
else
{
$this->location = new Location();
$this->locationTerminal = new LocationTerminal();
$this->location->setTerminals($this->locationTerminal);
$this->locationTerminal->setSince(0);
$this->locationTerminal->setTerminalName("");
$this->locationTerminal->setTo(0);
}
}
protected function getForm()
{
$form = LocationForm::create($this->get('form.context'), 'location');
$dm = $this->getDm();
$form->addTerminals($dm->getRepository('RalfBundle:Terminal')->findAll()->toArray());
return $form;
}
//... some Actions
public function createAction()
{
$this->getLocation();
$form = $this->getForm();
$form->bind($this->get('request'), $this->location);
if ($form->isValid())
{
$dm = $this->getDm();
$dm->persist($this->location);
$dm->flush();
return $this->redirect($this->generateUrl('Location'));
}
return $this->render('RalfBundle:Ralf:location_create.html.twig', array('form' => $form));
}
I could see, that locationName recieve the entered values in the form, but the EmbedMany-Array terminals is still empty.
What did I wrong?
Thanks for helping :D
UPDATED:
Ok, found a solution.
in public function addTerminals($dm) in LocationForm it should look like this:
public function addTerminals($dm)
{
$this->add(new ChoiceField('terminals.0.terminalName', array('choices' => $dm)));
$this->add(new DateField('terminals.0.since', array('required' => true, 'type'=> 'timestamp')));
$this->add(new DateField('terminals.0.to', array('required' => false, 'type' => 'timestamp')));
}
'type' => 'timestamp' is necessary, 'cause DateField will create an DateTime-Object, but the document expected an Int for timestamp.
a field from the terminals-array could be accessed by normal dot-notation.
Ok, found a solution.
in public function addTerminals($dm) in LocationForm it should look like this:
public function addTerminals($dm)
{
$this->add(new ChoiceField('terminals.0.terminalName', array('choices' => $dm)));
$this->add(new DateField('terminals.0.since', array('required' => true, 'type'=> 'timestamp')));
$this->add(new DateField('terminals.0.to', array('required' => false, 'type' => 'timestamp')));
}
'type' => 'timestamp' is necessary, 'cause DateField will create an DateTime-Object, but the document expected an Int for timestamp.
a field from the terminals-array could be accessed by normal dot-notation.
Symfony 2 actually gives you a tool to handle embedded documents in forms: It's called "collection field type" and it enables you to embed other formtypes (from other embedded documents) in a parent form.
It can be configured to allow/forbid addition/deletion of embedded documents and is actually quite powerful.