Using FormCollections in ZF2

FormCollection is nice when you have a form with an element that can take multiple values. The ZF2 implementation is not bad, it has some drawbacks ( lack of choosing how to render the element ), but does a decent job in the end. However, the documentation is not very explicit in the implementation. Beside setting up in the php code, some javascript code is also needed.

As as showed in a previous example, styling a FormCollection requires extending the default ZF2 classes. Going forward with a full implementation takes some JS to throw it and it should work ok for if you create something or editing it after that.

Let’s consider the scenario where you have a collection. You start with it empty, you can add new elements, but also you can remove it. The applies for both create and edit pages. So the HTML of an element from the collection will need an input but also a remove button.

For the FormCollection you need to add 3 things: the collection rendered, a button to add new elements and some JS that will take care of adding the remove button to each element.

No addition is needed in the Controller ( where you define the form, do validation etc.). If you populate the form, the Collection renderer will add you in view all the existing elements, however you need to add the remove buttons yourself.

In the Form class ( I use Factory-backed extension, guess it’s a habit from ZF1), the declaration of a Collection element you look like this:

   $this->add(array(
            'type' => 'ZendFormElementCollection',
            'name' => 'element_name',
            'options' => array(

                'count' => 0,
                'should_create_template' => true,
                'allow_add' => true,
                'allow_remove' => true,
                'template_placeholder' => '__element_name__',
                'target_element' => new CustomFieldset('element_name', 'Label')
            )
        ));

The CustomFieldset is an extended Fieldset. In the ZF2 docs there is an example of an extended fieldset. Mine is a bit modified, with several parameters for flexibility:

class CustomFieldset extends Fieldset implements InputFilterProviderInterface
{

    public function __construct($name, $label, $type = 'text')
    {
        parent::__construct($name);
        $this->setHydrator(new ClassMethodsHydrator())->setObject(new CustomEntity());
        $this->add(array(
            'name' => 'name',
            'type' => $type,
            'attributes' => array(
                'required' => 'required'
            ),
            'options' => array(
                'label' => $label
            )
        ));
    }
    public function getInputFilterSpecification()
    {
        return array(
            'name' => array(
                'required' => true,
            )
        );
    }

}

Of course, this is a bit incomplete, if the element needs to be a Select, you should add more code to allow setting possible values, or you can make a different Fieldset.
The CustomEntity would be simple hydrate class, with name and value as properties, their get/set methods and simple getArrayCopy()/populate() methods.
Now we need to get into the view. As I said there are 3 things we need to add. The first 2 are very simple, the collection renderer:

<div id="element_name_div">
<?php
	echo $this->formCollection($form->get('element_name_div'));
?>
</div>

and the adding button:

<button onclick="return add_field('element_name_div','element_name_div')"
					class="btn btn-primary">Add new element</button>

As you can see, the button will use a js function to add a new element. What will do is take the template generated by formCollection renderer, add the remove button in this HTML code and insert everything it in the container that surrounds the collection. Since we have an form element with multiple values, we need to change the name for each element, by counting how many elements we have ( dynamic added or from default).

function add_field(name,index) {
    var currentCount = $('#'+name+'  input').length ;
    var template = $('#'+name+'  > span').data('template');
    var re = new RegExp("__"+index+"__","g");
    template = $($(template.replace(re, currentCount)));
    c = template.find('div.col-sm-5');
    d = template.find('div.input-group');
	var b = $(' <span class="input-group-btn"><button class="btn btn-default" >remove</button></span>');
	b.click(function (){$(this).parent().parent().parent().remove();});
    b.appendTo(d);
    d.appendTo(c);
    c.appendTo(template);
    $('#'+name+' ').append(template);
    return false;
}

In my case I use a modified generated HTML for the element ( like I showed in a previous post), if you use the default rendering, the code will be simpler.Here I set an event on the remove button, but you can very well just use an onclick attribute that will call a remove function ( this might be better in case your collection is not allowed to be empty, so you can do a check, see below).
For the editing page, you need to add the remove buttons in case the collection already has some elements. So when document is ready, iterate all elements and insert the button

function remove_button(e)
{
    $(e).parent().parent().parent().parent().remove();
    return false;
}
$(document).ready(function(){
    $('#element_name_div').each(function(index,value){
                $('').appendTo($(this));
           });
});

In case you have a collection which has condition that it should have at least one ( or more) elements, you need to modify the remove_button() to check how many children the container div should have. Something like:

function remove_button(e)
{
	if($(e).children().length>2){
		   $(e).parent().parent().parent().parent().remove();
	}else {
		  alert('You need at least element!');
	}
	return false;
}

Please note that how you make the selection remove depends on the HTML structure you have. In my case, the remove button was surounded by 4 tags ( as I was using Bootstrap HTML).

Cafea varsata pe o Filco? No problem!

Please note that this is written in Romanian.

 

Pe la inceputul anului imi cumparasem un CM Storm QFR Red. Tastatura superba, totusi a trebuit s-o duc in service, am fost unul din “norocosii” care a prins un lot cu probleme. Mai precis dupa ceva vreme ( destul de repede, parca 2 luni) incepea sa nu mai functioneze tasta stanga Alt. Toate bune si frumoase, cei de la PCG mi-au schimbat-o pe garantie.

Intre timp, am avut un drum pana in Anglia si m-am decis sa iau un Filco pe switch-uri Blue.  Asa ca vorbesc cu un amic de acolo, daca tot ajungeam in UK, n-avea rost s-o comand din tara ca sa mai platesc in plus 15 GBP sau cat era transportul.

Dupa vreo luna, adormit ca de obicei dimineata, am reusit sa vars o cana de cafea pe noua si frumoasa mea Filco Majestouch-2 :)). Bine ca cafeaua nu era fierbinte.  Insa cafeaua intrase peste tot. Mai bine de 10 switch-uri balteau de cafea, nici macar nu mai functionau. Si totusi acum tastatura merge bine merci.

Deci ce e de facut in astfel de cazuri? Ei bine, tastatura are nevoie de o baie :)). Apa nu este cea mai mare problema pentru circuite, ci alte ingrediente, care pot coroda circuitele, gen zaharul din sucuri sau , unul din cele mai grave, cafeaua, care am inteles ca are o pasiune nebuna pentru asa ceva.  Cum spuneam cafeaua intrase in toata tastatura, chiar si in switch-uri.

Am dat repede fuga la un service auto care e aproape de mine, luat 2 litri de apa distilata. Desfacut toata tastaura, pana am ramas cu placa de baza in mana si am facut o baie consistenta cu apa distilata.  Cea distilata e cea mai buna, pentru ca nu contine minerale care se pot depune pe circuite. Daca chiar nu aveti apa distilata la indemana, folositi apa de la robinet – cea imbuteliata are un continut mai ridicat de minerale( gen calciu) si este posibil sa se depuna cate ceva in timp ce spalati tastatura. Oricum, sunt mai bine ceva minerale decat zaharul sau particulele de cafea, care fac prapad.

Dupa ce am spalat ( efectiv am SPALAT) placa de baza , bagand la greu si prin switch-uri ( atentie, si dupa spalare unele switch-uri  or sa dea impresia ca nu merg, stati calmi, isi revin dupa ce se usuca), am pus toate componentele la uscat. In 2 zile am asamblat tastatura la loc si merge perfect si in ziua de azi.

Din pacate, nu mai am pozele din timpul operatiunii, singura care am gasit-o e cand deja eram la faza de re-asamblare.

20130520_114725

Concluzia finala: ati varsat ceva pe o tastatura mecanica, bagati repede la spalat. Nu lasati sa se usuce ce ati varsat pe ea, posibil sa n-o mai recuperati deloc.

Sphinx demos

Several weeks ago I made 3 sphinx sample codes along with a written article about each Sphinx feature they use.

To keep things short:

Autocomplete and suggest

Faceting

Geo Search

 

Also watch Sphinx blog, I post articles on regular basis.

Elance.com – inactive account fee – really?

Elance.com sucks. Here’s why : if you have funds in your account and you didn’t had any activity for 5 months , they will start taking 5$/months as an “inactive account fee”.
It’s not something hidden, there is a full page about it ( at http://help.elance.com/entries/101221-inactive-account-fee ). I had some money from some freelancing work I’ve done around nov. 2011, but never withdraw them as it was a small number, yet I never though they would do such thing. So from may 2012 they started to take 5$ … in the end , they took me 40$.   I mean , it’s not a big sum , but – still – enough to get drunk in one evening.

How I discovered ? I get from time to time invites to projects, usualy not following them , but today clicked on one to see what’s about, then I remembered I had some dolars there and surprise. I was like WTF.

Interesting , checked Odesk.com , which I used more extensive, to see if they have same thing. Well :

How long can I leave my oDesk earnings in my account?
You can leave your oDesk earnings in your account as long as you want. Your funds can remain in your finance account without any type of penalty or fee.a

So seems Odesk are good guys. They don’t have any penalty for “inactive account”.
I also checked Vworker.com ( now owned by freelancer.com). I have 40$ there from sept. 2011 untouched! Not sure if that will change after the transaction to freelancer.com will complete, but I’ll check.

So if you did works on elance.com, make sure you withdraw your money, otherwise elance will make sure to take a cut 🙂