Showing posts with label zf. Show all posts
Showing posts with label zf. Show all posts

Friday, June 8, 2012

[ZF] Using usort() in a controller

Yes, I'm still alive.
... but very busy these days. Here goes!
Some of you probably tried using PHP's usort() method to... sort stuff. Well, it might seem unclear how it works with the Zend Framework.
As you know, this is how it should be used1:
usort ( array &$array , callable $cmp_function )

However, in a Zend Framework controller, you have to change things just a little bit to make it work:

usort($to_sort, array($this, 'sorting_method'));

I shouldn't have to explain that $to_sort is your array of data you want to sort and 'sorting_method' is your sorting method (duh).


1: http://www.php.net/usort

Wednesday, January 25, 2012

[ZF] Translating a page's title

So it's been a while, eh? Happy New Year (and Chinese New Year, while we're at it)!

For a project, we're finally in testing phase and all of a sudden, I point something out that looks wrong: the translator works perfectly... except for the page titles. By that, I mean the following tag:
<title>Title</title>
 
What gives? Well, my understanding is that the translator is not yet set in the Bootstrap when the
$this->view->headTitle($label);
is called.

The first thing I tried? Adding the translate() method to it:1
this->view->headTitle($this->view->translate($label));

... that didn't work at all. Simply no change. Great.

Then I put my thinking cap on (it's still early in the morning), and thougt this through. First of all, here is how the main parts of my Bootstrap file are laid out:
- Register plugins
- Register routes
- Prepare navigation (menus, prepare breadcrumbs, and set page title)
- Prepare translation
- Configure e-mail settings for sending stuff
- Prepare the view

Now this little brain of mine decided to kick in this morning, and made me realize: what if we prepared the translation before setting some text on the output page?

So there I am, moving the _initTranslate() method up a little, right over the _initNavigation(). Just leave the line of code that includes the translate() method ([1] above) when setting the page's title, and as long as $label is defined in your language file(s) and the translator is set correctly, shazam, it'll work like a beauty!

Final trick: you can prepend (or append, I guess) anything to your head title! In your _initView() method, be sure to do the following:
$view = new Zend_View();
$view->doctype('XHTML1_STRICT'); // Or whatever
$view->headTitle('Client - ');

// Add more settings here if needed

$view_renderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$view_renderer->setView($view);

return $view;
 
By doing this, all your pages' titles will look like "Client - Translated page title". Awesome, isn't it?

Friday, November 25, 2011

[ZF] Adding timepickers to forms

Validating forms is important to preserve data integrity throughout a project. In a user could simply enter any kind of data, this would generate many bugs and be a big nuisance security-wise.

One of the tricky input types (or let's say Zend_Form_Element types) is anything that has to do with dates. There are many ways to write the date and the time... here's a simple difference example:
USA: mm/dd/yyyy
Europe: dd/mm/yyyy

So if a user enters 05/11/2011, how can we know if we're using the 5th of november or the 11th of may as the date?

To make it easier for everyone, a simple datetimepicker is available on Binpress (← click me and follow instructions). If you're here, I'm guessing you know how to deal with PHP, Zend Framework, and you know how to RTFM (and know what that means).

The picker is based on jQuery. It's beautiful and awesome. You can configure it anyway you want and it'll work.

Right. So now your user can just click on a calendar and drag nifty little knobs to graciously select the time.

However, there is no easy way to actually have a simple timepicker as a Zend_Form_Element object.

This time is now over, and it's actually quite simple to have only the timepicker.

First of all, let's go to where the DateTimePicker is. If you've followed the simple instructions, the folder should be: \project\library\Core\Form\Element. Now duplicate the DateTimePicker.php file and rename the new one to TimePicker.php.

In this file, do the obvious: change Core_Form_Element_DateTimePicker for Core_Form_Element_TimePicker and 'dateTimePicker' for 'timePicker'.

Do the same thing for this folder: \project\library\Core\View\Helper... duplicate the DateTimePicker.php file and rename the new one to TimePicker.php, and in this file change Core_View_Helper_dateTimePicker to Core_View_Helper_timePicker, dateTimePicker to timePicker, and '%s("#%s").datetimepicker(%s);' to '%s("#%s").timepicker(%s);'.

If you're still smart, basically copy everything and remove the "date" part of it to only keep the time. Genius.

Next, well, you're done!

If you had this for a datetimepicker:
$element = new Core_Form_Element_DateTimePicker(
 $name,
 array(
  'jQueryParams' => array(
   'dateFormat' => 'dd/mm/yy',
   'defaultDate' => date('d/m/Y'),
   'timeFormat' => 'hh:mm'
  )
 )
);
Now you can simply have this for a timepicker:
$element = new Core_Form_Element_TimePicker(
 $name,
 array(
  'jQueryParams' => array(
   'dateFormat' => '',
   'defaultDate' => date('hh:mm'),
   'timeFormat' => 'hh:mm'
  )
 )
);
Oh, and just to make sure everything stays safe, let's add a little validator:
$element->addValidator('Date', true, array('format' => 'H:m'));
There you have it, a shiny little timepicker! If not, then try again or ask for help.

Now you can easily create sliders in a form so people can enter the time it took them to do something. Eat a donut, make coffee, take a bathroom break, go to work, sleep, anything! Awesome!

Friday, November 11, 2011

[ZF] Containing form elements in DIVs (or other HTML tags) with classes

Long time, no see.

So, let's say you're building a dynamic form and would like each dt (label) and dd (input field) pair to be grouped in a div,with one or more specific classes added to this div.

It's quite easy, actually!
// Wrap element in div with classes
$element->addDecorator(
array('openDiv' =>'HtmlTag'),
array(
'tag' => 'div',
'openOnly' => true,
'class' => $attrib_class
)
);
$element->addDecorator(
array('closeDiv' =>'HtmlTag'),
array('tag' => 'div', 'closeOnly' => true)
);

Considering your custom class name(s) are in the $attrib_class variable, this will output something similar to this:
<div class="class-1 class-2">
<dt id="item-id-label">
<label for="item-id">label</label>
</dt>
<dd id="item-id-element">
<input type="text" name="item-id" id="item-id" value="">
</dd>
</div>

And there you have it, customizable form elements!

Tuesday, August 23, 2011

[ZF] Adding links to form elements

Clicks, there's always too many of them.

Now imagine a form with a hundred checkboxes. Sure, you can pre-check all of them or leave them empty, but it would be much easier for the user to have a "select all" and "select none" buttons.

For more efficiency, it's better to have one for each set of checkboxes, and there's nothing better than to put these links as a decorator to the form element containing the checkboxes.


$form_element = new Zend_Form_Element_MultiCheckbox(

'name',
array(
'description' => '<a href="javascript: void();">Select all</a> <a href="javascript: void();">Select none</a>'
)
);



Now that's nice, but instead of having links, the HTML code has been escaped and that's of no use at all.

There's an easy trick to this: just set the decorators!


'decorators'	=> array(

'ViewHelper',
array(
'Description',
array('escape' => FALSE)
),
'FormElements',
'Form'
)



The important trick is to set the escape value to false, so that the HTML characters won't be converted.

And there we have it, two beautiful links that will same users a lot of hassle.

Friday, August 5, 2011

[ZF] Adding section titles in a Zend_Form

Do you have a Zend_Form with many elements and need to add section titles for it to be more user-friendly?

Nothing is as simple as this!

// Add section title
$personal_section = new Zend_Form_Element_Hidden('personal_section');
$personal_section->setLabel('Personal information');
$personal_section->setDecorators(
    array(
        'ViewHelper',
        'Description',
        'Errors',
        array('Label', array('tag' => 'h2'))
    )
);
$this->addElement($personal_section);


This basically creates a hidden input with a label, and we're just modifying this to use an H2 tag instead of the regular one. Not very nice technically, but very effective!

Friday, July 22, 2011

[ZF] Marking navigation tab as active with GET data in request

So I have a contract that I want to edit for a customer. I happily click on the "EDIT" link and the form appears, but wait a minute, something's wrong! The "contract" tab in my menu isn't showing up as the active tab anymore.

The main contract page showed the active class with this in the navigation XML file:
<contract>
    <label>Contracts</label>
    <uri>/contract/</uri>
    <resource>contract</resource>
    <privilege>index</privilege>
</contract>


...and to view a contract, I simply use (as a page of the "contract" section):
<contract-view>
    <label>View contract</label>
    <uri>/contract/view/</uri>
    <visible>0</visible>
</contract-view>


But if I want to view a contract, the ID goes through the url, as such: http://www.example.com/contract/view/123/

And when this happens, the navigation file doesn't detect that the current page is the one I want. To counter this, all you have to do is use the alternate method, which is a little longer but prevents any problem:
<contract-view>
    <label>View contract</label>
    <controller>contract</controller>
    <action>view</action>
    <visible>0</visible>
</contract-view>


When you specify the action and controller directly, the framework will take care of the rest, and even if you're using routes, everything will display smoothly. This is what I've set up for an URL like http://www.example.com/contract/123/view/
- in the navigation file:
<contract-view>
    <label>View contract</label>
    <controller>contract</controller>
    <action>view</action>
    <visible>0</visible>
</contract-view>

- in the routes.ini file:
routes.contractview.route = contract/:id/view
routes.contractview.defaults.controller = contract
routes.contractview.defaults.action = view
routes.contractview.defaults.id = 0


And there you have it! The Zend navigation and router will take care of the rest for you. Now it will detect the page you're on regardless of the URL, as long as the correct controller and action are defined in the configuration files. The beautiful tab is glowing as it should!

Monday, July 18, 2011

[ZF] Merging multiple Zend_Form into one

So you want to merge two or more of your Zend forms into one, right?

This is really easy to do, actually...

First of all, get a blank form:
$form = new Application_Form_UserInformation();

Then, get the other form(s):
$form_append = new Application_Form_UserAddress();

All you have to do is take all the elements from the other form(s) and add them to the first one!
$form->addElements($form_append->getElements());

You can still remove elements or add any by using addElement() or removeElement(). However, watch out for element names! If elements from two separate forms have the same one, the first you set will overwrite the others. This is useful for submit buttons, for example.

Be sure to always have your submission button at the end of your form, before sending it to your view:
$form->submit->setOrder(999);
$this->view->form = $form;


Piece of cake, told you so!

Monday, June 20, 2011

[ZF] Adding ("current/active/highlight" and/or other) class to menu item

So I had this menu that finally decided to generate correctly from an XML file, cool. Now I thought of the two following thigs:
  1. Show which tab is active depending on the page
  2. Add icons to the tabs
... and I wanted to implement this in the bootstrap file to make later changes easier.

1. Adding CSS class to current menu element
For the first item, nothing better than to head over to Stack Overflow, as usual. As you can see in there, all you need is:
$uri = $this->_request->getPathInfo();
$active_nav = $this->view->navigation()->findByUri($uri);
$
active_nav->active = true;
$
active_nav->setClass("active");

However, this won't work in the bootstrap because the request parameters aren't available yet. If you have a menu generating like this, it's very likely you already have a method for generating it in your bootstrap file (e.g.: _initNavigation()). To access the request parameters and find the path info, add this beforehand:
$this->bootstrap('frontController');
$front = $this->getResource('frontController');
$front->setRequest(new Zend_Controller_Request_Http());


Then, just arrange the previous code to find the correct data:
$uri = $front->getRequest()->getPathInfo();

And there we go, the "active" class will be added to whichever tab you are on.

But wait, what if I wanted to add custom icons for each tab?

2. Adding icons to menu tabs
So you've tried adding the image to the label, like so:
<label><img src="/image/nav-1.png" alt="nav-1" />User</label>
... but it doesn't work.

The trick is rather easy, just add some stuff in your CSS file as a class, and add the following node to your XML file (I guess you could easily adapt it if you're loading data from an array):
<class>nav-1</class>

Only there's a slight problem: if you are on the active tab, the bootstrap will override the class and set it to "active".

I've actually found the solution on the first try (nice guess, I admit). In the bootstrap (see code above), just get the tab's current class and add "active" to it:
$current_class = $active_nav->getClass() . ' ';
$active_nav->setClass($current_class . 'active');


That's all there is to it, you can now have a dynamic and beautiful menu!

[ZF] Hiding navigation elements

If you're building an XML file to create a menu/breadcrumb/sitemap and wish to hide elements from, let's say, the menu, here is what you can do:
Add <visible>0</visible> to your node

You can then use the usual <?php echo $this->navigation()->menu(); ?> to generate your menu.

Also note that making a node invisible will make all its children invisible as well. Duh.