I have created a custom module that I need to collect info and run a function when a form is filled out and submitted.
It has two text areas and a button.
This is what I have which shows up fine on the page:
File: myFunction.admin.inc
function myFunction_form($form)
{
$form['pages'] = array(
'#type' => 'fieldset',
'#title' => t('Data'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['pages']['title'] = array(
'#type' => 'textarea',
'#title' => t('Title'),
'#rows' => 5,
'#resizable' => FALSE,
);
$form['pages']['body'] = array(
'#type' => 'text_format',
'#title' => t('Body'),
'#rows' => 5,
'#resizable' => FALSE,
'#format' => 'full_html',
);
$form['submit'] = array('#type' => 'submit', '#value' => t('Run Function'));
myFunction($form);
return $form;
}
function myFunction()
{
//This is where I use the data collected from my form and do what I need to do.
}
So the things I missing form this (and please tell me if i'm going the wrong way about this) is I need to validate the form was filled out and return error message if not.
if the form was filled out then correctly pass the field data to my function which I did simply by adding function myFunction() before return $form; but this seems like the wrong way to do it. I don;t want the myFunction() to run if there is and errors with the form.
Could someone please help me with this last part of my custom module.
Please not that this module does NOT add anythig to the database.
Again, please tell me if i'm going the wrong way about this.
You should follow the standard naming structure used by the form API. If you function is:
myFunction_form(),
then
myFunction_form_validate() will be called when submitted to perform any validation. If everything passes, then
myFunction_form_submit() will be called. You should place your submit logic (or a call to your custom function) in the submit.
This is the preferred behavior over setting $form['#submit'][] and $form['#validate'][].
See the Examples Module for simple examples of how this works.
Yes you propably go the wrong way.
You will have to add your custom validator and submit functions like
<?php
$form['#submit'][] = my_submit_callback
$form['#validate'][] = my_validator_callback
function my_submit_callback($form, &$form_state) {
// form_state array contains the submitted values
}
function my_validator_callback($form, &$form_state) {
// form_state array contains the submitted values
if ($form_state['values']['body'] == '') {
form_set_error(...)
}
}
and ofcourse remove the myFunction() call from your form_builder function
Approach it this way:
$form['#validate'][] = 'myCustomValidateFunction';
$form['#submit'][] = 'myCustomSubmitFunction';
function myCustomValidateFunction($form, &$form_state) {
\\if validation was not passed use form_set_error()
}
function myCustomSubmitFunction($form, &$form_state) {
//submit logic, $form_state includes the values
}
Related
I'm trying to detect when the date has changed inside a regular "date" field
Have seen a few forum posts about how this can be done with text fields and dropdown, but it doesn't work for date fields.
Also tried:
'displayParams' =>
array (
'javascript' => 'onchange="checkStatusOption(this)"',
),
In the editviewdefs.php, but it didn't work (works for text fields though)
The closest I've got it actually just tracking every click on on the screen and then checking the before and after state of the date field, but it's obviously not a very elegant solution
Here's the code from the extended editview
function display() {
parent::display();
$js = <<<EOT
<script type="text/javascript" language="JavaScript">
// Capture initial state
calendar_before = document.getElementById("contract_date_c").value;
// Wait for any click to take place anywhere on the screen
$(document).click(function() {
// Capture state after we clicked somewhere
calendar_after = document.getElementById("contract_date_c").value;
// Compare the before and after
if(calendar_before != calendar_after) {
// Change detected
alert("Something's changed eh?" + calendar_before +" "+ calendar_after);
}
// Set the new state of the before_calendar
calendar_before = document.getElementById("contract_date_c").value;
});
}
</script>
EOT;
// now I output the javascript
echo $js;
}
UPDATE:
I also tried the suggested solution
1) Created a file custom/modules/un_inventory/contract_date_c_change.js and put the following inside:
function yourCustomFunction(formElement){
console.log(formElement);
}
2) Included a reference to that file in the metadata file (made sure it loads it):
array (
'file' => 'custom/modules/un_inventory/contract_date_c_change.js',
),
3) Attached the updateCallback to the field:
array (
'name' => 'contract_date_c',
'label' => 'LBL_CONTRACT_DATE',
// Checks if this field got filled up and shows hidden form field
'displayParams' =>
array (
'updateCallback' => 'yourCustomFunction();',
),
),
But nothing happens when I change that date field
Check this out (just tested in SuiteCRM 7.11) for a datetime field, for other fields take a look at this answer to another SO question
First, include your custom JS in the editviewdefs.php (example for Accounts module)
'includes' =>
array (
0 =>
array (
'file' => 'modules/Accounts/Account.js',
'file' => 'custom/modules/Accounts/myCustomFile.js',
),
),
Create the custom JS file custom/modules/Accounts/myCustomFile.js .
function yourCustomFunction(formElement){
console.log(formElement);
}
Then update the field you want to monitor for changes (contractsigned_c in the example) using the following code in the editviewdefs.php:
array (
'name' => 'contractsigned_c',
'label' => 'LBL_CONTRACTSIGNED',
'displayParams' =>
array (
'updateCallback' => 'yourCustomFunction(this)',
),
),
Now do a Repair and Rebuild inside the Admin/Repair section and voilà it should work :)
You can add the JS function on the function display() if you want, its the samething, the function will be called right after native combo update. It will look like this combo_contractsigned_c.update(); yourCustomFunction(this)
use this . it is working
YAHOO.util.Event.addListener('your_field_name', 'change', function(){
// your code here
});
I'm using cakephp 2.1 and let me tell you: I just love it.
In my form I have a field that can have multiple answers (checkboxes).
I don't want to create a database field for each option, nor use an HABTM.
Update:
Since I needed several sets of flags, I went the $hasAndBelongsToMany way. (allos to add new flags without fixing the code but editing a table (eg. via phpmyadmin).
1.- Made a table/model for each set of flags I need:
2.- In my main model, declared the relation to each:
var $hasAndBelongsToMany = array('Sample','Format','Report','Openend','Dataproce','Prefield');
3.- In my main Controller, populated an array per table to use:
$openends = $this->Project->Openend->find('list', array(
//'order' => 'name'
));
4.- And use the arrays in the view:
echo $this->Form->input('Dataproce', array('label' => false, 'type' => 'select', 'multiple' => 'checkbox', 'size' => 2));
=== Old question starts here; correct answer worked for only one set of flags ===
I'd like to save a string and use it to to magically create a group of checkboxes that belong to a single data field.
I'm in the middle of it already.
my view:
echo $this->Form->input('pr_reports', array('type' => 'select',
'multiple' => 'checkbox',
'options' => array('0' => 'Interrnal',
'1' => 'Disposition',
'2' => 'Web Disposition',
'3' => 'Marginal',
'4' => 'Custom')))
my controller add method, before saving
// Used serialize() to convert the array to string
$dataString = serialize($this->request->data['Project']['pr_reports']);
$this->request->data['Project']['pr_reports'] = $dataString;
The string is being saved allright (encoded it seems but is ok: a:5:{i:0;s)
My question is how would I go when editing the record in order for the checkboxes to check themselves accordingly? That is, where do I unserialize() the string field and handle this on the edit view?
Is there a better way for this?
Thank you very much for any help.
Carlos García
==== After solution, troubles to have more than one field with a different set of flags
data is only saved for one field, ignoring the other ====
Hello;
For one field on the table it works just fine as noted;
I'm having a hard time using another field (separate set of flags).
It seems only one behaviour is attached; I was wondering if I should attach them differently.
my fields:
pr_data_format` tinyint(3) unsigned NOT NULL,
pr_report_format` tinyint(3) unsigned NOT NULL,
my controller
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'));
my model
const STATUS_ASCII = 1;
const STATUS_SPSS = 2;
const STATUS_EXCEL = 4;
const STATUS_CUSTOM = 8;
public static function pr_data_formats($value = null) {
$options = array(
self::STATUS_ASCII => __('ASCIId'),
self::STATUS_SPSS => __('SPSSBd'),
self::STATUS_EXCEL => __('Exceld'),
self::STATUS_CUSTOM => __('Customd'),
);
return parent::enum($value, $options);
}
const REP_ASCII = 1;
const REP_SPSS = 2;
const REP_EXCEL = 4;
const REP_CUSTOM = 8;
public static function pr_report_formats($value = null) {
$options = array(
self::REP_ASCII => __('ASCIIr'),
self::REP_SPSS => __('SPSSBr'),
self::REP_EXCEL => __('Excelr'),
self::REP_CUSTOM => __('Customr'),
);
return parent::enum($value, $options);
}
my view
echo $this->Form->input('pr_data_formats', array('options' => Project::pr_data_formats(), 'multiple' => 'checkbox'));
echo $this->Form->input('pr_report_formats', array('options' => Project::pr_report_formats(), 'multiple' => 'checkbox'));
Just can't figure it out, tried:
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'), array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
but no use, only one field is updated.
Can you help? We'll use some 4 or 5 flagsets.
Thank you so much.
Carlos
a proper way to do this is using a behavior. this keeps your model clean and can be applied to several models simply by putting this in your model:
public $actsAs = array('MyBehavior');
now, for serializing I use my Jsonable Behavior:
http://www.dereuromark.de/2011/07/05/introducing-two-cakephp-behaviors/
it basically makes your input array a storable string on save and the string back to an array on read. you could easily adjust this to your needs.
BUT for what you want to do with multiple checkboxes there is even a better thing - bitmasks. I developed a so called Bitmasked behavior - you would need to use 1,2,4,8,... but other than that its the same thing:
http://www.dereuromark.de/2012/02/26/bitmasked-using-bitmasks-in-cakephp/
I use it exactly for the same thing.
I am developing a custom module and used hook_form_FORM_ID_alter method. I have provided option same as in block's configuration visibility settings for specific pages. I have also created a database table for my module. I am not getting any idea how the options selected by user should be stored in my table when submitted and also how the option selected for a particular menu link should be retained. I have added this functionality on menu item edit form.
Something like this is what you're looking for:
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
$form['my_val'] = array(
'#type' => 'textfield',
'#title' => 'Some Text'
);
$form['#submit'][] = 'MYMODULE_my_form_submit';
}
function MYMODULE_my_form_submit(&$form, &$form_state) {
$val = $form_state['values']['my_val'];
db_insert('my_table')->fields(array('val' => $val))->execute();
}
That's obviously a very basic example but it shows you how to add a submit handler to an existing form, how to add an extra field, and how to then get the data for that field in the submit handler.
Drupal 7
I'm having a similar problem to one that's been presented previously but so far I've not been able to make any of the suggestions work.
I have 'Product' pages of content type 'Software Products'. I want to place a link on the product pages pointing to a Webform 'Request Information' I want to populate a (hidden) field on the form with the product name which is also the title of the referring product page.
I have tried the following but this just results in the title of the form being shown - not the referring page.
<?php
/**
* Implementation of hook_form_alter().
*/
function AddNodeInfoToForm_form_alter(&$form, $form_state, $form_id) {
switch($form_id) {
case 'webform_client_form_10': // the id of the form
{$current_object = menu_get_object();
$product_title = $current_object->title;
$form['submitted']['product']['#default_value'] = $product_title; }
return $form;
}
}
I would appreciate any pointers - I'm new to Drupal
That's quite a messy way round of doing what you need to, you should just put the product nid in the URL as part of the query string in the link from your product page and then load it up from the webform.
In your node template/preprocess:
$webform_path = 'node/10'; // Or whatever the webform's nid is
$link = l('Request Information', $webform_path, array(
'query' => array(
'product_nid' => $product_node->nid
)
));
echo $link;
Then in your form alter:
function AddNodeInfoToForm_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'webform_client_form_10' && isset($_GET['product_nid']) && is_numeric($_GET['product_nid'])) {
$product_node = node_load($_GET['product_nid']);
if ($product_node) {
$product_title = $product_node->title;
$form['submitted']['product']['#default_value'] = $product_title;
}
}
}
Note that you don't return the form from the hook_form_alter function, the $form variable is passed in by reference so changes are stored that way.
I check for data below and if data is there in the form field it updates the table. Problem is that if I click the button twice (its ajax) it wll insert a second row. How can I prevent it.
thanks
function update() {
if (!empty($this->data)) {
$this->Test->saveAll($this->data['Test'])
}
}
The problem is the form doesn't have the id field so cakephp insert a new row.
The easy way is check if exists another record with the same values before save.
You can check in function beforeSave() of the Model instead of the Controller action.
http://book.cakephp.org/#!/view/1052/beforeSave
You should take a two-pronged approach to preventing this:
First, Disable the submit button on submit. For example in jQuery:
$('#form_id').submit(function(event) {
event.preventDefault();
$('input[type=submit]', this).attr('disabled', 'disabled');
// submit form via ajax...
}
Secondly, perform rational check server-side to prevent duplicate saves. For example, two identical records submitted within a minute or so would be considered an error. I'd probably do this by creating a custom validation rule on one of the fields that's always required. In your model:
var $validate = array(
'myfield' => array(
'rule' => 'duplicateCheck',
'message' => 'Duplicate record'
)
);
function duplicateCheck() {
$conditions = array(
'Model.field1' => $this->data['Model']['field1'],
...
'Model.created >' => date("Y-m-d H:i:s", time() - 60)
);
return !$this->hasAny($conditions);
}
Then in your controller, I would redirect the user to the existing record if the save fails and it was because of that specific validation rule. This let's the user move along without caring that anything went wrong.
function add() {
if(isset($this->data)) {
if($this->Model->save($this->data)) {
...
} else {
if(isset($this->Model->validationErrors['myfield'])) {
// find the existing record and redirect user to view it
}
}
}
}