I'm still experimenting with URL routes and just managed to get it to work.
my routes.ini has this:
[production]
routes.register.route = :lang/register
routes.register.defaults.controller = register
routes.register.defaults.action = index
routes.register.defaults.lang = en
routes.register.reqs.lang = "[a-z]{2}"
My URL would look like this:
http://www.mysite.com/en/register
So now, in my controller I can do this:
$lang = $request->getParam('lang');
My problem is: I'm trying to get a list of countries in a select element, which depending if the lang element is english or french, will return the countries in said language.
To do so, I would need to pass "lang" to the form.
Then in the form, pass it to the model querying the countries there.
And if there's a change, it makes for a lot of places to change as well.
So back to the question:
Can I just set this variable as global?
I would pass the variable to the form from your controller.
// Controller action
public function formAction()
{
$lang = $this->getParam('lang');
$form = new My_Form_Xyz($lang);
$this->view->form = $form;
}
// My_Form_Xyz
protected $lang;
public function __construct($lang)
{
$this->lang = $lang;
parent::__construct();
}
public function init()
{
switch ($this->lang) {
case 'en':
$selectOptions = array();
break;
case 'klingon':
$selectOptions = array();
break;
}
}
Injecting via form setter (as suggested by ArneRie) is better solution. But in case you don't have a discrete form class, or you generate the form from ini file, you can access the parameter like this anywhere after $front->dispatch() in your bootstrap
Zend_Controller_Front::getInstance()->getRequest()->getParam('lang');
Related
In ZF2, I have a factory for a multicheckbox (simplified)
class MultiCheckboxFactory
{
public function __invoke(FormElementManager $formElementManager)
{
$multiCheck = new MultiCheckbox();
$serviceManager = $formElementManager->getServiceLocator();
$mapper = $serviceManager->get('Path\To\The\Mapper\SomeMapper');
$resultFromQuery = $mapper->findText('text');
// further setting up of the multicheckbox based on $resultFromQuery
return $multiCheck;
}
}
I want the multicheckbox to render different content depending on $resultFromQuery that comes from the mapper's findText() method.
I thought of passing a variable to the __invoke(FormElementManager $formElementManager, $someText). But the problem is that when I call the multicheckbox from the service manager like this:
$element = $formElementManager->get('Path\To\Factory\Alias\Multicheckbox');
I don't see how to pass an additional variable. Any help?
Have a look at MutableCreationOptionsInterface, this allows your factory to receive runtime options which you pass through the serviceManager get() method.
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\MutableCreationOptionsTrait;
class MultiCheckboxFactory implements MutableCreationOptionsInterface
{
use MutableCreationOptionsTrait;
public function __invoke(FormElementManager $formElementManager)
{
$options = $this->getCreationOptions();
var_dump($options);
$multiCheck = new MultiCheckbox();
....
}
}
Now you can pass options:
$element = $formElementManager->get('Path\To\Factory\Alias\Multicheckbox', ['foo' => 'bar']);
Update: MutableCreationOptionsTrait is no longer available in ZF3: https://docs.zendframework.com/zend-servicemanager/migration/#miscellaneous-interfaces-traits-and-classes
The simplest way to do this now appears to be
$element = $formElementManager->build('Path\To\Factory\Alias\Multicheckbox', ['foo' => 'bar']);
though this will give you a discrete (not shared) instance every time.
I have a form say:
class Application_Form_UserDetails extends Zend_Form
{
public function init()
{
$pswd = new Zend_Form_Element_Password('password');
$pswd->setLabel('New password:');
$pswd->setAttrib('size', 25);
$pswd->setRequired(false);
$pswd->addValidator('StringLength', false, array(4,15));
$pswd->addErrorMessage('Wron password');
}
}
In my user details controller class I have:
class UserDetailsController extends Zend_Controller_Action {
public function editAction()
{
$userId = $this->userInfo->id;
$DbTableUsers = new Application_Model_DbTable_User;
$obj = $DbTableUsers->getUserDetails($userId);
$this->view->formUser = new $this->_UserDetails_form_class;
$this->view->formCompany = new $this->_CompanyDetails_form_class;
if ($obj) {
$this->view->formUser->populate($obj);
}
$url = $this->view->url(array('action' => 'update-user-details'));
$this->view->formUser->setAction($url);
}
public function updateUserDetailsAction()
{
$formUser = new $this->_UserDetails_form_class;
if ($formUser->isValid($this->getRequest()->getPost())) {
}
else {
//validation failed
$formUser->markAsError();
$this->view->formUser = $formUser;
$this->_helper->redirector('edit', 'user-details');
}
}
}
The first time Edit action is called the form built and displayed.
User fills the form and sends it (updateUserDetailsAction is called).
In updateUserDetailsAction, on validation failure I mark the form as having errors and want to display the form with error messages that I previously set in updateUserDetailsAction class.
Then I redirect:
$this->_helper->redirector('edit', 'user-details');
in order to display the same form but with errors for the user to re-enter correct values.
The problem is I don't know how to let know the edit action that the form must display validation errors?
On $this->_helper->redirector('edit', 'user-details'); the form is redisplayed
as a new form with cleared erros but I need them displayed.
Do I do this the correct way?
regards
Tom
Problem comes from the fact that you are redirecting and in each method you are creating a new instance of the form, that means the form class is loosing its state - data you injected from the request and any other values passed to this object.
Combine editAction and updateUserDetailsAction into one method:
...
$formUser = new Form();
// populate the form from the model
if ($this->getRequest()->isPost()) {
if ($formUser->isValid($this->getRequest()->getPost())) {
// update the model
}
}
...
and have the form being submitted to the edit action. This will simplify your code and remove code duplication.
If you just wan to fix your code you can instantiate the form object in the init() method of your controller as set it as a property of your controller. This will way you will reuse same instance after redirection. I still think that solution above is much more compact and easier to understand for someone else.
I'm trying to make a way to disable some view helpers that are inside "application/views/helpers"...
What I really want is to put some options on the application.ini to enable or disable some Helpers.
Example on application.ini:
helpers.Helper1=on
helpers.Helper2=off
Now the problem is that when a Helper is off, I want to rewrite some functions of this helper in order to return a different result on the view. In this way, I don't need to change anything in the view script.
I thought in having 2 different php files for each helper, in different locations. One with the real helper and another with the changed helper (to work when it is off on the application.ini).
The problem is that I don't know how to tell the view which one it shoul load...
Does anyone know how it could be done?
FINAL CODE
Ok, after many tries, I put it to work with the following code:
Bootstrap
protected function _initConfigureHelpers(){
$this->bootstrap('view');
$view = $this->getResource('view');
$view->addHelperPath("./../library/ConfigHelpers","Configurable_Helper");
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Application_Plugin_ViewPlugins());
return $view;
}
Application_Plugin_ViewPlugins
class Application_Plugin_ViewPlugins extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request){
$front=Zend_Controller_Front::getInstance();
$bootstrap=$front->getParam('bootstrap');
$options=$bootstrap->getOption("helpers");
if (is_array($options)){
$view = $bootstrap->getResource('view');
foreach($options as $option => $value){
$helper=$view->getHelper($option);
if ($helper){
if ($value=="off")
$helper->__disable();
else if ($value!="on")
throw new Exception('The value of helpers.'.$option.' must be "on" or "off" on application.ini.');
} else {
throw new Exception("Inexistent Helper");
}
}
}
}
}
Modified helper example
require_once APPLICATION_HELPERS."CssCrush.php";
class Configurable_Helper_CssCrush extends Zend_View_Helper_CssCrush {
protected $__config_enabled = true;
public function __disable(){
$this->__config_enabled = false;
return $this;
}
public function __enable(){
$this->__config_enabled = true;
return $this;
}
public function cssCrush(){
if ($this->__config_enabled){
return parent::cssCrush();
} else{
return new Modified_CssCrush();
}
}
}
class Modified_CssCrush {
public static function file ( $file, $options = null ) {
return $file;
}
}
APPLICATION_HELPERS is defined on /public/index.php as "../application/views/helpers/".
Now, when I want to add a configurable helper, I put the original helper on "/application/views/helpers/" and then, create a modified version of it on "/library/ConfigHelpers" with the structure of the example above.
What I think you want is Dependency Injection which is coming in zf2, but not available in zf1.
With some tinkering though you can get what you need.
Configuring helpers in the bootstrap
(assumes default project structure)
View helpers paths config : application/configs/application.ini:
resources.view.helperPath.Zf_View_Helper_ = "Zf/View/Helper"
A simple configurable helper, (allows disable/enable but you can obviously add any methods you need (use this as base class for helpers that need the behaviour)
class Zf_View_Helper_Configurable extends Zend_View_Helper_Abstract
{
protected $isEnabled = true;
public function configurable()
{
return $this;
}
public function disable()
{
$this->isEnabled = false;
return $this;
}
public function enable()
{
$this->isEnabled = true;
return $this;
}
public function __toString()
{
if ($this->isEnabled) {
return 'Configurable is enabled';
} else {
return 'Configurable is disabled';
}
}
}
And configure the helpers in the bootstrap:
public function _initConfigureHelpers()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$configurableHelper = $view->configurable();
$configurableHelper->disable();
}
You can add options in the .ini file and grab them in the bootstrap initConfigureHelpers() method.
If you want this behaviour from any default zf helper, do what #Ratzo said and extend those helpers and add the required behaviour and then configure them in your bootstrap.
Please take a look at the following link Zend_View link
Below is an important points you should consider from the Zend docs.
Note: Default Helper Path
The default helper path always points to the Zend Framework view
helpers, i.e., 'Zend/View/Helper/'. Even if you call setHelperPath()
to overwrite the existing paths, this path will be set to ensure the
default helpers work.
This means that you can't really turn off the helpers, unless you want to go about extending the Zend_View object and overwrite the setHelperPath method. This is not the way to go though.
Here is probably what you want to do. First though, here is my assumption.
Assumption : You want to write your own view helper that slightly alters what the current view helpers do by changing a few methods here or there.
Here is what you should do to accomplish that.
First, write your view helper. Make sure the last part of the class name is the same as the view helper you want to 'overwrite'. You don't have to, but this makes sure the original helper can't be used anymore.
class My_View_Helper_BaseUrl extends Zend_View_Helper_BaseUrl
{
private $_enabled = true;
public function setEnabled( $bool ){ $this->_enabled = (boolean) $bool; }
public function baseUrl(){
if( $this->_enabled ){
return 'testUrl'; //other code
}
else return parent::baseUrl();
}
Now that you have that, do the following
$view->setHelperPath('/path/to/my/helpers', 'My_View_Helper'); //1
echo $view->baseUrl(); //2
Excellent. Now you've effectively shadowed the original BaseUrl helper.
The above code will make it so that the view scans your directory
for any helpers before scanning the default zend directory. When it gets to line
2 the view will find YOUR baseUrl helper first and use THAT instead of the
original baseUrl helper. In the above example it should echo
'testurl' instead of the normal baseUrl behavior.
You can make a custom helper that extends the original helper, for example
class My_Helper_Url extends Zend_View_Helper_Url
{}
and rewrite the methods as you need.
What I want to do with Zend Framework is to render the action Y from the action X and to obtain the html:
Example:
public xAction(){
$html = some_function_that_render_action('y');
}
public yAction(){
$this->view->somedata = 'sometext';
}
where the y view is something like:
<h1>Y View</h1>
<p>Somedata = <?php echo $this->somedata ?></p>
I fount the action helper, but I cannot use it from a controller. How can I solve it?
It is possible?
Here is one possible way to do what you want.
public function xAction()
{
$this->_helper
->viewRenderer
->setRender('y'); // render y.phtml viewscript instead of x.phtml
$this->yAction();
// now yAction has been called and zend view will render y.phtml instead of x.phtml
}
public function yAction()
{
// action code here that assigns to the view.
}
Instead of using the ViewRenderer to set the view script to use, you could also call yAction as I showed above, but get the html by calling $html = $this->view->render('controller/y.phtml');
See also the ActionStack helper.
You can use the Action View Helper from the controller
public function xAction()
{
$html = $this->view->action(
'y',
$this->getRequest()->getControllerName(),
null,
$this->getRequest()->getParams()
);
}
public function yAction()
{
// action code here that assigns to the view.
}
It's not very beautiful but it works well and you don't have to use $view->setScriptPath($this->view->getScriptPaths());
This helper creates a new Zend_Controller_Request for yAction(), so you can give your own parameters as 4th argument or use $this->getRequest()->getParams() to spread the request parameters of xAction().
http://framework.zend.com/manual/1.12/en/zend.view.helpers.html#zend.view.helpers.initial.action
Finally I found this "solution", it's not what I want to do, but it works, if someone found the real solution, please answer here.
public function xAction(){
$data = $this->_prepareData();
$view = new Zend_View();
$view->somedata = $data;
$view->setScriptPath($this->view->getScriptPaths());
$html = $view->render('controller/y.phtml');
}
So I've created myself a custom form element which has a custom view helper. Now I want to be able to set certain parameters/variables on this form element and be able to access them in my element's view helper. How can I do that?
Here's an example of what I am talking about:
adding the element to the form:
$element = new My_Form_Element_Picker('elementname');
$element->setFoobar('hello');
// or
$form->addElement('Picker', 'elementname', array('foobar' => 'hello'));
form element:
class My_Form_Element_Picker extends Zend_Form_Element_Xhtml
{
public $helper = 'pickerElement';
}
view helper:
class My_View_Helper_PickerElement extends Zend_View_Helper_FormElement
{
public function pickerElement($name, $value = null, $attribs = null)
{
//now I want to check if the 'foobar' option was set, otherwise use a default value
$foobar = 'default';
}
}
There is a fourth optional argument to the view helper that might do the trick for you.
if you define your view helper like this:
public function pickerElement( $name, $value=null, $attribs=null, $options=null ) { }
And then inside your actual form element you define it like this:
class My_Form_Element_Picker extends Zend_Form_Element_Xhtml {
public $helper = 'pickerElement';
public $options = array();
public function setFoobar( $foobar ) {
$this->options['foobar'] = $foobar;
}
}
You will find that the options are passed into the view helper and can be used.
This code is from memory so please forgive any mistakes, this method definitely works for me though.