Zend form element use viewHelper as decorator - php

What I'm trying to achieve is using viewHelper to customize my element. I know that it's mostly done by decorators, but I would like to know is it possible to do it with viewHelper only?
Where I want to use viewHelper:
$defaults = $_GET;
$defaults['randomText'] = 'Something';
$defaults['something'] = 'placeholder';
$form = new Extension_Form();
$element = new Extension_Form_Element_Xhtml('randomText', $this->view->SpanAdder($defaults['randomText'])); // I'm creating Xhtml element, where I'm replacing content with viewHelper return. Want to get rid of $this->view->SpanAdder part :)
$element->setLabel('Label');
$form->addElement($element);
unset($defaults['randomText']); // I want to get rid of this line, but unfortunately I have to have it, otherwise SpanAdder result will be overwritten.
$form->setDefaults($defaults);
And I have viewHelper, which gives span around my value.
class View_Helper_SpanAdder extends Zend_View_Helper_Abstract {
public function SpanAdder($value) {
return '<span name="' . $value . '">' . $value . '</span>';
}
}

Related

passed parameter not working on xpath, although working in unit test

class ad_xml_model extends \ITico_core\common_class {
public function get_info_from_ad($input_id) {
$xml_file = parent::get_set('xml');
$ad_info = $xml_file->xpath("//ad[#id='" . $input_id . "']");
parent::get_set('ad_info', $ad_info);
return $ad_info;
}
}
The above code works when unit testing it, i've tried passing both a string and a int into the function and it works both times.
<h2>get_info_from_ad</h2>
test data: 1<br>
info from ad:- <br>
<?php
$ad_xml_model->get_info_from_ad(1);
$ad_info = $ad_xml_model->get_set('ad_info');
print_r($ad_info);
but in the code below when being called from the controller its not working
class main_controller(){
$ad_top_limit = count($ads_from_category);
$key = rand(0, $ad_top_limit - 1);
$chosen_ad = $ads_from_category[$key];
parent::get_set('chosen_ad', $chosen_ad);
$ad_info = $ad_xml_model->get_info_from_ad($chosen_ad);
parent::get_set('ad_info', $ad_info);
if ($ad_info != null) {
switch ($ad_type) {
case NUll:
break;
case 'long':
$long_view = new long_view($ad_info);
$long_view->show_ad();
}
}
}
and the debug page testing
<?php
echo $main_controller->get_set('chosen_ad') . "<br>";
?>
ad information:-: <br>
<?php
print_r($main_controller->get_set('ad_info'));
screenshot of the debug page
I've went every step of the way echoing all the variables to check that they aren't null but for some reason the xpath just doesnt work when being called from the main controller but it works fine from the unit test even though the exact same parameters are being passed.
I found solution was to implicitly tell the xpath it was an int
public function get_info_from_ad($input_id) {
$xml_file = parent::get_set('xml');
$ad_info = $xml_file->xpath("//ad[#id='" . (int)$input_id . "']");
parent::get_set('ad_info', $ad_info);
return $ad_info;
}
for some reason if I set it to a string it still doesn't work, perhaps someone else could share light on this.

Creating different classes for each dynamicly created menu

I am using Zend2 and i am creating menu items dynamicly.
This is the function i am using:
public static function getAdminMenu() {
$config = \App\Application::getInstance()->getConfig();
$menuItems = $config['menu_items'];
$html = '<ul>';
foreach ($menuItems as $section => $menuItem) {
$html .= '<div class="user-menu-section">' . $section . '</div>';
foreach ($menuItem as $subSection => $params) {
$html .= '<li>' . $subSection . '</li>';
}
}
$html .= '</ul>';
return $html;
}
How can i create divs with different class user-menu-section for each menu item. It should be something like 'user-menu-section1', 'user-menu-section2'...
Or maybe better to use something like this:
<div class="' . $section . '">;
but in this case, if $section is a string of two words i would need '-' in between words and both words small caps, if it is possible.
Well, just use your $section and modify this. Using ZF2, you'd use the Filter CamelCaseToDash
$filter = new \Zend\Filter\Word\CamelCaseToDash();
$classFiltered = strtolower($filter->filter($class);)
Now you can use $classFiltered for your CSS-Class assignment.
And since you've mentioned both frameworks in your tags. In case you are using ZF2, that code is horrible :D You should create yourself a ViewHelper that renders the Menu. Evan Coury has written a very easy introduction on how to do that.
Aside from that, you don't need a static call to some Application::getInstance(). If you want to gain access to the config you do this via the ServiceLocator. In a Controller this would look like this:
$config = $this->getServiceLocator()->get('config');
If you need the config in another class outside of the Controller, you create the class from the ServiceLocator and inject the config into this class.

Is there a way to prefix Zend_Form element name?

I have a page that has multiple forms on it. Several of the forms share an element with the same name like CustomerID. This means the element ID CustomerID will collide with that same ID in the other forms. I would like to find a clean way to prefix the field name with the name of the form. For instance PaymentProfile_CustomerID. Suggestions?
So far, the best I have been able to come up with is:
class MyForm extends Zend_Form
{
public function init()
{
$this->setName("PaymentProfile");
...
$this->_prefixElementNames();
}
private function _prefixElementNames()
{
$elements = $this->getElements();
$formName = $this->getName();
foreach($elements as $e) {
$e->setAttrib('id', $formName . '_' . $e->getName());
}
}
}
UPDATE #garvey's answer below worked well with a simple modification.
public function addElement($element, $name = null, $options = null)
{
$e = parent::addElement($element, $name, $options);
if($this->getName())
// I use setAttrib instead of setName because I only want the ID to be changed.
// Didn't want the form data to be prefixed, just the unique HTML identifier.
$element->setAttrib('id', $this->getName() . '_' . $element->getName());
return $e;
}
I think it's easier to just use elementsBelongTo:
public function init()
{
$this->setOptions(array(
'elementsBelongTo' => 'form_name'
));
}
edit: expanded for future use
Using elementsBelongTo wraps all form elements in array, so you'll get
Zend_Debug::dump($this->_getAllParams())
outputs:
["form_name"] => array(
["element1"] => "value1"
["element2"] => "value2"
)
I have investigated your issue. And I think the best way is to extend Zend_Form class like this:
class Cubique_Form extends Zend_Form
{
public function addElement($el)
{
$el->setName($this->getName() . '_' . $el->getName());
parent::addElement($el);
}
}
And form creation:
$form = new Cubique_Form();
$form->setName('form');
$el = new Zend_Form_Element_Text('element');
$form->addElement($el);

Is there a way to add a group of elements to a form?

I'm wondering if there was a way to add a group of elements to a zend form as if they were one element, I guess much like a subform, but it seems the functionality of a subform may be too much...
Here's my use-case. I've created a class that handles multi-page forms. I want to be able to write logic to change the buttons at the bottom of the form based on the page of the form I'm on.
I originally thought that Zend-Form-DisplayGroup would fix my problem, but you have to add the items to the form first and then add them to the display group and can't pass a display group through a function with attached elements. I would like to have a function that would be something like
public function setSubmitButtonGroup($submitButtonGroupElements)
{
/*Code to set button group*/
}
The idea of using an array of elements just hit me right now as opposed to something else and add logic to add that array of elements to the form on render... but does anyone have any "better" ideas or done this before?
BTW, if anyone is wondering... I'm loosely basing my initial design off of this section: Zend Framework Advance Form Usage.
Not sure I understand your problem correctly but this how I do some things.
In a Zend_Form object you can add elements as a group with `addElements($elements) in an array. For the Submit button etc. I have a class where I get the $elements array from and then I simply pop it in. I also add a displayGroup but separately and simply to control where the buttons are. Because a form is an object you can do simple things like the following but I always add a reference to show my intent.
update: shuffled the button manipulation
function addButtons(&$form,$nextName = null) {
$buttons = $this->getButtons(); // this will be an array with your buttons
// make sure you have the element names in your buttons arrays
if ( is_string($nextName) ) {
$buttons['nextButton']->setLabel($nextName);
} elseif ( is_bool($nextName) && false === $nextName ) {
unset($buttons['nextButton'];
}
// repeat for other buttons
$form->addElements($buttons);
$elementNames = array_keys($buttons);
$form->addDisplayGroup($elementNames,'buttonGroup',array('legend'=>'Click some buttons'));
}
$this->addButtons($form,'Finish');
You could make yourself a factory that receive three params, your form element, the current controller and the current action. Then in that factory, you could call a builder based on the controller/action combination and you pass your form.
In your builder you add 1, 2 or 3 buttons based on the corresponding controller/action requirement which are stored in diffrent components. Once it is done, you return your form to the factory and the factory return the form.
My_Form // your Zend_Form object
My_Form_Factory // Your new factory (see below)
My_Form_Factory_Builder_Controller_Action // One of your builder (see below)
My_Form_Factory_Component // Extends the corresponding Zend_Form_Elements
// basic factory that can be called like My_Factory::factory($form, $controller, $action)
class My_Form_Factory {
static public function factory($form, $controller, $action)
$builderClass = "My_Form_Factory_Builder_" . $controller . '_' . $action;
$builder = new $builderClass($form);
return $builder->buildForm();
}
// Basic builder
class My_Form_Factory_Builder_Controller_Action
{
protected $_form;
protected $_previousComponent ;
protected $_nextComponent ;
protected $_cancelComponent ;
public function __construct($form)
{
$this->_form = $form;
$this->_previousComponent = new My_Form_Factory_Component_Previous();
$this->_nextComponent = new My_Form_Factory_Component_Next();
$this->_cancelComponent = new My_Form_Factory_Component_Cancel();
}
public function buildForm()
{
$this->_form->addElement($previousCompnent);
$this->_form->addElement($nextComponent);
$this->_form->addElement($cancelComponent);
return $this->_form;
}
}
If you want to automatize the instanciation you could initialize all the different compoments you might require in an abstract class and in the method buildForm() only add the elements you need for that current interface. (I would rather repeat the code in each builder than rely on this kind of "magic" but it a viable method to do it).
So the complexity of my problem comes with knowing what page of the multipage form. Using an array and the above mentioned addElements() helped.
Simple Answer
The answer to my problem was an array that could be manipulated after the form was "built" so to speak but before it was rendered so that I could add to the form using addElements().
Long Answer
To get the whole picture, imagine each time you hit the next or previous button, you are traversing through an array of subforms. In this case one would need a function to handle the button rendering. I ended up using a case statment, though it's not the best implementation in the world (not reusable in the parent class Form_MultiPage), but it worked:
in my extention of my mulipage form class I have
public function setSubmitControls()
{
$previous = new Zend_Form_Element_Submit('previous',array(
'label'=>'previous',
'required'=>false,
'ignore'=>false,
'order'=>9000
));
$cancel = new Zend_Form_Element_Submit('cancel',array(
'label'=>'Cancel',
'required'=>false,
'ignore'=>false,
'order'=>9003
));
$next = new Zend_Form_Element_Submit('next',array(
'label'=>'Next',
'required'=>false,
'ignore'=>false,
'order'=>9002
));
$finished = new Zend_Form_Element_submit('finish',array(
'label'=>'Finish',
'required'=>false,
'ignore'=>false,
'order'=>9004
));
$submitControls = array();
echo var_dump($this->getCurrentSubForm()->getName());
switch($this->getCurrentSubForm()->getName())
{
case 'billInfo':
$submitControls = array(
$next,
$cancel
);
break;
case 'payerInfo':
$submitControls = array(
$previous,
$next,
$cancel
);
break;
//So on for other subforms
}
$this->setSubmitButtonGroup($submitControls);
}
In my parent class, Form_Multipage, I have
public function setSubmitButtonGroup(array $elements)
{
$this->_submitButton = $elements;
}
And
public function addSubmitButtonGroupToSubForm(Zend_Form_SubForm $subForm)
{
$subForm->addElements($this->_submitButton);
return $subForm;
}
Which is called when I render the "page" of the form with this function
public function prepareSubForm($spec)
{
if (is_string($spec)) {
$subForm = $this->{$spec};
} elseif ($spec instanceof Zend_Form_SubForm) {
$subForm = $spec;
} else {
throw new Exception('Invalid argument passed to ' .
__FUNCTION__ . '()');
}
$subform = $this->setSubFormDecorators($subForm);
$subform = $this->addSubmitButtonGroupToSubForm($subForm);
$subform = $this->addSubFormActions($subForm);
$subform->setMethod($this->getMethod());
return $subForm;
}

Which is the event listener after doSave() in Symfony?

I've been looking at this event-listeners page http://www.doctrine-project.org/documentation/manual/1_1/pl/event-listeners and I'm not sure which is the listener I have to use to make a change after the doSave() method in the BaseModelForm.class.php.
// PlaceForm.class.php
protected function doSave ( $con = null )
{
...
parent::doSave($con);
....
// Only for new forms, insert place into the tree
if($this->object->level == null){
$parent = Place::getPlace($this->getValue('parent'), Language::getLang());
...
$node = $this->object->getNode();
$method = ($node->isValidNode() ? 'move' : 'insert') . 'AsFirstChildOf';
$node->$method($parent); //calls $this->object->save internally
}
return;
}
What I want to do is to make a custom slug with the ancestors' name of that new place. So if I inserting "San Francisco", the slug would be "usa-california-san-francisco"
public function postXXXXXX($event)
{
...
$event->getInvoker()->slug = $slug;
}
The problem is that I'm inserting a new object with no reference to its parent. After it's saved, I insert it to the tree. So I can't change the slug until then.
I think a Transaction listener could work, but I'm use there is a better way I'm not seeing right now.
thanks!
You are looking at the wrong piece of code. As stated by benlumley, you should manage your slug directly in the model, not in the form. To achieve what you want (a recursive slug) is quite easy using doctrine's Sluggable behavior. You need to implement a getUniqueSlug() into your model so that it gets called by the behavior (it's automatic) and handle your slug specifities in there:
public function getUniqueSlug()
{
$slug = '';
$parent = $this->getParent();
if ($parent->exists())
{
$slug = $this->getParent()->getUniqueSlug().'-';
}
return $slug.$this->getName();
}
What we do here is basically traverse all the ancestors of the current object and append the slugs on the go (replace the getParent() by whatever method you use to retrieve an object's parent.
Firstly, I'd put this into the model rather than the form - that way if the object is ever edited/updated the behaviour would still happen.
In the form though, I'd use updateObject:
function updateObject($values = array()) {
parent::updateObject($values);
// do your stuff
}
In the model (looks like you are using doctrine ...) I'd put this into the postSave() method. As I say, I think its better there than the form.
I had the same problems, and the Doctrine_Record::postInsert(Doctrine_Event $event) method did not work for me. Indeed the node aren't hydrated yet.
I had to overwrite the sfFormObject::doSave method like this:
protected function doSave($con = null)
{
$is_new = $this->isNew();
parent::doSave($con);
$this->doSaveNestedSet($con);
$service = $this->getObject();
if( $is_new and ! $service->getClientId() and $parent = $service->getParent())
{
$service->setClient($parent->getClient());
$service->save();
}
}

Categories