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

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()

Related

CSRF field is missing when I embed my form with a requestAction in CakePHP 3

I want to embed a contact form in multiple places on my website.
I developed a contact form in a contact() function within my MessagesController.php:
// MessagesController.php
public function contact()
{
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
... // shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
I loaded the CSRF component in the initialize() function of the AppController.php:
// AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Csrf');
... // shortened for brevity
}
The form is rendered with a contact.ctp and it works fine.
I followed CakePHP's cookbook which suggests using requestAction() within an element, then echoing the element where I want it:
// contact_form.ctp
<?php
echo $this->requestAction(
['controller' => 'Messages', 'action' => 'contact']
);
?>
And:
// home.ctp
<?= $this->element('contact_form'); ?>
The problem is that the form is rendered fine, but the CSRF hidden field is missing. It should be automatically added to the form since the CSRF component is called in the AppController.php.
I guess either using an element with a requestAction() isn't the solution for this particular case, or I am doing something wrong.
Any ideas? Thanks in advance for the input!
Request parameters need to be passed manually
requestAction() uses a new \Cake\Network\Request instance, and it doesn't pass the _Token and _csrf parameters to it, so that's why things break.
While you could pass them yourself via the $extra argument, like
$this->requestAction(
['controller' => 'Messages', 'action' => 'contact'],
[
'_Token' => $this->request->param('_Token'),
'_csrf' => $this->request->param('_csrf')
]
);
Use a cell instead
I would suggest using a cell instead, which is way more lightweight than requesting an action, also it operates in the current request and thus will work with the CSRF component out of the box.
You'd pretty much just need to copy your controller action code (as far as the code is concerned that you are showing), and add a loadModel() call to load the Messages table, something like
src/View/Cell/ContactFormCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class ContactFormCell extends Cell
{
public function display()
{
$this->loadModel('Messages');
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
// ... shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
}
Create the form in the corresponding cell template
src/Template/Cell/ContactForm/display.ctp
<?php
echo $this->Form->create(
/* ... */,
// The URL needs to be set explicitly, as the form is being
// created in the context of the current request
['url' => ['controller' => 'Messages', 'action' => 'contact']]
);
// ...
And then wherever you want to place the form, just use <?= $this->cell('ContactForm') ?>.
See also
API > \Cake\Routing\RequestActionTrait::requestAction()
Cookbook > Views > Cells

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.

TYPO3: Use t3lib_TCEforms in frontend plugin

I would like to use as much standard TYPO3 as possible to create a form to edit single records from tx_mytable.
In pi1 i load the tca for the table:
t3lib_div::loadTCA('tx_mytable');
Now I would like to use standard functions to create my form elements more or less like it is done in the backend...
I found this for the front end but cannot find any working examples:
t3lib_TCEforms_fe.php (that extends the normal t3lib_TCEforms)
Is this the right way to go or is there a better way?
I got something working but not really that nice code in the frontend
Here is a link that telss that TCA is not enough but two new entries in the array is needed
http://www.martin-helmich.de/?p=15
It is itemFormElName and itemFormElValue
// include tceforms_fe (place outside class where pipase is included)
require_once(PATH_t3lib.'class.t3lib_tceforms_fe.php');
// load TCA for table in frontend
t3lib_div::loadTCA('tx_ogcrmdb_tasks');
// init tceforms
$this->tceforms = t3lib_div::makeInstance("t3lib_TCEforms_FE");
$this->tceforms->initDefaultBEMode(); // is needed ??
$this->tceforms->backPath = $GLOBALS['BACK_PATH']; // is empty... may not be needed
//////////REPEAT FOR EACH INPUT FIELD/////////
// start create input fields, here just a single select for responsible
// conf used for tceforms similar to but not exactly like normal TCA
$conftest = array(
'itemFormElName' => $GLOBALS['TCA']['tx_ogcrmdb_tasks']['columns']['responsible']['label'],
'itemFormElValue' => 1,
'fieldConf' => array(
'config' => $GLOBALS['TCA']['tx_ogcrmdb_tasks']['columns']['responsible']['config']
)
);
// create input field
$this->content .= $this->tceforms->getSingleField_SW('','',array(),$conftest);
// wrap in form
$output = '<form action="" name="editform" method="post">';
$output .= $this->content;
$output .= '</form>';
// wrap and return output
return $output;
Still looking for a working example with custem template for input fields.

CakePHP: allowing database update with button click

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());
}

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.