CakePHP: allowing database update with button click - forms

I have a product search page with the form below. The search result is displayed on the same page with search bar at the top.
echo $this->Form->create('Searches', array('action'=>'products', 'type' => 'get', 'name' => 'textbox1'));
echo $form->input($varName1, array('label' => false));
echo $form->end('Locate');
I also have a little box next to the search result that allows (it doesn't work yet) the user to flag using checkboxes a product and accordingly update its database (table products and using model Product) with a button click. Note that I have a Searches controller for this search page.
<form method="link" action="/myapp/product/test_update_db>
<label><input type="checkbox" name="flag1" <?php echo $preCheckBox1; ?>>Flag 1</input></label>
<label><input type="checkbox" name="flag2" <?php echo $preCheckBox2; ?>>Flag 2</input></label>
<input type="submit" value="Update">
</form>
I'm having difficulty with this approach figuring out how to perform this check-box-and-DB-update routine. I'm getting to the link I'd like to go (/myapp/product/test_update_db), but I don't know how to take variables flag1 and flag2, along with row ID of this result ($results['Product']['id'])) to the new page.
Could someone guide me on how to perform this neatly? Is this general approach correct? If not, what route should I be taking? I'd prefer not to use javascript at this time, if possible.
EDIT: I think I can make this work if I use the URL for passing data.. but I'd still like to know how this could be done "under the hood" or in MVC. I feel like I'm hacking at the CakePHP platform.
UPDATE: So, I ended up using the URL parameters for retrieving information pieces like flag1 and flag2. I'm still looking for an alternative method.

To see where your is-checkbox-checked data is located, do the following in your controller:
// Cake 2.0+
debug($this->request->data);
// previous versions
debug($this->data);
If you want to pass data to your search controller from the current page, you can always add the data to your form:
$this->input
(
'Product.id',
array
(
'type' => 'hidden',
'value' => $yourProductId
)
);

I ended up using information embedded in the URL for getting submission data. Something like below..
In Products controller, when the form with flag1 and flag2 are submitted:
public function test_update_db() {
// Get variables from URL, if any, and save accordingly
$result = $this->Product->updateProduct($this->params['url'], 'url');
if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}
else {
$this->Session->setFlash('Update was unsuccessful!', 'default', array('class' => 'error'));
$this->redirect($this->referer());
}
}
This works for doing what I needed to do. I feel like there's a more proper way to do this though.

if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}

Related

How to add a class to a postLink form - CakePHP 3.4

Is it possible to add a class to the hidden form created by CakePHP's postLink form helper?
Here's my code
Form->postLink(
' ' . __('Delete'),
['action' => 'delete', $this->fetch('item')],
['confirm' => __('Are you sure you want to delete # {0}?', $this->fetch('item'))
,'escape' => false
,'title' => __('Delete')
,'class' => 'btn btn-danger btn-xs isAction'
]) ?>
Note that I'm not looking to add a class to the link that gets created.
Any ideas are welcome!
There's pretty much only one way currently, and that would be to change the formStart template temporarily, something along the lines of this:
// read current template and set the new one
$formStart = $this->Form->getTemplates('formStart');
$this->Form->setTemplates([
'formStart' => '<form class="hiddenFormClass"{{attrs}}>'
]);
echo $this->Form->postLink(/* ... */);
// set the template back to its previous state
$this->Form->setTemplates([
'formStart' => $formStart
]);
It's also possible to reset the templates to their default state using the resetTemplates() method, however this would reset all possible changes made to any of the templates, so it's probably better to play it safe as shown above.
See also
Cookbook > Controllers > Views > Helpers > Form > Customizing the Templates FormHelper Uses
API \Cake\View\Helper\FormHelper::setTemplates()
API \Cake\View\Helper\FormHelper::getTemplates()
API \Cake\View\Helper\FormHelper::resetTemplates()

Cakephp input form view as unordered list

I'm developing a website using cakephp 2.x.
Now, I create a form using Cakedc/search. This form has input(select/dropdown list).
But the list is too long, so I want the dropdown to be view as unordered list (< ul >< li >).
Like in the lazada (search for brand): http://www.lazada.com.my/womens-watches-bags-accessories/.
the code:
<?php echo $this->Form->create('Product', array(
'url' => array_merge(array('action' => 'search'), $this->params['pass'])));
echo $this->Form->input('brand_id', array('label' => 'Brand', 'options' => $brands, 'empty' => 'Select Brand'));
<?php echo $this->Form->submit(__('Search', true), array('div' => false));
echo $this->Form->end();
?>
Please someone help me. Thanks in advance..
You'll have to use javascript to get the id of the clicked li element or the link inside, use data attributes for that for example. Then set this value to a hidden form field or directly submit the whole form using ajax to update your results.
Your example URL is a simple list by the way that is just doing redirect on click.

ZF: Form array field - how to display values in the view correctly

Let's say I have a Zend_Form form that has a few text fields, e.g:
$form = new Zend_Form();
$form->addElement('text', 'name', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
$form->addElement('text', 'surname', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
After rendering it I have following HTML markup (simplified):
<div id="people">
<div class="person">
<input type="text" name="name[]" />
<input type="text" name="surname[]" />
</div>
</div>
Now I want to have the ability to add as many people as I want. I create a "+" button that in Javascript appends next div.person to the container. Before I submit the form, I have for example 5 names and 5 surnames, posted to the server as arrays. Everything is fine unless somebody puts the value in the field that does not validate. Then the whole form validation fails and when I want to display the form again (with errors) I see the PHP Warning:
htmlspecialchars() expects parameter 1 to be string, array given
Which is more or less described in ticket: http://framework.zend.com/issues/browse/ZF-8112
However, I came up with a not-very-elegant solution. What I wanted to achieve:
have all fields and values rendered again in the view
have error messages only next to the fields that contained bad values
Here is my solution (view script):
<div id="people">
<?php
$names = $form->name->getValue(); // will have an array here if the form were submitted
$surnames= $form->surname->getValue();
// only if the form were submitted we need to validate fields' values
// and display errors next to them; otherwise when user enter the page
// and render the form for the first time - he would see Required validator
// errors
$needsValidation = is_array($names) || is_array($surnames);
// print empty fields when the form is displayed the first time
if(!is_array($names))$names= array('');
if(!is_array($surnames))$surnames= array('');
// display all fields!
foreach($names as $index => $name):
$surname = $surnames[$index];
// validate value if needed
if($needsValidation){
$form->name->isValid($name);
$form->surname->isValid($surname);
}
?>
<div class="person">
<?=$form->name->setValue($name); // display field with error if did not pass the validation ?>
<?=$form->surname->setValue($surname);?>
</div>
<?php endforeach; ?>
</div>
The code work, but I want to know if there is an appropriate, more comfortable way to do this? I often hit this problem when there is a need for a more dynamic - multivalue forms and have not find better solution for a long time.
Having no better idea, I have created a view helper that handles the logic presented above. It can be found here.
If the helper is available in the view, it can be used in the following way (with the form from the question):
<?=
$this->formArrayElements(
array($form->name, $form->surname),
'partials/name_surname.phtml'
);
?>
The contents of the application/views/partials/name_surname.phtml partial view are:
<div class="person">
<?= $this->name ?>
<?= $this->surname ?>
</div>
The fields are rendered according to the posted form and validation messages are shown only next to the values that failed validation.
The helper's code is far from perfect (I just rewrote the idea from the question) but is easy to use and can be considered as good starting point.

Form->end without a div in CakePHP

I'd like to create a submit button that does NOT have a <div> around it. I assumed using the inputDefaults would make this happen like it does for all of the forms inputs, but - no luck.
Obviously I could just create a submit button via HTML, without CakePHP, but - I was hoping there'd be a cake answer. Here's what I've tried:
$this->Form->create(false, array('inputDefaults' => array('div'=>false)));
$this->Form->end('Submit');
echo $this->Form->submit('Submit', array('div'=>false));
Should do what you are after. The other example may have been Cake 1.2 or something; not sure.
It also appears you can just do this instead:
<?php
$options = array(
'label' => 'Update',
'value' => 'Update!',
'div' => false
)
);
echo $this->Form->end($options);
That looks more cakey.

Add Database Row Button Cakephp

The Controller:
function add(){
if (!empty($this->data)) {
$qnote = $this->Qnote->save($this->data);
if (!empty($qnote)) {
$this->data['Step']['qnote_id'] = $this->Qnote->id;
$this->Qnote->Step->save($this->data);
}
$this->Session->setFlash('Your note has been saved.');
$this->redirect(array('action' => 'index'));
}
}
The Form.
<?php
$userID = Authsome::get('id');
echo $form->create('Qnote', array('action'=>'add'));
echo $form->input('Qnote.id', array('type' => 'hidden'));
echo $form->input('Qnote.user_id', array('value' => $userID, 'type' => 'hidden'));
echo $form->input('Qnote.subject');
echo $form->input('Qnote.body', array('rows' => '3'));
echo $form->input('Step.id', array('type' => 'hidden'));
echo $form->input('Step.user_id', array('value' => $userID, 'type' => 'hidden'));
echo $form->input('Step.body', array('rows' => '3'));
echo $form->end('Save Notes');
?>
This Form Adds Data in 2 Models.
Model 1 = Qnote;
Model 2 = Step;
I am able to add Data to the Models.
I was wondering I could add a button to the form
The Button would allow users to add multiple Step.data to the Step model.
Some like a +1 Button.
Basically I want to add multiple steps Per Qnote.
Could someone point me in the right direction how i can achieve this.
This is something I would do with jQuery. Basically all you need to do is using jQuery to dynamically add more inputs in the CakePHPs conventions: Step.0.user_id for example.
What you need to do now on a +1: you need to count the zero up, so you will get Step.1.user_id and so on.
First option: Use a jQuery-Script for doing this
var count = 1;
$('#add_step').click(function() {
var new_form = $('.Step').eq(0).clone();
$('input, textarea, select, radio', new_form).filter('[name^="data"]').each(function() {
var name = $(this).attr('name');
var new_name = name.replace(/\[\d*\]/, '['+count+']');
$(this).attr('name', new_name).attr('value', '');
});
$('#YourForm').after(new_form);
count+;
return false;
});
In this case you're cloning a div with the class step which holds your inputs for the model Step. You then replace the name-attribute to replace the zeros through the new value of the variable count. count++ enables you to add as many steps as you want.
This is an jQuery only solution and may require additional work for your environment.
Second option: Use AJAX with an element
You could also write a function in your StepsController which renders an element which holds your form and takes care of the counter.
Third option: Use a URL-parameter to decide how many options you want
If you have an URL like /qnote/add/3 you could use the 3 as a parameter in a for-loop to iterate through these form-inputs.
You need to take care, that eventually already typed in values are sent with the form when adding another Step so that these don't get lost.
Hope this helps to get on the right way.