Zend_Form_Element_MultiCheckbox: How to display a long list of checkboxes as columns? - zend-framework

So I am using a Zend_Form_Element_MultiCheckbox to display a long list of checkboxes. If I simply echo the element, I get lots of checkboxes separated by <br /> tags. I would like to figure out a way to utilize the simplicity of the Zend_Form_Element_MultiCheckbox but also display as multiple columns (i.e. 10 checkboxes in a <div style="float:left">). I can do it manually if I had an array of single checkbox elements, but it isn't the cleanest solution:
<?php
if (count($checkboxes) > 5) {
$columns = array_chunk($checkboxes, count($checkboxes) / 2); //two columns
} else {
$columns = array($checkboxes);
}
?>
<div id="checkboxes">
<?php foreach ($columns as $columnOfCheckboxes): ?>
<div style="float:left;">
<?php foreach($columnOfCheckboxes as $checkbox): ?>
<?php echo $checkbox ?> <?php echo $checkbox->getLabel() ?><br />
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
How can I do this same sort of thing and still use the Zend_Form_Element_MultiCheckbox?

The best place to do this is using a view helper. Here is something I thought of really quickly that you could do. You can use this in your view scripts are attach it to a Zend_Form_Element.
I am going to assume you know how to use custom view helpers and how to add them to form elements.
class My_View_Helper_FormMultiCheckbox extends Zend_View_Helper_FormMultiCheckbox
{
public function formMultiCheckbox($name, $value = null, $attribs = null,
$options = null, $listsep = "<br />\n")
{
// zend_form_element attrib has higher precedence
if (isset($attribs['listsep'])) {
$listsep = $attribs['listsep'];
}
// Store original separator for later if changed
$origSep = $listsep;
// Don't allow whitespace as a seperator
$listsep = trim($listsep);
// Force a separator if empty
if (empty($listsep)) {
$listsep = $attribs['listsep'] = "<br />\n";
}
$string = $this->formRadio($name, $value, $attribs, $options, $listsep);
$checkboxes = explode($listsep, $string);
$html = '';
// Your code
if (count($checkboxes) > 5) {
$columns = array_chunk($checkboxes, count($checkboxes) / 2); //two columns
} else {
$columns = array($checkboxes);
}
foreach ($columns as $columnOfCheckboxes) {
$html .= '<div style="float:left;">';
$html .= implode($origSep, $columnOfCheckboxes);
$html .= '</div>';
}
return $html;
}
}
If you need further explanation just let me know. I did this fairly quickly.
EDIT
The reason I named it the same and placed in a different directory was only to override Zend's view helper. By naming it the same and adding my helper path:
$view->addHelperPath('My/View/Helper', 'My_View_Helper');
My custom view helper gets precedence over Zend's helper. Doing this allowed me to test without changing any of my forms,elements, or views that used Zend's helper. Basically, that's how you replace one of Zend's view helpers with one of your own.
Only reason I mentioned the note on adding custom view helpers and adding to form elements was because I assumed you might rename the helper to better suit your needs.

Related

Find and extract content of division of certain class using DomXPath

I am trying to extract and save into PHP string (or array) the content of a certain section of a remote page. That particular section looks like:
<section class="intro">
<div class="container">
<h1>Student Club</h1>
<h2>Subtitle</h2>
<p>Lore ipsum paragraph.</p>
</div>
</section>
And since I can't narrow down using class container because there are several other sections of class "container" on the same page and because there is the only section of class "intro", I use the following code to find the right division:
$doc = new DOMDocument;
$doc->preserveWhiteSpace = FALSE;
#$doc->loadHTMLFile("https://www.remotesite.tld/remotepage.html");
$finder = new DomXPath($doc);
$intro = $finder->query("//*[contains(#class, 'intro')]");
And at this point, I'm hitting a problem - can't extract the content of $intro as PHP string.
Trying further the following code
foreach ($intro as $item) {
$string = $item->nodeValue;
echo $string;
}
gives only the text value, all the tags are stripped and I really need all those divs, h1 and h2 and p tags preserved for further manipulation needs.
Trying:
foreach ($intro->attributes as $attr) {
$name = $attr->nodeName;
$value = $attr->nodeValue;
echo $name;
echo $value;
}
is giving the error:
Notice: Undefined property: DOMNodeList::$attributes in
So how could I extract the full HTML code of the found DOM elements?
I knew I was so close... I just needed to do:
foreach ($intro as $item) {
$h1= $item->getElementsByTagName('h1');
$h2= $item->getElementsByTagName('h2');
$p= $item->getElementsByTagName('p');
}

Zend Framework 2 form element error-class + custom ViewHelper to render form

This is my third question this week (and overall) - hope I don't get banned here :D
Anyway, searched around and couldn't find an exact explanation to solve my issue(s).
A. I've searched around and found a custom ViewHelper to render my forms. What it does is recursively get all fieldsets and when it gets to the element level, it goes like this:
public function renderElement($element) {
$html = '
<div class="row">' .
'<label class="col-md-12" for="' . $element->getAttribute('id') . '">' . $element->getLabel() . '</label>' .
$this->view->formElement($element) .
$this->view->FormElementErrors($element) .
'<div class="clearfix" style="height: 15px;"></div>';
return $html . PHP_EOL;
}
Form renders ok, except:
1) How can I add an error class to the form element? (like if I use formRow helper in my view, it automatically ads an 'input-error' class, while also keeping the initial class specified in my fieldset when creating the element - 'attributes' => array('class' => 'some-class')), so the element's class attribute becomes "some-class input-error" in case it's invalid.
2) How can I set a class for the 'ul' containing the error messages (the 'ul' rendered by $this->view->FormElementErrors($element))? Hope this is a one-liner and I don't have to go message-by-message and compose the html for the error messages list, but if not so be it (I don't know how to do that either).
B. Let's say that sometimes I don't use this custom ViewHelper to render my form. Zend's formRow view helper can be handy sometimes. This brings me to the following code in my view:
echo $this->formRow($this->form->get('user_fieldset')->get('user_name'));
I've noticed this automatically adds 'input-error' class on my element (in case it's invalid) which is perfect, BUT how can I also tell formRow to give a class to the 'ul' that's displaying the error messages?
I'd go even further and ask how I can turn this:
echo $this->formLabel($this->form->get('user_fieldset')->get('user_name'));
echo $this->formInput($this->form->get('user_fieldset')->get('user_name'));
echo $this->formElementErrors($this->form->get('user_fieldset')->get('user_name'), array('class' => 'form-validation-error'));
into something that ads an error-class to the element as well, not just to the error messages list, but if anyone answers to point A I think it's the same issue.
I've managed to do it like this:
public function renderElement($element) {
// FORM ROW
$html = '<div class="form-group">';
// LABEL
$html .= '<label class="form-label" for="' . $element->getAttribute('id') . '">' . $element->getLabel() . '</label>';
// ELEMENT
/*
- Check if element has error messages
- If it does, add my error-class to the element's existing one(s),
to style the element differently on error
*/
if (count($element->getMessages()) > 0) {
$classAttribute = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : '');
$classAttribute .= 'input-error';
/*
* Normally, here I would have added a space in my string (' input-error')
* Logically, I would figure that if the element already has a class "cls"
* and I would do $element->getAttribute('class') . 'another-class'
* then the class attribute would become "clsanother-class"
* BUT it seems that, even if I don't intentionally add a space in my string,
* I still get "cls another-class" as the resulted concatenated string
* I assume that when building the form, ZF2 automatically
* adds spaces after attributes values? so you/it won't have to
* consider that later, when you'd eventually need to add another
* value to an attribute?
*/
$element->setAttribute('class', $classAttribute);
}
$html .= $this->view->formElement($element);
/*
* Of course, you could decide/need to do things differently,
* depending on the element's type
switch ($element->getAttribute('type')) {
case 'text':
case 'email': {
break;
}
default: {
}
}
*/
// ERROR MESSAGES
// Custom class (.form-validation-error) for the default html wrapper - <ul>
$html .= $this->view->FormElementErrors($element, array('class' => 'form-validation-error'));
$html .= '</div>'; # /.form-group
$html .= '<div class="clearfix" style="height: 15px;"></div>';
return $html . PHP_EOL;
}
I'm not to fond of this, but I suppose there is no shorter way. I thought ZF2 shoud have something like:
if ($element->hasErrors()) { $element->addClass('some-class'); }
right out of the box. That's the answer I would have expected, that it would simply be a method I missed/couldn't find. But it turns out that ZF2 doesn't have quite anything in the whole world that you might need right out of the box, you end up having to write the (more or less) occasional helpers.
Anyway, if someone ever needs it here's the entire RenderForm view helper:
namespace User\View\Helper;
use Zend\View\Helper\AbstractHelper;
class RenderForm extends AbstractHelper {
public function __invoke($form) {
$form->prepare();
$html = $this->view->form()->openTag($form) . PHP_EOL;
$html .= $this->renderFieldsets($form->getFieldsets());
$html .= $this->renderElements($form->getElements());
$html .= $this->view->form()->closeTag($form) . PHP_EOL;
return $html;
}
public function renderFieldsets($fieldsets) {
foreach ($fieldsets as $fieldset) {
if (count($fieldset->getFieldsets()) > 0) {
$html = $this->renderFieldsets($fieldset->getFieldsets());
} else {
$html = '<fieldset>';
// You can use fieldset's name for the legend (if that's not inappropriate)
$html .= '<legend>' . ucfirst($fieldset->getName()) . '</legend>';
// or it's label (if you had set one)
// $html .= '<legend>' . ucfirst($fieldset->getLabel()) . '</legend>';
$html .= $this->renderElements($fieldset->getElements());
$html .= '</fieldset>';
// I actually never use the <fieldset> html tag.
// Feel free to use anything you like, if you do have to
// make grouping certain elements stand out to the user
}
}
return $html;
}
public function renderElements($elements) {
$html = '';
foreach ($elements as $element) {
$html .= $this->renderElement($element);
}
return $html;
}
public function renderElement($element) {
// FORM ROW
$html = '<div class="form-group">';
// LABEL
$html .= '<label class="form-label" for="' . $element->getAttribute('id') . '">' . $element->getLabel() . '</label>'; # add translation here
// ELEMENT
/*
- Check if element has error messages
- If it does, add my error-class to the element's existing one(s),
to style the element differently on error
*/
if (count($element->getMessages()) > 0) {
$classAttribute = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : '');
$classAttribute .= 'input-error';
$element->setAttribute('class', $classAttribute);
}
$html .= $this->view->formElement($element);
// ERROR MESSAGES
$html .= $this->view->FormElementErrors($element, array('class' => 'form-validation-error'));
$html .= '</div>'; # /.row
$html .= '<div class="clearfix" style="height: 15px;"></div>';
return $html . PHP_EOL;
}
}
User is my module. I've created a 'viewhelper.config.php' in it's config folder:
return array(
'invokables' => array(
'renderForm' => 'User\View\Helper\RenderForm',
),
);
and in Module.php:
public function getViewHelperConfig() {
return include __DIR__ . '/config/viewhelper.config.php';
}
Then, in your view simply call:
$this->renderForm($form);
Of course, if you don't have many view helpers, you could not create a separate config file just for that, leave Module.php alone and simply add:
'view_helpers' => array(
'invokables' => array(
'renderForm' => 'User\View\Helper\RenderForm',
),
),
to any configuration file.
I use getMessages() method to check if an element has an validation message. for eg.
<div class="form-group <?=($this->form->get('user_fieldset')->get('user_name')->getMessages())?'has-error':'';?>">
...
</div>
This question seems to be very old and you must have solved it by yourself. :)

How to filter New WP Query by Custom Field Value?

I am creating new pages for each of my categories in wordpress. The post editor has a custom field that allows the selection of a sector type, this gets applied to the post on update. The custom field key is: sector, for custom field meta value options lets use SectorA, SectorB and SectorC. I am using a custom post type called projects.
I followed the advice at this link http://weblogtoolscollection.com/archives/2008/04/13/how-to-only-retrieve-posts-with-custom-fields/
How can I change the query line in the code below so that it filters the loop by a Sector name, lets use SectorA. I'll then reuse the code on each template page changing the value to SectorB and SectorC on the other pages.
I think this needs changing somehow:
$customPosts->query('showposts=5&sector=sectorA&post_type=projects' );
Currently it echos the sector value and description value successfully but is showing all the posts. So my attempt to limit it to sectorA using sector=sectorA doesn't seem to work?
This code is in functions.php:
function get_custom_field_posts_join($join) {
global $wpdb, $customFields;
return $join . " JOIN $wpdb->postmeta postmeta ON (postmeta.post_id = $wpdb->posts.ID and postmeta.meta_key in ($customFields)) ";
}
function get_custom_field_posts_group($group) {
global $wpdb;
$group .= " $wpdb->posts.ID ";
return $group;
}
And this code is on the Template Page:
<?php /* Begin Custom Field Posts */ ?>
<h2>Custom Posts</h2>
<ul>
<?php
global $customFields;
$customFields = "'sector', 'description'";
$customPosts = new WP_Query();
add_filter('posts_join', 'get_custom_field_posts_join');
add_filter('posts_groupby', 'get_custom_field_posts_group');
$customPosts->query('showposts=5&sector=sectorA&post_type=projects' );//Uses same parameters as query_posts
remove_filter('posts_join', 'get_custom_field_posts_join');
remove_filter('posts_groupby', 'get_custom_field_posts_group');
while ($customPosts->have_posts()) : $customPosts->the_post();
$sector = get_post_custom_values("sector");
$description= get_post_custom_values("description");?>
<li><?php echo $sector[0]; ?></li>
<li><?php echo $description[0]; ?></li><br />
<?php endwhile; ?>
</ul>
<?php /* End Custom Field Posts */ ?>
Thanks for your help
May be this what you want
function get_custom_field_posts_join($join) {
global $wpdb, $customSector;
return $join . " JOIN $wpdb->postmeta postmeta ON (postmeta.post_id = $wpdb->posts.ID and postmeta.meta_key = 'sector' and postmeta.value = '$customSector') ";
}
and modification of page
$customSector='sectorA';//<--- insert this
add_filter('posts_join', 'get_custom_field_posts_join');
Try using this code.
<?php
$sector = get_post_meta($post->ID, "sector", false);
if ($sector[0]=="") { ?>
<!-- If there are no custom fields, show nothing -->
<?php } else { ?>
<div class="sector">
<h3>Title</h3>
<?php foreach($sector as $sector) {
echo '<blockquote><p>'.$sector.'</p></blockquote>';
} ?>
</div>
<?php } ?>

Zend - How do I combine a form and pagination in the same view

This is probably simpler that it seems but I am stumped. I have a simple controller that builds a small form for inputting parameters. When the form is submitted, the same page is redrawn with the same form at the top but with a paginated view of results under it. My controller is here:
public function indexAction()
{
$form = new Application_Form_Battery();
$request = $this->getRequest();
if ($request->isPost()) {
$data = $request->getPost();
$form->populate($data);
// get the data
Zend_View_Helper_PaginationControl::setDefaultViewPartial('pagination.phtml');
$reportsTBL = new Model_DBTable_Reports();
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($reportsTBL->getBatteryLog($data)));
$paginator->setCurrentPageNumber($this->_getParam('page',1))
->setItemCountPerPage(50);
$this->view->paginator = $paginator;
}
$this->view->form = $form;
}
As you can see, after the form is submitted, the paginated results are overwritten by the view->form statement... How can I combine them after form submit? I can't have two view calls...
Some thing like untested here,
indexController
public function indexAction()
{
.....
$_category = new Admin_Model_DbTable_Category();
$category = $_category->browse($browseArray);
$this->view->vCount = count($category);
$paginator = Zend_Paginator::factory($category);
$page = ($postData['page'] > 0) ? $postData['page'] : 1;
$paginator->setCurrentPageNumber($page);
$paginator->setItemCountPerPage(10);
$paginator->setPageRange(5);
$this->view->categoryList = $paginator; // print_obj($paginator);
....
}
index.phtml
.....
<ul>
<?php if (count($this->categoryList) != 0) { ?>
<?php
foreach ($this->categoryList AS $key => $category) {
?>
<li>
<div >
<?php echo $category['name'] ?>
</div>
</li>
<?php } ?>
<li>
<div><?php echo "Total Category(" . $this->vCount . ")"; ?></div>
<div style="text-align:center;"><?php echo $this->paginationControl($this->categoryList, 'Sliding', 'pagination.phtml'); ?></div>
</li>
<?php } else { ?>
<li><div style="text-align:center;">No records found</div></li>
<?php } ?>
</ul>
...
Category.php
class Admin_Model_DbTable_Category extends Zend_Db_Table_Abstract
{
...
protected $_name = 'category';
protected $_primary = 'category_id';
public function browse($browseArray = array())
{
extract($browseArray);
$whereSQL = ' `parent` = 0 ';
if($category_id)
$whereSQL .= ' AND `category_id` = "'. $category_id .'"';
if($catname)
$whereSQL .= ' AND `name` = "'. $catname .'"';
if($name)
$whereSQL .= ' AND `name` LIKE "%'. $name .'%"';
if($description)
$whereSQL .= ' AND `description` LIKE "%'. $description .'%"';
if($status)
$whereSQL .= ' AND `status` = "'. $status .'"';
$select = $this->_db->select()
->from($this->_name)
->where($whereSQL);
$result = $this->getAdapter()->fetchAll($select);
return $result;
}
....
}
You can use general function named browse which will fetch result from table , by using the result we can paginate the data when page is rendering.
I solved it. I was using a view that called a form template and I was trying to display the paginated results on that form. I moved the paginated results to the actual view script and everything is solved.
Thanks for the help.

Zend Form Element Row needs either an id or class set through Zend_Config_Ini

I have the following default decorators in a Zend_Config_Ini to set up my form:
elementDecorators.viewHelper.decorator = "ViewHelper"
elementDecorators.label.decorator = "Label"
elementDecorators.errors.decorator = "Errors"
elementDecorators.htmlTag.decorator = "HtmlTag"
elementDecorators.htmlTag.options.tag = "li"
I have the following element definition also in the Zend_Config_Ini:
elements.username.type = "text"
elements.username.options.label = "Username:"
elements.username.options.required = true
and the following output is produced:
<li>
<label for="username" class="required">Username:</label>
<input type="text" name="username" id="username" value="" />
</li>
Now what I need to know is, how do I (through the ini config file preferably), set the id or class of the LI tag? I would like the following output:
<li id="form-username-element"> ... </li>
or
<li class="form-2col"> ... </li>
Update:
I was able to get it by overriding all the decorators in the element config itself like this:
elements.username.options.decorators.viewHelper.decorator = "ViewHelper"
elements.username.options.decorators.label.decorator = "Label"
elements.username.options.decorators.errors.decorator = "Errors"
elements.username.options.decorators.htmlTag.decorator = "HtmlTag"
elements.username.options.decorators.htmlTag.options.tag = "li"
elements.username.options.decorators.htmlTag.options.class = "username-row-element"
So that will work, however creates a lot of duplication as that would have to go onto every element (with the single change of the last line which would be the class setting itself). So what I am NOW wondering, is, from the ini file, is there a way to just override the class name using the default decorators (rather than having to duplicate all of the decorators for each element)?
Easiest thing to do is create your own Decorator. For instance, I've created an ElementWrap decorator, which wraps each element with a div and adds the necessary class and id. It could look something like this:
class Form_Decorator_ElementWrap extends Zend_Form_Decorator_Abstract
{
public function render($content)
{
$element = $this->getElement();
if($this->getOption('openOnly')) {
return '<div class="'.$this->getClass().'" id="'.$this->getId().'">' . $content;
} else if($this->getOption('closeOnly')) {
return $content . PHP_EOL . '</div>' . PHP_EOL;
} else {
return '<div class="'.$this->getClass().'" id="'.$this->getId().'">' . $content . '</div>';
}
}
public function getClass()
{
$element = $this->getElement();
$classes = array(
'field_wrap',
'field_' . strtolower(substr(strrchr($element->getType(), '_'), 1)),
$this->getOption('class'),
);
if($element->hasErrors()) {
$classes[] = 'field_error';
}
if($elementClass = $element->getAttrib('class')) {
$classes[] = $elementClass;
}
return implode(' ', array_filter($classes));
}
public function getId()
{
return 'fieldwrap-' . $element->getId();
}
}