Gravity Forms: Require minimum/maximum characters in input field - wordpress

is it possible to limit the characters in an input field for every form we have?
I need an input field with exact 5 numbers (zip field).
I found this solution:
(full code here: https://gravitywiz.com/require-minimum-character-limit-gravity-forms/)
new GW_Minimum_Characters( array(
'form_id' => 524,
'field_id' => 1,
'min_chars' => 4,
'max_chars' => 5,
'min_validation_message' => __( 'Oops! You need to enter at least %s characters.' ),
'max_validation_message' => __( 'Oops! You can only enter %s characters.')
)
);
The problem is, that we have dozens of forms and couldn't build a function for all of them ;)
So we couldn't use the "form_id" and the "field_id".
Maybe there is a way to use a parameter name for the input field?

I had a similar issue where I needed to have minimum no. of characters for text fields and textarea. This setting had to be used in different forms and multiple fields therefore I could not add filters with pre-defined form ids and field ids.
What I did is that I created a new setting which was visible when editing a field and then I validated the submission. You can use the code below:
add_action( 'gform_field_standard_settings', 'minimum_field_setting', 10 );
add_action( 'gform_editor_js', 'editor_script' );
add_filter( 'gform_tooltips', 'add_encryption_tooltips' );
add_filter( 'gform_validation', 'general_validation' );
/**
* Adds the Minimum Characters Field to Form Fields
*
* #param integer $position
*/
function minimum_field_setting( $position ) {
//Position: Underneath Description TextArea
if ( $position == 75 ) {
?>
<li class="minlen_setting field_setting">
<label for="field_minlen" class="section_label">
<?php esc_html_e( 'Minimum Characters', 'gravityforms' ); ?>
<?php gform_tooltip( 'form_field_minlen' ) ?>
</label>
<input type="number"
id="field_minlen"
onblur="SetFieldProperty('minLength', this.value);"
value=""
/>
</li>
<?php
}
}
/**
* Adds Javascript to Gravity Forms in order to render the new setting field in their appropriate field types
*/
function editor_script() {
?>
<script type='text/javascript'>
//Append field setting only to text and textarea fields
jQuery.each(fieldSettings, function (index, value) {
if (index === 'textarea' || index === 'text') {
fieldSettings[index] += ", .minlen_setting";
}
});
//binding to the load field settings event to initialize the checkbox
jQuery(document).bind("gform_load_field_settings", function (event, field, form) {
if (field.type === 'textarea' || field.type === 'text') {
console.log(field);
if (typeof field.minLength !== "undefined") {
jQuery("#field_minlen").attr("value", field.minLength);
} else {
jQuery("#field_minlen").attr("value", '');
}
}
});
</script>
<?php
}
/**
* Add GF Tooltip for Minimum Length
*
* #param array $tooltips
*
* #return mixed
*/
function add_encryption_tooltips( $tooltips ) {
$tooltips['form_field_minlen'] = "<h6>Minimum Length</h6>Minimum number of characters for this field";
return $tooltips;
}
/**
* Validate Form Submission
*
* #param array $validation_result
*
* #return mixed
*/
function general_validation( $validation_result ) {
$form = $validation_result['form'];
foreach ( $form['fields'] as &$field ) {
if ( in_array( $field->type, [ 'text', 'textarea' ] ) && ! empty( $field->minLength ) ) {
$input_name = 'input_' . $field->id;
if ( isset( $_POST[ $input_name ] ) && $_POST[ $input_name ] != '' ) {
if ( strlen( $_POST[ $input_name ] ) < (int) $field->minLength ) {
$field->failed_validation = true;
$field->validation_message = 'Field must contain at least ' . $field->minLength . ' characters';
$validation_result['is_valid'] = false;
}
}
}
}
$validation_result['form'] = $form;
return $validation_result;
}
You can change the setting from Minimum Length to Exact Length and then update the validation accordingly.

Related

woocommerce_order_item_display_meta_value filtering out input fields? Checkbox for order item meta

I'm trying to display some order item meta data as checkboxes instead of plain text via the woocommerce_order_item_display_meta_value hook (hope my terminology is right. I'm new to this woocommerce game).
add_filter( 'woocommerce_order_item_display_meta_value', 'modify_order_item_display_value' , 10, 3 );
function modify_order_item_display_value( $display_value, $meta, $wc_order_item ) {
$meta_data = $meta->get_data();
if( $meta_data['key'] === '_Packed' ) {
$display_value = __('<input type="checkbox" name="mycheckbox" id="mycheckbox" value="1" />', 'woocommerce' );
}
return $display_value;
}
If I do something like $display_value = '<p>TEST</p>'; it comes out fine. When I try to use an input it doesn't show up. Looking at DOM in the browser I can get down to the <td> that should contain my input and the HTML just isn't there.
Does woocommerce_order_item_display_meta_value filter out input fields?
Is there another way to get a checkbox for order item meta data ?
I don't know if this is the right way to do it but it works. I'd love any feedback about the "woocommercyness" of it...
STEP1
Use woocommerce_hidden_order_itemmeta to hide my field so it doesn't get shown "the normal way"
add_filter( 'woocommerce_hidden_order_itemmeta', 'myplugin_hide_order_item_meta_fields' );
function myplugin_hide_order_item_meta_fields( $fields )
{
$fields[] = '_Packed';
return $fields;
}
STEP 2
Use woocommerce_after_order_itemmeta to display my field in a custom way after all of the other Meta data.
Note that I give the checkbox an ID based on the order item id. In other code I need to see if these IDs are passed in requests or not
add_action( 'woocommerce_after_order_itemmeta', 'myplugin_order_meta_customized_display',10, 3 );
function myplugin_order_meta_customized_display( $item_id, $item, $product )
{
$packed = $item->get_meta('_Packed') === 'Y';
echo "<div>";
woocommerce_form_field("packed_$item_id",
array(
'type' => 'checkbox',
'label' => __('Packed', 'woocommerce'),
'required' => false,
'class' => array('input-checkbox')
), $packed);
echo "</div>";
}
STEP 3
Use the REQUEST to set the meta datafunction myplugin_set_meta_from_post($order_id, $order)
I call a wrapper from the actions woocommerce_checkout_order_processed and save_post_shop_order. The actuions get the order id - I lookup the order (because I'm doing other stuff, not just the crazy meta display)
function myplugin_set_meta_from_post($order_id, $order)
{
// For each meta data in each order item
// If it is '_Packed' then see if a corresponding packed_<id> parameter
// is in the request and update the item's meta data accordingly.
//
foreach ($order->get_items() as $item_key => $item ) {
$item_meta = $item->get_meta_data();
foreach($item_meta as $meta)
{
if ($meta->__get('key') === '_Packed')
{
$k = 'packed_'.$item->get_id();
$packed = 'Y';
if ( ! isset( $_REQUEST[ $k ] ) || empty( $_REQUEST[ $k ] ) ) {
$packed = 'N';
}
if ($packed != $item->get_meta('_Packed'))
{
$item->update_meta_data('_Packed', $packed);
}
}
}
}
}

Word Press: How to add custom validation to Advanced Custom Fields

I'm trying to use this plug in: Advanced Custom Fields and add my own validation for specific fields.
I tried to follow this post:
https://www.advancedcustomfields.com/resources/acf-validate_value/
But what file do I change in my word press folder to allow it to work?
I'm trying to validate a Pin # that matches 1234.
I have recently tried to put it in the acf plugin folder for validation.php.
I tried it in here:
In
function acf_validate_value( $value, $field, $input ) {
// vars
$valid = true;
$message = sprintf( __( '%s value is required', 'acf' ), $field['label'] );
// valid
if ( $field['required'] ) {
// valid is set to false if the value is empty, but allow 0 as a valid value
if ( empty( $value ) && ! is_numeric( $value ) ) {
$valid = false;
}
}
/**
* Filters whether the value is valid.
*
* #date 28/09/13
* #since 5.0.0
*
* #param bool $valid The valid status. Return a string to display a custom error message.
* #param mixed $value The value.
* #param array $field The field array.
* #param string $input The input element's name attribute.
*/
$valid = apply_filters( "acf/validate_value/type={$field['type']}", $valid, $value, $field, $input );
$valid = apply_filters( "acf/validate_value/name={$field['_name']}", $valid, $value, $field, $input );
$valid = apply_filters( "acf/validate_value/key={$field['key']}", $valid, $value, $field, $input );
$valid = apply_filters( 'acf/validate_value', $valid, $value, $field, $input );
$valid = add_filter('acf/validate_value/name=registration_pin', 'my_acf_validate_value', 10, 4);
// allow $valid to be a custom error message
if ( ! empty( $valid ) && is_string( $valid ) ) {
$message = $valid;
$valid = false;
}
if ( ! $valid ) {
acf_add_validation_error( $input, $message );
return false;
}
// return
return true;
}
function my_acf_validation_registation_pin($valid, $value, $field, $input_name ){
// Bail early if value is already invalid.
if( $valid !== true ) {
return $valid;
}
console.log('validate registration pin: test ->' + new Date());
}
Yeah your code wont work. The add_filter will be hit too late.
If you are simply trying to get an acf field to validate the submitted value against a defined set value.
Best way to do this is to add this to your functions.php, see comments in my code below...
<?php
// add validate value filter for all acf fields with the field name `registration_pin`
// you can use `acf/validate_value/key=` to target a specific acf field by key if you have multiple fields with the same name `registration_pin` in different field groups
add_filter('acf/validate_value/name=registration_pin', 'acf_validate_reg_pin', 10, 4);
// function used by the above `add_filter`
function acf_validate_reg_pin($valid, $value, $field, $input_name) {
// define our registration pin to validate submitted value against
$reg_pin = '1234';
// if submitted value is empty or false
// remove this condition if you want the form to still validate if the field value is empty/false
if(!$value) {
// field returns invalid and show this error message
return __('Please enter registration pin.');
}
// if submitted value is a string and is not equal to our defined registration pin
if(is_string($value) && $value !== $reg_pin) {
// field returns invalid and show this error message
return __('Incorrect registration pin.');
}
// return field as valid, if none of the above conditions are true
return $valid;
}
This might make it a bit clearer for you about how to handle validation on specific acf fields. You should use this add_filter in your functions.php so the filter is hit.
You can use require_once(__DIR__ . '/folder/example.php'); in your functions.php, if you want to keep things tidy in your functions. You can load php files, lib classes, etc... everything required here runs first in the wordpress admin and your theme.

Why does my custom WP_List_Table not return any results?

I have built a plugin to display results a custom table I have built in my Wordpress DB, following the tutorial found on Smashing Magazine I have built the following code:
<?php
/*
Plugin Name: Post Tracker
Version: 0.1
*/
add_action('admin_menu', 'yt_plugin_setup_menu');
function yt_plugin_setup_menu(){
add_menu_page( 'Post Tracker Plugin', 'Post Tracker', 'manage_options', 'tracker', 'tracker_init' );
}
//Our class extends the WP_List_Table class, so we need to make sure that it's there
if(!class_exists('WP_List_Table')){
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
class Link_List_Table extends WP_List_Table {
/**
* Constructor, we override the parent to pass our own arguments
* We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX.
*/
function __construct() {
parent::__construct( array(
'singular'=> 'yt_tracker_link', //Singular label
'plural' => 'yt_tracker_links', //plural label, also this well be one of the table css class
'ajax' => false //We won't support Ajax for this table
) );
}
/**
* Add extra markup in the toolbars before or after the list
* #param string $which, helps you decide if you add the markup after (bottom) or before (top) the list
*/
function extra_tablenav( $which ) {
if ( $which == "top" ){
//The code that goes before the table is here
echo"Hello, I'm before the table";
}
if ( $which == "bottom" ){
//The code that goes after the table is there
echo"Hi, I'm after the table";
}
}
/**
* Define the columns that are going to be used in the table
* #return array $columns, the array of columns to use with the table
*/
function get_columns() {
return $columns= array(
'col_id'=>__('ID'),
'col_post_id'=>__('Post'),
'col_last_checked'=>__('Last Checked'),
'col_last_reviewed'=>__('Last Reviewed')
);
}
/**
* Decide which columns to activate the sorting functionality on
* #return array $sortable, the array of columns that can be sorted by the user
*/
function get_sortable_columns() {
return $sortable = array(
'col_last_checked'=>'last_checked',
'col_last_reviewed'=>'last_checked'
);
}
/**
* Prepare the table with different parameters, pagination, columns and table elements
*/
function prepare_items() {
global $wpdb, $_wp_column_headers;
$screen = get_current_screen();
/* -- Preparing your query -- */
$query = "SELECT * FROM yt_tracker";
/* -- Ordering parameters -- */
//Parameters that are going to be used to order the result
$orderby = !empty($_GET["orderby"]) ? mysql_real_escape_string($_GET["orderby"]) : 'ASC';
$order = !empty($_GET["order"]) ? mysql_real_escape_string($_GET["order"]) : '';
if(!empty($orderby) & !empty($order)){ $query.=' ORDER BY '.$orderby.' '.$order; }
/* -- Pagination parameters -- */
//Number of elements in your table?
$totalitems = $wpdb->query($query); //return the total number of affected rows
//How many to display per page?
$perpage = 5;
//Which page is this?
$paged = !empty($_GET["paged"]) ? mysql_real_escape_string($_GET["paged"]) : '';
//Page Number
if(empty($paged) || !is_numeric($paged) || $paged<=0 ){ $paged=1; } //How many pages do we have in total? $totalpages = ceil($totalitems/$perpage); //adjust the query to take pagination into account if(!empty($paged) && !empty($perpage)){ $offset=($paged-1)*$perpage; $query.=' LIMIT '.(int)$offset.','.(int)$perpage; }
/* -- Register the pagination -- */
$this->set_pagination_args( array(
"total_items" => $totalitems,
"total_pages" => $totalpages,
"per_page" => $perpage,
) );
//The pagination links are automatically built according to those parameters
/* -- Register the Columns -- */
$columns = $this->get_columns();
$_wp_column_headers[$screen->id]=$columns;
/* -- Fetch the items -- */
$this->items = $wpdb->get_results($query);
}
/**
* Display the rows of records in the table
* #return string, echo the markup of the rows
*/
function display_rows() {
//Get the records registered in the prepare_items method
$records = $this->items;
//Get the columns registered in the get_columns and get_sortable_columns methods
list( $columns, $hidden ) = $this->get_column_info();
//Loop for each record
if(!empty($records)){foreach($records as $rec){
//Open the line
echo '< tr id="record_'.$rec->id.'">';
foreach ( $columns as $column_name => $column_display_name ) {
//Style attributes for each col
$class = "class='$column_name column-$column_name'";
$style = "";
if ( in_array( $column_name, $hidden ) ) $style = ' style="display:none;"';
$attributes = $class . $style;
//edit link
$editlink = '/wp-admin/link.php?action=edit&link_id='.(int)$rec->link_id;
//Display the cell
switch ( $column_name ) {
case "col_id": echo '< td '.$attributes.'>'.$rec->id.'< /td>'; break;
case "col_post_id": echo '< td '.$attributes.'>'.$rec->post_id.'7< /td>'; break;
case "col_last_checked": echo '< td '.$attributes.'>'.$rec->last_checked.'< /td>'; break;
case "col_last_reviewed": echo '< td '.$attributes.'>'.$rec->last_reviewed.'< /td>'; break;
}
}
//Close the line
echo'< /tr>';
}}
}
}
function tracker_init() {
//Prepare Table of elements
$wp_list_table = new Link_List_Table();
$wp_list_table->prepare_items();
//Table of elements
$wp_list_table->display();
}
?>
This is what my table schema looks like:
My actual plugin page only returns the following:
I believe it something to do with the column setup but I'm not sure what's wrong.
The markup is wrong. The code copied from smashing magazine has spaces in the markup. < tr>, < /tr>, < td>, < /td> should be <tr>, </tr>, <td>, </td>.

How to add a custom attribute?

How to add a custom attribute in the field Contact Form 7 without javascript ?
For example, there is such a field on the page:
<input type="text" name="name" class="form-control" id="name-1" data-attr="custom" data-msg="Текст 1">
Question: is it possible to set these custom attributes (data-attr, data-msg) of fields in the admin panel?
Find the name of your field.
[text* text-21]
If the name of your field name="text-21", like in my example, add this code to functions.php file.
add_filter( 'wpcf7_form_elements', 'imp_wpcf7_form_elements' );
function imp_wpcf7_form_elements( $content ) {
$str_pos = strpos( $content, 'name="text-21"' );
if ( $str_pos !== false ) {
$content = substr_replace( $content, ' data-attr="custom" data-msg="Foo Bar 1" ', $str_pos, 0 );
}
return $content;
}
Note, it will add those attributes to all forms elements where the name is text-21, if you want prevent it then give your form element some unique name [text* unique-name]
And change the code to
add_filter( 'wpcf7_form_elements', 'imp_wpcf7_form_elements' );
function imp_wpcf7_form_elements( $content ) {
$str_pos = strpos( $content, 'name="unique-name"' );
if ( $str_pos !== false ) {
$content = substr_replace( $content, ' data-attr="custom" data-msg="Foo Bar 1" ', $str_pos, 0 );
}
return $content;
}
Here is a generic solution that doesn't involve hardcoding the field name and the attributes
add_filter( 'wpcf7_form_tag', function ( $tag ) {
$datas = [];
foreach ( (array)$tag['options'] as $option ) {
if ( strpos( $option, 'data-' ) === 0 ) {
$option = explode( ':', $option, 2 );
$datas[$option[0]] = apply_filters('wpcf7_option_value', $option[1], $option[0]);
}
}
if ( ! empty( $datas ) ) {
$id = uniqid('tmp-wpcf');
$tag['options'][] = "class:$id";
add_filter( 'wpcf7_form_elements', function ($content) use ($id, $datas) {
return str_replace($id, $name, str_replace($id.'"', '"'. wpcf7_format_atts($datas), $content));
});
}
return $tag;
} );
It works on all data attributes so you can use it like this
[text* my-name data-foo:bar data-biz:baz placeholder "Blabla"]
Output: <input type="text" name="my-name" data-foo="bar" data-biz="baz" placeholder="Blabla">
Since wpcf7 doesn't provide a way to hook into options directly the solutions uses a trick and temporary adds a uniquely generated class to the field that is then replaced in a later filter with the added attributes
If you need it to work with more than just data attributes you can whitelist some more attributes by replacing this line
if ( strpos( $option, 'data-' ) === 0 ) {
to something like the following
if ( preg_match( '/^(data-|pattern|my-custom-attribute)/', $option ) ) {
Note: wpcf7_format_atts will not output empty attributes, so make sure you give a value to your attributes
Multiple attributes can be added also. eg
add_filter( 'wpcf7_form_elements', 'imp_wpcf7_form_elements' );
function imp_wpcf7_form_elements( $content ) {
$str_pos = strpos( $content, 'name="your-email-homepage"' );
$content = substr_replace( $content, ' aria-describedby="emailHelp" ', $str_pos, 0 );
$str_pos2 = strpos( $content, 'name="your-fname-homepage"' );
$content = substr_replace( $content, ' aria-describedby="fnameHelp" ', $str_pos2, 0 );
$str_pos3 = strpos( $content, 'name="your-lname-homepage"' );
$content = substr_replace( $content, ' aria-describedby="lnameHelp" ', $str_pos3, 0 );
return $content;
}
Extending on from Tofandel's solution, for the benefit of those who got 99% of the way there, but suffered validation issues - I've resolved that in my case and would like to offer an extended solution that gets as far as Tofandel's (putting the attribute into the form proper) but also successfully validates on submission.
add_filter('wpcf7_form_tag', function($tag) {
$data = [];
foreach ((array)$tag['options'] as $option) {
if (strpos( $option, 'autocomplete') === 0) {
$option = explode(':', $option, 2);
$data[$option[0]] = apply_filters('wpcf7_option_value', $option[1], $option[0]);
}
}
if(!empty($data)) {
add_filter('wpcf7_form_elements', function ($content) use ($tag, $data) {
$data_attrs = wpcf7_format_atts($data);
$name = $tag['name'];
$content_plus_data_attrs = str_replace("name=\"$name\"", "name=\"$name\" " . $data_attrs, $content);
return $content_plus_data_attrs;
});
}
return $tag;
} );
Rather than changing the tag ID to a random value only to replace it with the "real" value later, we just reference the real value in the first place, replacing the relevant part of the content in the wpcf7_form_elements filter (in my case, autocomplete, but as Tofandel's example shows, this can be extended to any data attribute you'd like).
I propose a solution from the one given by Tofandel without JS (CF7) error and to authorize the use of an attribute without value:
/**
* Add custom attributes on inputs
* Put "data-my-attribute" to use it, with or without value
*
* #param array $tag
*
* #return array
*/
function cf7AddCustomAttributes($tag) {
$datas = [];
foreach ((array) $tag['options'] as $option) {
if (strpos($option, 'data-') === 0 || strpos($option, 'id:') === 0) {
$option = explode(':', $option, 2);
$datas[$option[0]] = apply_filters('wpcf7_option_value', $option[1], $option[0]);
}
}
if (!empty($datas)) {
$id = $tag['name'];
if (array_key_exists('id', $datas)) {
$id = $datas['id'];
} else {
$tag['options'][] = "id:$id";
}
add_filter('wpcf7_form_elements', function ($content) use ($id, $datas) {
$attributesHtml = '';
$idHtml = "id=\"$id\"";
foreach ($datas as $key => $value) {
$attributesHtml .= " $key";
if (is_string($value) && strlen($value) > 0) {
$attributesHtml .= "=\"$value\"";
}
}
return str_replace($idHtml, "$idHtml $attributesHtml ", $content);
});
}
return $tag;
}
add_filter('wpcf7_form_tag', 'cf7AddCustomAttributes');
I use this simple method for setting attribute
[checkbox optName use_label_element "optName"]
<script>
document.querySelector(".optName").setAttribute("onchange","myscript");
</script>

Wordpress - radio button checkout woocommerce show/hide required field

I'm Italian (sorry for my english) and I'm not a programmer.
I need to insert in my checkout woocommerce site, a radio button with two options to answer this question: "Sei un privato cittadino, un'azienda o un libero professionista?".
These are the options:
1) Privato cittadino
2) Azienda o libero professionista
When users click on the first option, it has to show a required field: "codice fiscale". When users click on the second option, it has to show two required fields: "P.Iva" and "Ragione sociale". I've created the radio field with this code in form-billing.php:
<div class="woocommerce-billing-fields">
<?php if ( wc_ship_to_billing_address_only() && WC()->cart->needs_shipping() ) : ?>
<h3><?php _e( 'Billing & Shipping', 'woocommerce' ); ?></h3>
<?php else : ?>
<h3><?php _e( 'Billing Details', 'woocommerce' ); ?></h3>
<?
if($key=='billing_first_name')
{
?>
<p>Sei un privato cittadino, un'azienda o un libero professionista?</p>
<input type="radio" name="choice" value="privato_cittadino">Privato cittadino
<input type="radio" name="choice" value="azienda_professionista">Azienda o libero professionista<br>
<?
}
?>
<?php endif; ?>
It works properly, but now I don't know how can I create the required fields like I described.
Can you help me step by step? Please, consider I'm not a programmer.
To answer the question, it will need to be done in 4 steps.
Add 4 custom fields to the woocommerce checkout page. The way you have done it is not best practice and should be done through use of actions/filters.
Validate the data when the request is made to the back-end, this is to make sure the user actually selected and entered what is required.
Save the data to the order as post meta so it can be accessed later.
Implement javascript toggle feature so that based on what the user selects for the first question, the related fields are then shown.
Adding custom fields to woocommerce checkout
You will need to find a suitable action for where you would like to add the fields. I would recommend using the action woocommerce_after_checkout_billing_form as it will display fields after all the personal/billing information.
if( !function_exists( 'custom_checkout_question_field' ) ) {
/**
* Add custom question field after the billing form fields
*
* #param Integer $order_id New order id
*/
function custom_checkout_question_field( $checkout ) {
echo "<div class='custom-question-field-wrapper custom-question-1'>";
echo sprintf( '<p>%s</p>', __( "Sei un privato cittadino, un'azienda o un libero professionista?" ) );
woocommerce_form_field( 'custom_question_field', array(
'type' => 'radio',
'required' => true,
'class' => array('custom-question-field', 'form-row-wide'),
'options' => array(
'privato_cittadino' => 'Privato cittadino',
'azienda_professionista' => 'Azienda o libero professionista',
),
), $checkout->get_value( 'custom_question_field' ) );
woocommerce_form_field( 'custom_question_text_codice_fiscale', array(
'type' => 'text',
'label' => 'Codice Fiscale',
'required' => true,
'class' => array('custom-question-codice-fiscale-field', 'form-row-wide'),
), $checkout->get_value( 'custom_question_text_codice_fiscale' ) );
woocommerce_form_field( 'custom_question_text_p_iva', array(
'type' => 'text',
'label' => 'P.Iva',
'required' => true,
'class' => array('custom-question-p-iva-field', 'form-row-wide'),
), $checkout->get_value( 'custom_question_text_p_iva' ) );
woocommerce_form_field( 'custom_question_text_ragione_sociale', array(
'type' => 'text',
'label' => 'Ragione sociale',
'required' => true,
'class' => array('custom-question-ragione-sociale-field', 'form-row-wide'),
), $checkout->get_value( 'custom_question_text_ragione_sociale' ) );
echo "</div>";
}
add_action( 'woocommerce_after_checkout_billing_form', 'custom_checkout_question_field' );
}
Javascript front-end toggle
You will need to add custom javascript in order to toggle the 3 additional fields depending on the question. I have created a php function which will output a html script tag with some javascript. This is then attached to the wp_footer action.
This is not recommended method and you should really separate it into a new file js and enqueue the file when needed.
See: https://codex.wordpress.org/Plugin_API/Action_Reference/wp_enqueue_scripts
if( !function_exists( 'custom_question_conditional_javascript' ) ) {
function custom_question_conditional_javascript() {
?>
<script type="text/javascript">
(function() {
// Check if jquery exists
if(!window.jQuery) {
return;
};
var $ = window.jQuery;
$(document).ready(function() {
var questionField = $('.custom-question-field'),
codiceFiscaleField = $('.custom-question-codice-fiscale-field'),
pIvaField = $('.custom-question-p-iva-field'),
ragioneSocialeField = $('.custom-question-ragione-sociale-field ');
// Check that all fields exist
if(
!questionField.length ||
!codiceFiscaleField.length ||
!pIvaField.length ||
!ragioneSocialeField.length
) {
return;
}
function toggleVisibleFields() {
var selectedAnswer = questionField.find('input:checked').val();
if(selectedAnswer === 'privato_cittadino') {
codiceFiscaleField.show();
pIvaField.hide();
ragioneSocialeField.hide();
} else if(selectedAnswer === 'azienda_professionista') {
codiceFiscaleField.hide();
pIvaField.show();
ragioneSocialeField.show();
} else {
codiceFiscaleField.hide();
pIvaField.hide();
ragioneSocialeField.hide();
}
}
$(document).on('change', 'input[name=custom_question_field]', toggleVisibleFields);
$(document).on('updated_checkout', toggleVisibleFields);
toggleVisibleFields();
});
})();
</script>
<?php
}
add_action( 'wp_footer', 'custom_question_conditional_javascript', 1000 );
}
Getting fields from submitted post request
You will need to get the data from php $_POST array and also sanitise it to prevent sql injection or other malicious code. I have created a helper function which will grab all the fields by keys provided in an array and sanitise them using wordpress sanitize_text_field helper function.
This helper can then be used when validating and also adding post meta.
if( !function_exists( 'custom_checkout_question_get_field_values' ) ) {
/**
* Get all form field values based on user submitted post data
*
* #return Array Key/value pair of field values based on $_POST data
*/
function custom_checkout_question_get_field_values() {
$fields = [
'custom_question_field' => '',
'custom_question_text_codice_fiscale' => '',
'custom_question_text_p_iva' => '',
'custom_question_text_ragione_sociale' => '',
];
foreach( $fields as $field_name => $value ) {
if( !empty( $_POST[ $field_name ] ) ) {
$fields[ $field_name ] = sanitize_text_field( $_POST[ $field_name ] );
} else {
unset( $fields[ $field_name ] );
}
}
return $fields;
}
}
Validating fields on the back-end
Validation is important because front-end cannot be trusted, it is very easy for users to modify required fields on the front-end. You can use Woocommerce woocommerce_checkout_process action in order to validate the fields and add error messages if it does not meet what is required.
if( !function_exists( 'custom_checkout_question_field_validate' ) ) {
/**
* Custom woocommerce field validation to prevent user for completing checkout
*
* #param Integer $order_id New order id
*/
function custom_checkout_question_field_validate() {
$field_values = custom_checkout_question_get_field_values();
if ( empty( $field_values['custom_question_field'] ) ) {
wc_add_notice( 'Please select an answer for the question.', 'error' );
}
if (
$field_values['custom_question_field'] === 'privato_cittadino' &&
empty( $field_values['custom_question_text_codice_fiscale'] )
) {
wc_add_notice( 'Please enter codice fiscale.', 'error' );
}
if (
$field_values['custom_question_field'] === 'azienda_professionista' &&
empty( $field_values['custom_question_text_p_iva'] )
) {
wc_add_notice( 'Please enter p iva.', 'error' );
}
if (
$field_values['custom_question_field'] === 'azienda_professionista' &&
empty( $field_values['custom_question_text_ragione_sociale'] )
) {
wc_add_notice( 'Please enter ragione sociale.', 'error' );
}
}
add_action( 'woocommerce_checkout_process', 'custom_checkout_question_field_validate' );
}
Saving custom post meta to the order
You can use the woocommerce woocommerce_checkout_update_order_meta action in order to save additional post meta to the order post type. Here we will use the helper function custom_checkout_question_get_field_values created above to get all fields from php $_POST array and also sanitize them before saving each one to the post meta.
if( !function_exists( 'custom_checkout_question_field_save' ) ) {
/**
* Update order post meta based on submitted form values
*
* #param Integer $order_id New order id
*/
function custom_checkout_question_field_save( $order_id ) {
$field_values = custom_checkout_question_get_field_values();
foreach( $field_values as $field_name => $value ) {
if( !empty( $field_values[ $field_name ] ) ) {
update_post_meta( $order_id, $field_name, $value );
}
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_question_field_save' );
}

Resources