# formidable/trunk/classes/models/fields/FrmFieldProduct.php

Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes &amp; More, version trunk. 461 lines.

- Page: https://pluginprobe.com/plugins/formidable/trunk/code/classes/models/fields/FrmFieldProduct.php
- Raw: https://pluginprobe.com/plugins/formidable/trunk/raw/classes/models/fields/FrmFieldProduct.php
- Modified: 2026-04-16T12:57:58+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/formidable/trunk/code/classes/models/fields/FrmFieldProduct.php#L10-L20`.

```php
<?php

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

/**
 * @since 6.30 This is copied from FrmProFieldProduct.php in Formidable Pro.
 */
class FrmFieldProduct extends FrmFieldType {

	protected $type = 'product';

	/**
	 * We use this because of the display cases of checkbox and radio. Dropdown can
	 * still manage with the aria-labelledby="field_[key]_label" in the custom html.
	 *
	 * @var bool
	 */
	protected $has_for_label = false;

	/**
	 * @inheritDoc
	 */
	protected function input_html() {
		return $this->multiple_input_html();
	}

	/**
	 * @inheritDoc
	 */
	protected function include_form_builder_file() {
		return $this->include_front_form_file();
	}

	/**
	 * @inheritDoc
	 */
	protected function new_field_settings() {
		return array(
			'options' => serialize(
				array(
					'',
					__( 'Product 1', 'formidable' ),
				)
			),
		);
	}

	/**
	 * @inheritDoc
	 */
	protected function field_settings_for_type() {
		$settings = parent::field_settings_for_type();

		$settings['options']       = true;
		$settings['default_value'] = true;

		/**
		 * So that we can have placeholder and size in Advanced settings
		 * because of the possibility of displaying like a select field.
		 */
		$settings['clear_on_focus'] = true;
		$settings['size']           = true;

		return $settings;
	}

	/**
	 * @param array $args
	 */
	protected function show_priority_field_choices( $args = array() ) {
		include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/radio-images.php';
	}

	/**
	 * @param array $args
	 */
	public function show_primary_options( $args ) {
		$field      = $args['field'];
		$data_types = $this->get_data_type_settings();
		include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/product-options.php';
		parent::show_primary_options( $args );
	}

	/**
	 * @return array
	 */
	private function get_data_type_settings() {
		return array(
			'select'   => __( 'Dropdown', 'formidable' ),
			'radio'    => __( 'Radio Buttons', 'formidable' ),
			'checkbox' => __( 'Checkboxes', 'formidable' ),
			'single'   => __( 'Single Product', 'formidable' ),
			'user_def' => __( 'User Defined (Pro)', 'formidable' ),
		);
	}

	/**
	 * @inheritDoc
	 */
	protected function extra_field_opts() {
		$form_id = $this->get_field_column( 'form_id' );

		return array_merge(
			parent::extra_field_opts(),
			array(
				'data_type' => 'select',
				/* 'align' is needed for the checkbox and radio 'data_type' cases. */
				'align'     => FrmStylesController::get_style_val( 'check_align', $form_id ? $form_id : 'default' ),
			)
		);
	}

	/**
	 * @param array|object $field
	 */
	public function displayed_field_type( $field ) {
		return array(
			$this->type => true,
		);
	}

	/**
	 * Remove the frm_opt_container class for dropdowns.
	 *
	 * @param array  $args
	 * @param string $html
	 *
	 * @return string
	 */
	protected function after_replace_html_shortcodes( $args, $html ) {
		$data_type = FrmField::get_option( $this->field, 'data_type' );

		if ( 'radio' !== $data_type && 'checkbox' !== $data_type ) {
			$html = str_replace( '"frm_opt_container', '"frm_data_container', $html );
		}

		$form_id = $args['parent_form_id'] ?? 0;

		if ( ! $form_id ) {
			$form_id = $this->get_field_column( 'form_id' );
		}

		FrmCurrencyHelper::add_currency_to_global( $form_id );

		return $html;
	}

	/**
	 * @inheritDoc
	 */
	protected function include_front_form_file() {
		if ( is_array( $this->field ) && ! is_array( $this->field['options'] ) ) {
			$this->field['options'] = array();
		}

		$product_type = FrmField::get_option( $this->field, 'data_type' );
		$file         = FrmAppHelper::plugin_path() . '/classes/views/frm-fields/front-end/product-';

		if ( $product_type === 'checkbox' ) {
			$file .= 'radio.php';
		} elseif ( array_key_exists( $product_type, self::get_data_type_settings() ) ) {
			$file .= str_replace( '_', '-', $product_type ) . '.php';
		} else {
			$file .= 'select.php';
		}

		return $file;
	}

	/**
	 * @param array $args
	 *
	 * @return bool
	 */
	protected function should_continue_to_field_options( $args ) {
		return true;
	}

	/**
	 * @inheritDoc
	 */
	protected function get_bulk_edit_string() {
		return __( 'Bulk Edit Products', 'formidable' );
	}

	/**
	 * @param array $args
	 */
	protected function show_single_option( $args ) {
		self::single_option( $args['field'] );
	}

	/**
	 * @inheritDoc
	 */
	protected function extra_field_choices_class() {
		$data_type  = FrmField::get_option( $this->field, 'data_type' );
		$type_class = '';

		if ( 'single' === $data_type ) {
			$type_class = ' frm_prod_type_' . $data_type;
		}

		return ' frmjs_product_choices' . $type_class;
	}

	/**
	 * @param array $args
	 */
	protected function field_choices_heading_attrs( $args ) {
		echo ' class="frm_prod_options_heading"';
	}

	/**
	 * @param array $args
	 *
	 * @return array
	 */
	public function validate( $args ) {
		$parent_errors = parent::validate( $args );

		if ( $parent_errors || empty( $args['value'] ) ) {
			return $parent_errors;
		}

		global $frm_products;

		if ( ! $frm_products ) {
			$frm_products = array();
		}

		$product_field_key = $this->get_field_column( 'id' ) . '_' . $args['parent_field_id'] . '_' . $args['key_pointer'];

		if ( ! isset( $frm_products[ $product_field_key ] ) || ! is_array( $frm_products[ $product_field_key ] ) ) {
			$frm_products[ $product_field_key ] = array();
		}
		$frm_products[ $product_field_key ]['price']           = $this->get_posted_price( $args['value'] );
		$frm_products[ $product_field_key ]['key_pointer']     = $args['key_pointer'];
		$frm_products[ $product_field_key ]['parent_field_id'] = $args['parent_field_id'];

		return array();
	}

	/**
	 * @param array|string $posted_value
	 *
	 * @return array|float
	 */
	public function get_posted_price( $posted_value ) {
		$price   = 0;
		$options = $this->get_field_column( 'options' );

		if ( ! is_array( $options ) ) {
			return $price;
		}

		if ( ! is_array( $posted_value ) ) {
			$this->get_price( $options, $posted_value, $price );
			return $price;
		}

		$price = array();

		foreach ( $posted_value as $value ) {
			$this->get_price( $options, $value, $price );
		}

		return $price;
	}

	/**
	 * @param array     $options
	 * @param mixed     $value
	 * @param array|int $price
	 *
	 * @return void
	 */
	private function get_price( $options, $value, &$price ) {
		foreach ( $options as $option ) {
			if ( ! is_array( $option ) ) {
				continue;
			}

			// In Lite, since separate values is not available, we can always use label.
			$check_key = 'label';

			if ( ! isset( $option[ $check_key ] ) || $option[ $check_key ] !== $value ) {
				continue;
			}

			if ( isset( $option['price'] ) && trim( $option['price'] ) ) {
				if ( is_array( $price ) ) {
					$price[] = trim( $option['price'] );
				} else {
					$price = trim( $option['price'] );
				}
			}
			break;
		}//end foreach
	}

	/**
	 * @param array $field
	 *
	 * @return void
	 */
	public static function single_option( $field ) {
		self::hidden_field_option( $field );

		if ( ! is_array( $field['options'] ) ) {
			return;
		}

		$base_name     = 'default_value_' . $field['id'];
		$html_id       = $field['html_id'] ?? FrmFieldsHelper::get_html_id( $field );
		$default_type  = self::get_default_value_type( $field );
		$options_count = count( $field['options'] );

		foreach ( $field['options'] as $opt_key => $opt ) {
			$field_val  = FrmFieldsHelper::get_value_from_array( $opt, $opt_key, $field );
			$price      = self::get_price_from_array( $opt, $opt_key, $field );
			$opt        = FrmFieldsHelper::get_label_from_array( $opt, $opt_key, $field );
			$field_name = $base_name . ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
			$checked    = isset( $field['default_value'] ) && ( is_array( $field['default_value'] )
					? in_array( $field_val, $field['default_value'], true )
					: $field['default_value'] === $field_val );

			require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/product-single-option.php';

			unset( $checked );
		}
	}

	/**
	 * @param array $field
	 *
	 * @return void
	 */
	private static function hidden_field_option( $field ) {
		$opt_key    = '000';
		$field_val  = __( 'New Product', 'formidable' );
		$opt        = $field_val;
		$price      = '';
		$checked    = false;
		$field_name = 'default_value_' . $field['id'];
		$html_id    = $field['html_id'] ?? FrmFieldsHelper::get_html_id( $field );

		$default_type = self::get_default_value_type( $field );
		$field_name  .= $default_type === 'checkbox' ? '[' . $opt_key . ']' : '';

		require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/product-single-option.php';
	}

	/**
	 * @param array $field
	 *
	 * @return string
	 */
	private static function get_default_value_type( $field ) {
		$data_type = FrmField::get_option_in_array( $field, 'data_type' );
		return 'checkbox' === $data_type ? $data_type : 'radio';
	}

	/**
	 * @param array  $opt {
	 *     The option.
	 *
	 *     @type string $price
	 * }
	 *
	 * @param string $opt_key
	 * @param array  $field
	 *
	 * @return string
	 */
	public static function get_price_from_array( $opt, $opt_key, $field ) {
		/**
		 * Filter the product option before getting the price.
		 *
		 * @since 6.30
		 *
		 * @param array|string $opt
		 * @param string       $opt_key
		 * @param array        $field
		 */
		$opt = apply_filters( 'frm_field_price_saved', $opt, $opt_key, $field );
		return is_array( $opt ) ? (string) ( $opt['price'] ?? '' ) : '';
	}

	/**
	 * Format price when show=price.
	 *
	 * @param array|string $value
	 * @param array        $atts {
	 *     The args.
	 *
	 *     @type string $show
	 *     @type string $sep
	 * }
	 *
	 * @return array|string
	 */
	protected function prepare_display_value( $value, $atts ) {
		if ( ! isset( $atts['show'] ) || $atts['show'] !== 'price' ) {
			return $value;
		}

		$options = is_array( $this->field ) ? $this->field['options'] : $this->field->options;

		if ( ! is_array( $options ) ) {
			return $value;
		}

		$is_array = is_array( $value );

		// Temporary turn value into array.
		if ( ! $is_array ) {
			$value = ! empty( $atts['sep'] ) && is_string( $atts['sep'] ) ? explode( $atts['sep'], $value ) : (array) $value;
		}

		$format = $atts['format'] ?? 'currency';

		// Lite does not support separate values, so always check label.
		// In Pro, this will check for "value" instead when separate values is enabled.
		$check_key = 'label';

		if ( 'single' === FrmField::get_option( $this->field, 'data_type' ) ) {
			foreach ( $options as $option ) {
				if ( is_array( $option ) ) {
					$options = array( $option );
					break;
				}
			}
		}

		/**
		 * @var array $value
		 */

		foreach ( $value as $k => $v ) {
			foreach ( $options as $option ) {
				if ( ! is_array( $option ) || ! isset( $option['price'] ) ) {
					continue;
				}

				if ( ! isset( $option[ $check_key ] ) || $option[ $check_key ] !== $v ) {
					continue;
				}

				$value[ $k ] = 'number' === $format ? $option['price'] : FrmCurrencyHelper::format_price( $option['price'] );

				break;
			}
		}

		return $is_array ? $value : implode( $atts['sep'], $value );
	}
}

```
