Is it possible to remove the 'id' attribute that Zend Framework adds to every Form Element by default?
I've looked at the documentation but don't seem to be able to find an answer to this rather straight forward question.
Possible Solution
Is there any cleaner way to do it other than setOption?
$submit = new Zend_Form_Element_Submit('submit');
$submit->setRequired(FALSE)
->setIgnore(TRUE)
->setDecorators($this->elementDecorators)
->setOptions(array('id' => ''));
A solution would be to override Zend_View_Helper_Form with your own View Helper.
But sincerely, don't take too much attention to this id attribute in your form, you will sooner or later need this id (if you're using Javascript for example) and the performance gain (to render the page) is too tiny to be taken into consideration. It will even be a performance loss since you're going to override a Helper.
If your purpose is different and you want to do that anyway, you need to write you own View Helper as follow:
class My_View_Helper_Form extends Zend_View_Helper_FormElement
{
public function form($name, $attribs = null, $content = false)
{
$info = $this->_getInfo($name, $content, $attribs);
extract($info);
$xhtml = '<form'
. $this->_htmlAttribs($attribs)
. '>';
if (false !== $content) {
$xhtml .= $content
. '</form>';
}
return $xhtml;
}
}
Finally, you simply need to overload the default view helper using a plugin loader. Read the manual for more information about plugin loader.
Related
I'm having an issue displaying information returned from a custom class defined within a plugin's files, when using a shortcode. I'll write up some mock files that showcase my issue.
/wp-content/plugins/my-plugin/classes/my_class.php
<?php
class People {
public $api_url = "https://www.external-service.com/api";
private $api_key;
function __construct($key = null) {
if $(key) {
$this->api_key = $key;
}
function get_response() {
$path = $this->api_url . "?my_api_token=" . $this->api_key;
}
}
?>
/wp-content/plugins/my-plugin/my-plugin.php
<?php
/**
* all of the wordpress plugin comments
* ...
*/
require "myplg_options.php";
require "myplg_shortcodes.php";
The options page and menu is generated from myplg_options; it is functioning correctly (including using get_option to retrieve the saved option (in this case, the api key).
/wp-content/plugins/my-plugin/myplg_shortcodes.php
<?php
require "classes/my_class.php";
$options = get_option('myplg_settings');
$myplg = new People($options['myplg_api_key']);
$response = $myplg->get_response();
function myplg_list_result(){
echo "the shortcode is working!";
var_dump($options, $myplg, $respnose);
}
add_shortcode('myplg_list', 'myplg_list_result');
?>
Testing externally from wordpress, the class works and everything is fine and dandy. The plugin's option page sets and retains the single option perfectly; the shortcode actually registers and is usable from within a WordPress page/portfolio/etc.
The issue I'm having is that using var_dump, all three of those variables are dumped as NULL.
After doing some homework, I was able to determine that moving the three variable declarations inside the shortcode makes it work. It would seem to me, however, that doing that is not the best workflow, as I'd need to re-grab the option, instantiate a new class, and call the class' function for every shortcode.
Is there a way around this?
As mentioned in the comment it's because variables are function scoped. You may be better off using a Closure.
<?php
require "classes/my_class.php";
$options = get_option('myplg_settings');
$myplg = new People($options['myplg_api_key']);
$response = $myplg->get_response();
add_shortcode('myplg_list', function() use ($options, $response, $myplg) {
echo "the shortcode is working!";
var_dump($options, $myplg, $respnose);
});
Im sure this must be a RTM question, but I must be looking in the wrong places. In symfony 1.4 I used post validator callbacks quite a lot. For example checking that start and end dates were in the correct order. I am developing an app in Silex but cant figure out how to add similar functionality as a validator. This is what I am working with (basically):
$app['form.example'] = function ($app) {
$constraints = new Assert\Collection(array(
'date1' => new Assert\Date(),
'date2' => new Assert\Date(),
));
$builder = $app['form.factory']->createNamedBuilder('form', 'example', array(), array('validation_constraint' => $constraints));
return $builder
->add('date1', 'date')
->add('date2', 'date')
->getForm();
};
I can put my own validation test in the 'process form' part, like: if ($form->isValid() && --my datetest--) but it doesnt feel right to me there.
Any help? Thanks!
I guess you can use form events for this kind of thing; certainly for Symfony2, so I assume Silex too? There's a cookbook article about using it to generate dynamic forms:
http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html
Some useful detail in another SO question:
Description of Symfony2 form events?
Could have sworn I had a SO discussion about this with someone before but I cannot find the question. I used BIND_CLIENT_DATA to calculate some form fields in some code a while back; I'll see if I can dig it out.
EDIT
Okay, I found something but I'm modifying this from Symfony code so I'm not 100% on this but it might be a starting point (I hope):
$builder->addEventListener(FormEvents::BIND_NORM_DATA, function($event) {
// your form data
$data = $event->getData();
// get date objects - if you cannot dereference this way try getters
$d1 = $data['date1'];
$d2 = $data['date2'];
// naive comparison :)
$isCorrectDateOrder = $d1->getTimestamp() < $d2->getTimestamp();
// check the comparison
if (!$isCorrectDateOrder) {
// trouble... create and add a FormError object to the form
$event->getForm()->get('date1')->addError(new \Symfony\Component\Form\FormError('uh oh...'));
}
});
Hope this helps :)
Is there some method that accepts inserting custom html without having to actually add form controls, even if they're hidden and making my html a decorator?
I'm looking for something like:
$this->addCustomElement( array(
'div',
'body' => '<p>inner text</p>'
) );
I need something short and quick, I don't want to create a new class or something overkill.
Well it's really as simple as this:
$note = new Zend_Form_Element('note');
$note->helper = 'formNote';
$note->setValue('<b>hi</b>');
$form->addElement($note);
But the problem is that when you submit the form, the form calls $note->isValid(), which overrides the value, so if there are errors with the form, the next time you display it, the custom HTML won't be shown. There are two easy ways to fix this, the first is to override isValid() in your Form class like this:
public function isValid($data)
{
$note = $this->note->getValue();
$valid = parent::isValid($data);
$this->note->setValue($note);
return $valid;
}
But personally I find this kinda hackish way, and prefer the second option. That is to write a very simple class (this should really be part of Zend itself, I have no idea why it isn't, since it includes a formNote view helper, but no element that uses it):
class My_Form_Element_Note extends Zend_Form_Element_Xhtml
{
public $helper = 'formNote';
public function isValid($value, $context = null) { return true; }
}
Then you just have to do:
$note = new My_Form_Element_Note('note');
$note->setValue('<b>hi</b>');
$form->addElement($note);
And everything will just work.
Other options include doing some black magic with decorators, but I really recommend you to not go down that path.
Also note the AnyMarkup Decorator.
I have two fields in my Zend Form, and i want to apply the validation rule that ensures the user enters either one of the these two fields.
$companyname = new Zend_Form_Element_Text('companyname');
$companyname->setLabel('Company Name');
$companyname->setDecorators($decors);
$this->addElement($companyname);
$companyother = new Zend_Form_Element_Text('companyother');
$companyother->setLabel('Company Other');
$companyother->setDecorators($decors);
$this->addElement($companyother);
How can i add a validator that will look at both fields?
See the 'Note: Validation Context' on at this page. Zend_Form passes the context along to every Zend_Form_Element::isValid call as the second parameter. So simply write your own validator that analyzes the context.
EDIT:
Alright, I thought I'ld take a shot at this myself. It's not tested, nor is it a means to all ends, but it will give you a basic idea.
class My_Validator_OneFieldShouldBePresent extend Zend_Validator_Abstract
{
const NOT_PRESENT = 'notPresent';
protected $_messageTemplates = array(
self::NOT_PRESENT => 'Field %field% is not present'
);
protected $_messageVariables = array(
'field' => '_field'
);
protected $_field;
protected $_listOfFields;
public function __construct( array $listOfFields )
{
$this->_listOfFields = $listOfFields;
}
public function isValid( $value, $context = null )
{
if( !is_array( $context ) )
{
$this->_error( self::NOT_PRESENT );
return false;
}
foreach( $this->_listOfFields as $field )
{
if( isset( $context[ $field ] ) )
{
return true;
}
}
$this->_field = $field;
$this->_error( self::NOT_PRESENT );
return false;
}
}
Usage:
$oneOfTheseFieldsShouldBePresent = array( 'companyname', 'companyother' );
$companyname = new Zend_Form_Element_Text('companyname');
$companyname->setLabel('Company Name');
$companyname->setDecorators($decors);
$companyname->addValidator( new My_Validator_OneFieldShouldBePresent( $oneOfTheseFieldsShouldBePresent ) );
$this->addElement($companyname);
$companyother = new Zend_Form_Element_Text('companyother');
$companyother->setLabel('Company Other');
$companyother->setDecorators($decors);
$companyname->addValidator( new My_Validator_OneFieldShouldBePresent( $oneOfTheseFieldsShouldBePresent ) );
$this->addElement($companyother);
The solution provided by #fireeyedboy is handy but not working for this exact issue.
Zend_Validate_Abstract is using the context, which cannot be passed as variable to isValid(). This way when using the isValid() method (no matter if the original or overwritten one) the empty fields are not passed over and validated (unless you have setRequired(true) or setAllowEmpty(false), which we don't want). So in the case when you leave both two fields (companyname and companyother) empty, no action will take place. The only solution I am aware of is extending the Zend_Validate class to allow empty fields being validated.
Please let me know if you know better solution as I am struggling with similar problem too.
I haven't come across such a solution, but it's perfectly valid so +1.
I would extend Your_Form::isValid() to include a manual check for the values of those two elements.
If all fields pass their own individual validators, this validation probably belongs on the form as-a-whole and such it could be placed on the validation of the form instead of the fields. Do you agree with this line of thinking?
I agree with #chelmertz that a feature like this does not exists.
What I don't agree is extending Your_Form::isValid(). Instead, I'd write a custom Validator that accepts the values of both form elements that have to have a value. This way I could reuse it on arbitrary form elements. This is somewhat similar to the Identical Validator.
I'm using the CSRF hidden hash element with Zend_Form and trying to Unit Test the login but don't know how to write a Unit Test to include that element. Looked in the docs and read as many tutorials as I could find. I even delicioused them all, but no one mentions this.
Csrf value is generated each time form is rendered. Hidden element of the form gets prefilled with that value. This value also gets stored in session.
After submitting form, validation checks if value posted from the form is stored in session, if not then validation fails. It is essential, that form must be rendered during the test (so it can generate the hidden value and store it to session), then we can extract what is the hidden value out of rendered html, and later we can add hidden hash value into our request.
Consider this example:
function testAddPageStoreValidData()
{
// render the page with form
$this->dispatch('/form-page');
// fetch content of the page
$html = $this->getResponse()->getBody();
// parse page content, find the hash value prefilled to the hidden element
$dom = new Zend_Dom_Query($html);
$csrf = $dom->query('#csrf')->current()->getAttribute('value');
// reset tester for one more request
$this->resetRequest()
->resetResponse();
// now include $csrf value parsed from form, to the next request
$this->request->setMethod('POST')
->setPost(array('title'=>'MyNewTitle',
'body'=>'Body',
'csrf'=>$csrf));
$this->dispatch('/form-page');
// ...
}
The correct hash is stored in the session, and the Hash form element has a Zend_Session_Namespace instance which contains the namespace for the hash.
To unit test the element, you would replace the Zend_Session_Namespace instance in the element (with setSession) with one you create yourself which contains the correct hash (the hash is stored in key "hash")
For further examples you could probably look at the Zend Framework unit tests for the Zend_Form_Element_Hash class. I would assume they have had to deal with this as well.
I set an environment variable in my Apache vhost file, which tells the code which server it's running on:
development, staging, or production
The line for the vhost file is:
SetEnv SITE_ENV "dev"
Then I just make my forms react to the appropriate environment:
if($_SERVER['SITE_ENV']!='dev')
{
$form_element->addValidator($csrf_validator);
}
I use this same technique for lots of stuff. For example, if it IS dev, I redirect all outgoing email to me, etc.
I answered a more recent question similar to this one. I'm putting my answer here as well in case it helps anybody in the future.
I recently found a great way of testing forms with hash elements. This will use a mock object to stub away the hash element and you won't have to worry about it. You won't even have to do a session_start or anything this way. You won't have to 'prerender' the form either.
First create a 'stub' class like so
class My_Form_Element_HashStub extends Zend_Form_Element_Hash
{
public function __construct(){}
}
Then, add the following to the form somewhere.
class MyForm extends Zend_Form
{
protected $_hashElement;
public function setHashElement( Zend_Form_Hash_Element $hash )
{
$this->_hashElement = $hash;
return $this;
}
protected function _getHashElement( $name = 'hashElement' )
{
if( !isset( $this->_hashElement )
{
if( isset( $name ) )
{
$element = new Zend_Form_Element_Hash( $name,
array( 'id' => $name ) );
}
else
{
$element = new Zend_Form_Element_Hash( 'hashElement',
array( 'id' => 'hashElement' ) );
}
$this->setHashElement( $element );
return $this->_hashElement;
}
}
/**
* In your init method you can now add the hash element like below
*/
public function init()
{
//other code
$this->addElement( $this->_getHashElement( 'myotherhashelementname' );
//other code
}
}
The set method is there just for testing purposes really. You probably won't use it at all during real use but now in phpunit you can right the following.
class My_Form_LoginTest extends PHPUnit_Framework_TestCase
{
/**
*
* #var My_Form_Login
*/
protected $_form;
/**
*
* #var PHPUnit_Framework_MockObject_MockObject
*/
protected $_hash;
public function setUp()
{
parent::setUp();
$this->_hash = $this->getMock( 'My_Form_Element_HashStub' );
$this->_form = new My_Form_Login( array(
'action' => '/',
'hashElement' => $this->_hash
}
public function testTrue()
{
//The hash element will now always validate to true
$this->_hash
->expects( $this->any() )
->method( 'isValid' )
->will( $this->returnValue( true ) );
//OR if you need it to validate to false
$this->_hash
->expects( $this->any() )
->method( 'isValid' )
->will( $this->returnValue( true ) );
}
}
You HAVE to create your own stub. You can't just call the phpunit method getMockObject() because that will directly extend the hash element and the normal hash element does 'evil' stuff in its constructor.
With this method you don't even need to be connected to a database to test your forms! It took me a while to think of this.
If you want, you can push the setHashElement() method ( along with the variable and the get method ) into some FormAbstract base class.
REMEMBER, in phpunit you HAVE to pass the hash element during form construction. If you don't, your init() method will get called before your stub hash can be set with the set method and you'll end up using the regular hash element. You'll know you're using the regular hash element because you'll probably get some session error if you're NOT connected to a database.
Let me know if you find this helpful or if you use it.
Solution for ZF2 is creating your form in test, and getting value from your csrf form element:
$form = new \User\Form\SignupForm('create-user');
$data = [
'security' => $form->get('security')->getValue(),
'email' => 'test#test.com',
'password' => '123456',
'repeat-password' => '123456',
];
$this->dispatch('/signup', 'POST', $data);