On Sat, Feb 19, 2011 at 10:37 AM, Thomas <[email protected]> wrote:
> I have never used cake before but found myself thrust into it because
> a project I have inherited was developed using it. I must say I find
> it very inflexible as opposed to good old fashioned coding. I have a
> form where the developers used iterating keys in the form. What I want
> to do, and what I am accustomed to doing when the number of inputs is
> variable or unknown is something like this: <input type=text
> name=something[]/> where it just plugs my post into an array with the
> key undeclared. How do you guys bake (correct term?) into your
> projects? This is what I have now:
> echo $form->checkbox('OnsiteTrainingClass.select_class'.
> $i,array('id'=>'select'.$i,'label'=>'','value'=>
> $value['OnsiteTrainingClass']['id']));
>
> If I take the $i out of this it hoses everything up.
This is going to be long, but I figured it'd be best so you have maybe
a better idea what's going on under the hood.
Let's say we have two models, Thingy and Widget. Each Thingy is made
from one or more kinds of Widget. When creating a new Thingy, we
choose which kinds of Widgets to build it from, as well as how many of
each Kind.
To keep things simple, each of the tables has just id & name columns.
class Thingy extends AppModel
{
var $hasAndBelongsToMany = array('Widget');
var $hasMany = array('ThingiesWidget');
}
class Widget extends AppModel
{
var $hasAndBelongsToMany = array('Thingy');
var $hasMany = array('ThingiesWidget');
}
The $hasMany both of them have is the model for the join table,
thingies_widgets. Ordinarily, we wouldn't need to mention that
association, except that, along with the usual FK columns, it's going
to have a num_pieces column.
Baking views, you'll get the following for your add form:
echo $this->Form->input('name');
echo $this->Form->input('Widget');
FormHelper creates a multi-select list for Widget, using the name as
option and id as value.
<select name="data[Widget][Widget][]" multiple="multiple" id="WidgetWidget">
I misspoke when I said that Cake doesn't use the name[] format. It
does use it, but YOU don't want to do so for what you're trying to do.
Because it won't give the correct format for the data.
Selecting a few Widgets from the list and submitting results in:
Array
(
[Thingy] => Array
(
[name] => foo
)
[Widget] => Array
(
[Widget] => Array
(
[0] => 2
[1] => 3
)
)
)
That's fine, but doesn't allow for setting the number of pieces. First
thing is to change the select list to a group of checkboxes:
echo $this->Form->select('Widget', $widgets, null, array('multiple' =>
'checkbox'));
giving:
<input type="checkbox" name="data[Widget][Widget][]" value="1"
id="WidgetWidget1" />
etc.
Either way, $this->data will be formatted the same.
But we need text inputs matched up with each checkbox, so we have to
use a loop and create each checkbox/text input pair manually. Starting
with just the checkboxes:
foreach ($widgets as $key => $widget)
{
?>
<label for="Widget<?= $key ?>">
<?php
echo $this->Form->checkbox('Widget', array('value' => $key, 'id' =>
'Widget'.$key));
echo $widget;
?>
</label>
<?php
}
giving:
<label for="Widget1">
<input type="hidden" name="data[Widget][Widget]" id="Widget1_"
value="0" />
<input type="checkbox" name="data[Widget][Widget]" value="1"
id="Widget1" />
red
</label>
<label for="Widget2">
<input type="hidden" name="data[Widget][Widget]" id="Widget2_"
value="0" />
<input type="checkbox" name="data[Widget][Widget]" value="2"
id="Widget2" />
blue
</label>
etc.
Note that now we no longer have the empty [] in the name attributes.
If we selected a couple and submitted, $this->data would come in as:
Array
(
[Thingy] => Array
(
[name] => foo
)
[Widget] => Array
(
[Widget] => 0
)
)
Oops. We could instead do something like this:
foreach ($widgets as $key => $widget)
{
?>
<label for="Widget<?= $key ?>">
<input type="checkbox" name="data[Widget][Widget][]" value="<?=
$key
?>" id="Widget<?= $key ?>" />
<?= $widget ?>
</label>
<?php
}
This gives the same form mark-up, except that we've got the [] again
and the hidden element wasn't created (it's not required in this
situation). Submitting, we get:
Array
(
[Thingy] => Array
(
[name] => foo
)
[Widget] => Array
(
[Widget] => Array
(
[0] => 2
[1] => 3
)
)
)
OK, back to where we were. But we want to save our data into the
thingies_widgets table using that $hasMany association. We can change
the loop to create the tags like so:
<input type="checkbox" name="data[ThingiesWidget][<?= $key ?>]"
value="<?= $key ?>" id="Widget<?= $key ?>" />
giving:
<input type="checkbox" name="data[ThingiesWidget][1]" value="1" id="Widget1" />
etc.
and:
Array
(
[Thingy] => Array
(
[name] => foo
)
[ThingiesWidget] => Array
(
[2] => 2
[3] => 3
)
)
Getting much closer. BTW, notice that the keys have changed. They're
no longer zero-based, but the same as the ids. That's doesn't matter,
because we'll be using saveAll(), and the keys can be arbitrary
(though unique). Have a look here:
http://api.cakephp.org/view_source/model/#line-1575
Under if (Set::numeric(array_keys($data))) { ... the $key is only used
to keep track of validation errors.
So, now we can use the same manual element technique, but include the
text field, and change the naming convention slightly to create a new
sub-array and keys.
<input type="checkbox" name="data[ThingiesWidget][<?= $key
?>][widget_id]" value="<?= $key ?>" id="Widget<?= $key ?>" />
<input type="text" name="data[ThingiesWidget][<?= $key ?>][num_pieces]" />
giving:
Array
(
[Thingy] => Array
(
[name] => foo
)
[ThingiesWidget] => Array
(
[1] => Array
(
[num_pieces] =>
)
[2] => Array
(
[widget_id] => 2
[num_pieces] => 23
)
[3] => Array
(
[widget_id] => 3
[num_pieces] => 14
)
[4] => Array
(
[num_pieces] =>
)
)
)
Precisely the format for saveAll(). We only need to add the Thingy.id
to each sub-array and remove the ones with no id (the text inputs are
submitted even if empty).
function add()
{
if (!empty($this->data))
{
$this->Thingy->create();
if ($this->Thingy->save($this->data))
{
$id = $this->Thingy->getInsertID();
foreach($this->data['ThingiesWidget'] as $k => $v)
{
if (!isset($v['widget_id']))
{
unset($this->data['ThingiesWidget'][$k]);
}
else
{
$this->data['ThingiesWidget'][$k]['thingy_id'] = $id;
}
}
$this->Thingy->ThingiesWidget->saveAll($this->data['ThingiesWidget']);
...
mysql db_foo > select * from thingies_widgets;
+----+-----------+-----------+------------+
| id | thingy_id | widget_id | num_pieces |
+----+-----------+-----------+------------+
| 1 | 1 | 2 | 23 |
| 2 | 1 | 3 | 14 |
+----+-----------+-----------+------------+
2 rows in set (0.00 sec)
--
Our newest site for the community: CakePHP Video Tutorials
http://tv.cakephp.org
Check out the new CakePHP Questions site http://ask.cakephp.org and help others
with their CakePHP related questions.
To unsubscribe from this group, send email to
[email protected] For more options, visit this group at
http://groups.google.com/group/cake-php