/** * Dynamic functions class */ // If this file is called directly, abort.x if ( ! defined( 'WPINC' ) ) { die; } if ( ! class_exists( 'Jet_Engine_Dynamic_Functions' ) ) { class Jet_Engine_Dynamic_Functions { private $functions_list = array(); private $cache_group = 'jet_engine_dynamic_functions'; public function __construct() { $number_format_settings = array( 'decimal_point' => array( 'label' => __( 'Decimals Point', 'jet-engine' ), 'type' => 'text', 'default' => '.', ), 'thousands_separator' => array( 'label' => __( 'Thousands separator', 'jet-engine' ), 'type' => 'text', 'default' => ',', ), 'decimal_count' => array( 'label' => __( 'Decimals Count', 'jet-engine' ), 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 5, ), ); $this->functions_list = apply_filters( 'jet-engine/dynamic-functions/functions-list', array( 'sum' => array( 'label' => __( 'Summed value', 'jet-engine' ), 'type' => 'sql', // sql or raw 'query' => 'SUM( CAST( meta_value AS DECIMAL( 10, %decimal_count% ) ) )', 'cb' => array( $this, 'number_format' ), 'custom_settings' => $number_format_settings, ), 'avg' => array( 'label' => __( 'Average value', 'jet-engine' ), 'type' => 'sql', 'query' => 'ROUND( AVG( CAST( meta_value AS DECIMAL( 10, %decimal_count% ) ) ), %decimal_count% )', 'cb' => array( $this, 'number_format' ), 'custom_settings' => $number_format_settings, ), 'count' => array( 'label' => __( 'Count', 'jet-engine' ), 'type' => 'sql', 'query' => 'COUNT(meta_value)', 'cb' => false, ), 'max' => array( 'label' => __( 'Maximum value', 'jet-engine' ), 'type' => 'sql', 'query' => 'MAX( CAST( meta_value AS DECIMAL( 10, %decimal_count% ) ) )', 'cb' => array( $this, 'number_format' ), 'custom_settings' => $number_format_settings, ), 'min' => array( 'label' => __( 'Minimum value', 'jet-engine' ), 'type' => 'sql', 'query' => 'MIN( CAST( meta_value AS DECIMAL( 10, %decimal_count% ) ) )', 'cb' => array( $this, 'number_format' ), 'custom_settings' => $number_format_settings, ), 'query_var' => array( 'label' => __( 'SQL query results', 'jet-engine' ), 'type' => 'query_var', 'query' => false, 'cb' => array( $this, 'number_format' ), 'custom_settings' => $number_format_settings, 'custom_settings_cb' => array( $this, 'get_query_settings' ), ), ) ); add_filter( 'jet-engine/blocks-views/dynamic-content/data-sources', array( $this, 'register_blocks_dynamic_source' ) ); add_filter( 'jet-engine/blocks-views/editor-data', array( $this, 'register_blocks_controls' ) ); add_filter( 'jet-engine/blocks-views/dynamic-content/get-dynamic-value/dynamic_function', array( $this, 'get_block_dynamic_value' ), 10, 2 ); } /** * Register Dynamic function source for blocks dynamic data. * * @param [type] $sources [description] * @return [type] [description] */ public function register_blocks_dynamic_source( $sources ) { $sources[] = array( 'value' => 'dynamic_function', 'label' => __( 'Dynamic function', 'jet-engine' ), ); return $sources; } /** * Get block dyanmic value * * @param [type] $result [description] * @param [type] $data [description] * @return [type] [description] */ public function get_block_dynamic_value( $result, $data ) { $prepared_data = array(); $prefix = 'dynamic_functions_'; foreach ( $data as $key => $value ) { if ( false !== strpos( $key, $prefix ) ) { $prepared_data[ str_replace( $prefix, '', $key ) ] = $value; } } $function_name = \Jet_Engine_Tools::safe_get( 'function_name', $prepared_data ); $source = \Jet_Engine_Tools::safe_get( 'data_source', $prepared_data ); $field_name = \Jet_Engine_Tools::safe_get( 'field_name', $prepared_data ); if ( ! $source ) { $source = 'post_meta'; } $data_source = array( 'source' => $source ); if ( empty( $function_name ) ) { return; } if ( 'post_meta' === $source ) { $data_source['context'] = \Jet_Engine_Tools::safe_get( 'data_context', $prepared_data , 'all_posts' ); $data_source['context_tax'] = \Jet_Engine_Tools::safe_get( 'data_context_tax', $prepared_data ); $data_source['context_tax_term'] = \Jet_Engine_Tools::safe_get( 'data_context_tax_term', $prepared_data ); $data_source['context_user_id'] = \Jet_Engine_Tools::safe_get( 'data_context_user_id', $prepared_data ); $data_source['context_relation'] = \Jet_Engine_Tools::safe_get( 'data_context_relation', $prepared_data ); $data_source['post_status'] = \Jet_Engine_Tools::safe_get( 'data_post_status', $prepared_data ); $data_source['post_types'] = \Jet_Engine_Tools::safe_get( 'data_post_types', $prepared_data ); } $custom_settings = $this->get_custom_settings( $function_name, null, $prepared_data ); $result = $this->call_function( $function_name, $data_source, $field_name, $custom_settings ); return $result; } /** * Register denamic functions related controls for the blocks editor * * @param [type] $editor_config [description] * @return [type] [description] */ public function register_blocks_controls( $editor_config ) { $editor_config['dynamicFunctionsControls'] = $this->get_editor_controls( 'blocks' ); return $editor_config; } /** * Returns functions list for options */ public function functions_list() { $result = array(); foreach ( $this->functions_list as $func_key => $func_data ) { $result[ $func_key ] = ! empty( $func_data['label'] ) ? $func_data['label'] : $func_key; } return $result; } public function get_query_settings() { $queries = \Jet_Engine\Query_Builder\Manager::instance()->get_queries(); $options = array(); $sql_queries = array(); if ( ! empty( $queries ) ) { foreach( $queries as $query ) { if ( ! $query || ! is_object( $query ) ) { continue; } if ( 'sql' === $query->query_type ) { $sql_queries[] = $query; } } } if ( empty( $sql_queries ) ) { $options['no_query_notice'] = array( 'type' => 'raw_html', 'raw' => sprintf( esc_html__( 'With this function you can output any data from custom SQL query. To start, please %s', 'jet-elements' ), '' . esc_html__( 'create your query', 'jet-elements' ) . '' ) ); } $columns = array(); foreach ( $sql_queries as $query ) { $fields = $query->get_instance_fields(); $prepared_fields = array(); foreach ( $fields as $field ) { $prepared_fields[ $query->get_instance_id() . '::' . $field ] = $field; } $columns[] = array( 'label' => $query->get_instance_name(), 'options' => $prepared_fields, ); } $options['_query_field'] = array( 'label' => __( 'Query Column', 'jet-engine' ), 'description' => __( 'Select column from the query to show', 'jet-engine' ), 'type' => 'select', 'groups' => $columns, ); $options['_additional_function'] = array( 'label' => __( 'Additional Function', 'jet-engine' ), 'description' => __( 'Apply additional function to SQL query', 'jet-engine' ), 'type' => 'select', 'options' => array( '' => __( 'Select...', 'jet-engine' ), 'SUM' => __( 'Sum', 'jet-engine' ), 'AVG' => __( 'Average', 'jet-engine' ), 'COUNT' => __( 'Count', 'jet-engine' ), 'MIN' => __( 'Minimum', 'jet-engine' ), 'MAX' => __( 'Maximum', 'jet-engine' ), ), ); return $options; } public function get_relations_for_options() { $result = array( '' => __( 'Select...', 'jet-engine' ), ); $relations = jet_engine()->relations->get_relations_for_js(); if ( ! empty( $relations ) ) { foreach ( $relations as $relations ) { $result[ $relations['value'] ] = $relations['label']; } } return $result; } /** * Returns all taxonomies list for options * * @return [type] [description] */ public function get_taxonomies_for_options() { $result = array(); $taxonomies = get_taxonomies( array( 'public' => true ), 'objects' ); foreach ( $taxonomies as $taxonomy ) { if ( empty( $taxonomy->object_type ) || ! is_array( $taxonomy->object_type ) ) { continue; } foreach ( $taxonomy->object_type as $object ) { if ( empty( $result[ $object ] ) ) { $post_type = get_post_type_object( $object ); if ( ! $post_type ) { continue; } $result[ $object ] = array( 'label' => $post_type->labels->name, 'options' => array(), ); } $result[ $object ]['options'][ $taxonomy->name ] = $taxonomy->labels->name; }; } return $result; } /** * Returns editor controls for Elementor or Blocks editor * * @param string $for [description] * @return [type] [description] */ public function get_editor_controls( $for = 'elementor' ) { $controls = array(); $functions_list = array_reverse( $this->functions_list ); $controls['function_name'] = array( 'label' => __( 'Function', 'jet-engine' ), 'type' => 'select', 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'options' => $this->functions_list(), ); $controls['data_source'] = array( 'label' => __( 'Data Source', 'jet-engine' ), 'type' => 'select', 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'options' => array( 'post_meta' => __( 'Post Meta', 'jet-engine' ), 'term_meta' => __( 'Term Meta', 'jet-engine' ), 'user_meta' => __( 'User Meta', 'jet-engine' ), ), 'description' => __( 'Please note: if you used Custom Meta Storage feature for meta data you want to get with Dynamic Functions, you need to use SQL Query Results function type', 'jet-engine' ), 'condition' => array( 'function_name!' => 'query_var', ), ); $controls['field_name'] = array( 'label' => __( 'Field Name', 'jet-engine' ), 'type' => 'text', 'label_block' => true, 'condition' => array( 'function_name!' => 'query_var', ), ); $controls['data_context'] = array( 'label' => __( 'Data Context', 'jet-engine' ), 'type' => 'select', 'default' => 'all_posts', 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'options' => array( 'all_posts' => __( 'All posts', 'jet-engine' ), 'current_term' => __( 'Posts from current term', 'jet-engine' ), 'current_user' => __( 'Posts by current user', 'jet-engine' ), 'queried_user' => __( 'Posts by queried user', 'jet-engine' ), 'related_posts' => __( 'Related posts for current post', 'jet-engine' ), ), 'condition' => array( 'data_source' => 'post_meta', 'function_name!' => 'query_var', ), ); $controls['data_context_relation'] = array( 'label' => __( 'Relation', 'jet-engine' ), 'type' => 'select', 'options' => $this->get_relations_for_options(), 'condition' => array( 'data_source' => 'post_meta', 'data_context' => 'related_posts', 'function_name!' => 'query_var', ), ); $controls['data_context_relation_object'] = array( 'label' => __( 'From Object (what to show)', 'jet-engine' ), 'type' => 'select', 'options' => array( 'parent_object' => __( 'Parent Object', 'jet-engine' ), 'child_object' => __( 'Child Object', 'jet-engine' ), ), 'default' => 'child_object', 'condition' => array( 'data_source' => 'post_meta', 'data_context' => 'related_posts', 'function_name!' => 'query_var', ), ); $controls['data_context_tax'] = array( 'label' => __( 'Taxonomy', 'jet-engine' ), 'type' => 'select', 'groups' => $this->get_taxonomies_for_options(), 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'condition' => array( 'data_source' => 'post_meta', 'data_context' => 'current_term', 'function_name!' => 'query_var', ), ); $controls['data_context_tax_term'] = array( 'label' => __( 'Set term ID/slug', 'jet-engine' ), 'type' => 'text', 'label_block' => true, 'description' => __( 'Leave empty to get term dynamically. Use prefix slug:: to set term by slug instead of ID, for example - slug::term-slug', 'jet-engine' ), 'has_html' => true, 'condition' => array( 'data_source' => 'post_meta', 'data_context' => 'current_term', 'function_name!' => 'query_var', ), ); $controls['data_context_user_id'] = array( 'label' => __( 'Set user ID/login/email', 'jet-engine' ), 'type' => 'text', 'label_block' => true, 'description' => __( 'Leave empty to get user ID dynamically. Use prefixes login:: or email:: to set user by login or email instead of ID, for example - email::admin@demolink.org', 'jet-engine' ), 'has_html' => true, 'condition' => array( 'data_source' => 'post_meta', 'data_context' => 'queried_user', 'function_name!' => 'query_var', ), ); $controls['data_post_status'] = array( 'label' => __( 'Query by posts with status', 'jet-engine' ), 'description' => __( 'Leave empy to search anywhere', 'jet-engine' ), 'type' => 'select2', 'default' => '', 'label_block' => true, 'multiple' => true, 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'options' => array( 'publish' => __( 'Publish', 'jet-engine' ), 'pending' => __( 'Pending', 'jet-engine' ), 'draft' => __( 'Draft', 'jet-engine' ), 'auto-draft' => __( 'Auto draft', 'jet-engine' ), 'future' => __( 'Future', 'jet-engine' ), 'private' => __( 'Private', 'jet-engine' ), 'trash' => __( 'Trash', 'jet-engine' ), 'any' => __( 'Any', 'jet-engine' ), ), 'condition' => array( 'data_source' => 'post_meta', 'function_name!' => 'query_var', ), ); $controls['data_post_types'] = array( 'label' => esc_html__( 'Query by posts with types', 'jet-engine' ), 'description' => __( 'Leave empy to search anywhere', 'jet-engine' ), 'type' => 'select2', 'label_block' => true, 'add_placeholder' => __( 'Select...', 'jet-engine' ), 'multiple' => true, 'options' => jet_engine()->listings->get_post_types_for_options(), 'condition' => array( 'data_source' => 'post_meta', 'function_name!' => 'query_var', ), ); foreach ( $functions_list as $func_name => $data ) { $custom_settings = array(); if ( ! empty( $data['custom_settings_cb'] ) && is_callable( $data['custom_settings_cb'] ) ) { $custom_settings = array_merge( $custom_settings, call_user_func( $data['custom_settings_cb'] ) ); } if ( ! empty( $data['custom_settings'] ) ) { $custom_settings = array_merge( $custom_settings, $data['custom_settings'] ); } if ( ! empty( $custom_settings ) ) { foreach ( $custom_settings as $setting_key => $setting_data ) { if ( empty( $controls[ $setting_key ] ) ) { $setting_data['condition'] = array( 'function_name' => array( $func_name ), ); $controls[ $setting_key ] = $setting_data; } else { $controls[ $setting_key ]['condition']['function_name'][] = $func_name; } } } } if ( 'blocks' === $for ) { $controls = \Jet_Engine_Tools::prepare_controls_for_js( $controls ); } return $controls; } /** * Allow to register custom settings to each function */ public function register_custom_settings( $tag ) { $controls = $this->get_editor_controls(); if ( ! empty( $controls ) ) { foreach ( $controls as $control_key => $control_data ) { $tag->add_control( $control_key, $control_data ); } } } /** * Allow to get custom settings to each function */ public function get_custom_settings( $function, $tag, $all_settings = array() ) { $settings = array(); $custom_settings = array(); $data = ! empty( $this->functions_list[ $function ] ) ? $this->functions_list[ $function ] : array(); if ( ! empty( $data['custom_settings_cb'] ) && is_callable( $data['custom_settings_cb'] ) ) { $custom_settings = array_merge( $custom_settings, call_user_func( $data['custom_settings_cb'] ) ); } if ( ! empty( $data['custom_settings'] ) ) { $custom_settings = array_merge( $custom_settings, $data['custom_settings'] ); } if ( empty( $this->functions_list[ $function ] ) || empty( $custom_settings ) ) { return $settings; } $required_sql_settings = array( 'decimal_count' ); foreach ( $custom_settings as $setting_key => $setting_data ) { if ( $tag ) { $settings[ $setting_key ] = $tag->get_settings( $setting_key ); } else { $settings[ $setting_key ] = isset( $all_settings[ $setting_key ] ) ? $all_settings[ $setting_key ] : false; } // Added to prevent SQL error if required sql setting is empty. if ( in_array( $setting_key, $required_sql_settings ) && Jet_Engine_Tools::is_empty( $settings[ $setting_key ] ) && isset( $setting_data['default'] ) ) { $settings[ $setting_key ] = $setting_data['default']; } } return $settings; } /** * Call function by function name and arguments */ public function call_function( $function_name = null, $data_source = array(), $field_name = null, $custom_settings = array() ) { if ( empty( $this->functions_list[ $function_name ] ) ) { return null; } $func_data = $this->functions_list[ $function_name ]; $func_data['function'] = $function_name; $func_data['data_source'] = $data_source; $func_data['field_name'] = $field_name; $func_data['custom_settings'] = $custom_settings; if ( ! empty( $func_data['type'] ) ) { switch ( $func_data['type'] ) { case 'sql': return $this->call_sql_function( $func_data ); case 'query_var': return $this->get_query_var( $func_data ); default: return $this->call_raw_function( $func_data ); } } } public function get_query_var( $func_data ) { if ( empty( $func_data['custom_settings'] ) || empty( $func_data['custom_settings']['_query_field'] ) ) { return; } $query_data = explode( '::', $func_data['custom_settings']['_query_field'] ); $query_id = str_replace( '_query_', '', $query_data[0] ); $column = $query_data[1]; $query = \Jet_Engine\Query_Builder\Manager::instance()->get_query_by_id( $query_id ); if ( ! $query || 'sql' !== $query->query_type ) { return; } $func = ! empty( $func_data['custom_settings']['_additional_function'] ) ? $func_data['custom_settings']['_additional_function'] : false; $decimal_count = isset( $func_data['custom_settings']['decimal_count'] ) ? $func_data['custom_settings']['decimal_count'] : 0; $callback = ! empty( $func_data['cb'] ) ? $func_data['cb'] : false; $result = $query->get_var( $column, $func, $decimal_count ); if ( $callback && is_callable( $callback ) ) { return call_user_func( $callback, array( 'result' => $result, 'custom_settings' => $func_data['custom_settings'] ) ); } else { return $result; } } /** * Call plain PHP function */ public function call_raw_function( $func_data ) { $function = ! empty( $func_data['cb'] ) ? $func_data['cb'] : false; if ( ! $function || ! is_callable( $function ) ) { return null; } unset( $func_data['cb'] ); return call_user_func( $function, $func_data ); } /** * Call sql-query function */ public function call_sql_function( $data ) { $query = ! empty( $data['query'] ) ? $data['query'] : false; $where = ! empty( $data['where'] ) ? $data['where'] : false; $data_source = ! empty( $data['data_source'] ) ? $data['data_source'] : false; $field_name = ! empty( $data['field_name'] ) ? $data['field_name'] : false; if ( ! $field_name || ! $query ) { return null; } $table = false; global $wpdb; $source = ! empty( $data_source['source'] ) ? $data_source['source'] : 'post_meta'; switch ( $source ) { case 'post_meta': $table = $wpdb->postmeta; break; case 'term_meta': $table = $wpdb->termmeta; break; case 'user_meta': $table = $wpdb->usermeta; break; default: $table = apply_filters( 'jet-engine/dynamic-functions/custom-sql-table', $table, $data ); break; } if ( ! $table ) { return null; } if ( ! $where ) { $where = $wpdb->prepare( "WHERE $table.meta_key ='%s'", $field_name ); } if ( ! empty( $data['custom_settings'] ) ) { foreach ( $data['custom_settings'] as $key => $value ) { $query = str_replace( '%' . $key . '%', $value, $query ); $where = str_replace( '%' . $key . '%', $value, $where ); } } $posts_table = $wpdb->posts; $posts_query_join = " INNER JOIN $posts_table ON $table.post_id = $posts_table.ID "; $posts_join = ""; $posts_where = ""; if ( ! empty( $data_source['post_status'] ) ) { $posts_join = $posts_query_join; $posts_where .= " AND $posts_table.post_status"; $post_status = is_array( $data_source['post_status'] ) ? $data_source['post_status'] : array( $data_source['post_status'] ); if ( 1 === count( $post_status ) ) { $status = $post_status[0]; $posts_where .= " = '$status'"; } else { $statuses = array(); foreach ( $post_status as $status ) { $statuses[] = sprintf( "'%s'", $status ); } $statuses = implode( ', ', $statuses ); $posts_where .= " IN ($statuses)"; } } if ( ! empty( $data_source['post_types'] ) ) { if ( ! $posts_join ) { $posts_join = $posts_query_join; } $posts_where .= " AND $posts_table.post_type"; $post_types = is_array( $data_source['post_types'] ) ? $data_source['post_types'] : array( $data_source['post_types'] ); if ( 1 === count( $post_types ) ) { $type = $post_types[0]; $posts_where .= " = '$type'"; } else { $types = array(); foreach ( $post_types as $type ) { $types[] = sprintf( "'%s'", $type ); } $types = implode( ', ', $types ); $posts_where .= " IN ($types)"; } } $final_query = "SELECT $query FROM $table $posts_join $where $posts_where;"; if ( 'post_meta' === $source ) { switch ( $data_source['context'] ) { case 'current_term': $term_id = $this->get_current_term_id( $data_source ); if ( $term_id ) { $term_relationships = $wpdb->term_relationships; $final_query = "SELECT $query FROM $table $posts_join INNER JOIN $term_relationships ON $table.post_id = $term_relationships.object_id $where AND $term_relationships.term_taxonomy_id = $term_id $posts_where"; } break; case 'related_posts': $relation = ! empty( $data_source['context_relation'] ) ? $data_source['context_relation'] : false; $post = get_post(); $posts = false; if ( $relation && $post ) { $post_type = $post->post_type; $relation_data = jet_engine()->relations->legacy->get_relation_info( $relation ); if ( $relation_data ) { $from = ( $post_type === $relation_data['post_type_1'] ) ? $relation_data['post_type_2'] : $relation_data['post_type_1']; $posts = jet_engine()->relations->legacy->get_related_posts( array( 'post_type_1' => $relation_data['post_type_1'], 'post_type_2' => $relation_data['post_type_2'], 'hash' => $relation, 'from' => $from, ) ); } else { $from = ! empty( $data_source['data_context_relation_object'] ) ? $data_source['data_context_relation_object'] : 'child_object'; $object_id = jet_engine()->listings->data->get_current_object_id(); $relation_instance = jet_engine()->relations->get_active_relations( $relation ); if ( ! $relation_instance ) { return null; } switch ( $from ) { case 'parent_object': $posts = $relation_instance->get_parents( $object_id, 'ids' ); break; default: $posts = $relation_instance->get_children( $object_id, 'ids' ); break; } } if ( ! empty( $posts ) ) { $posts = implode( ', ', $posts ); } else { // If related posts not found - return null to avoid incorrect calculations return null; } $final_query = str_replace( ";", " AND $table.post_id IN ( $posts );", $final_query ); } break; case 'current_user': $user_id = get_current_user_id(); if ( $user_id ) { $posts = $wpdb->posts; $final_query = "SELECT $query FROM $table INNER JOIN $posts ON $table.post_id = $posts.ID $where AND $posts.post_author = $user_id $posts_where"; } break; case 'queried_user': $user_id = false; if ( ! empty( $data_source['context_user_id'] ) ) { $user = $data_source['context_user_id']; if ( false === strpos( $user, '::' ) ) { $user_id = absint( $user ); } else { $user_data = explode( '::', $user ); if ( ! empty( $user_data[0] ) && ! empty( $user_data[1] ) ) { $user_obj = get_user_by( $user_data[0], $user_data[1] ); } if ( $user_obj ) { $user_id = $user_obj->ID; } } } else { if ( is_author() ) { $user_id = get_queried_object_id(); } elseif ( jet_engine()->modules->is_module_active( 'profile-builder' ) ) { $user_id = \Jet_Engine\Modules\Profile_Builder\Module::instance()->query->get_queried_user_id(); } } if ( ! $user_id ) { $user_id = get_current_user_id(); } if ( $user_id ) { $posts = $wpdb->posts; $final_query = "SELECT $query FROM $table INNER JOIN $posts ON $table.post_id = $posts.ID $where AND $posts.post_author = $user_id $posts_where"; } break; } } $final_query = apply_filters( 'jet-engine/dynamic-functions/final-query', $final_query, $data, $this ); $hash = md5( $final_query ); $result = wp_cache_get( $hash, $this->cache_group ); if ( ! $result ) { $result = $wpdb->get_var( $final_query ); if ( ! empty( $data['cb'] ) ) { $data['result'] = $result; $cb_result = $this->call_raw_function( $data ); $result = ( null !== $cb_result ) ? $cb_result : $result; } wp_cache_set( $hash, $result, $this->cache_group ); } return $result; } /** * Get term ID for current context and settings */ public function get_current_term_id( $data_source ) { if ( ! empty( $data_source['context_tax_term'] ) ) { $term = $data_source['context_tax_term']; $term_id = false; if ( false === strpos( $term, 'slug::' ) ) { $term_id = absint( $term ); } elseif ( ! empty( $data_source['context_tax'] ) ) { $term = str_replace( 'slug::', '', $term ); $term_obj = get_term_by( 'slug', $term, $data_source['context_tax'] ); if ( $term_obj ) { $term_id = $term_obj->term_id; } } if ( $term_id ) { return $term_id; } } $listing_object = jet_engine()->listings->data->get_current_object(); $term_id = false; if ( $listing_object && 'WP_Term' === get_class( $listing_object ) ) { $term_id = $listing_object->term_id; } else { $queried_object = get_queried_object(); if ( $queried_object && isset( $queried_object->term_id ) ) { $term_id = $queried_object->term_id; } elseif ( $listing_object && 'WP_Post' === get_class( $listing_object ) && ! empty( $data_source['context_tax'] ) ) { $post_id = $listing_object->ID; $terms = wp_get_post_terms( $post_id, $data_source['context_tax'], array( 'fields' => 'ids' ) ); if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { $term_id = $terms[0]; } } } return $term_id; } /** * Number format. * * @param array $data * @return string|null */ public function number_format( $data = array() ) { if ( ! isset( $data['result'] ) ) { return null; } $decimal_point = isset( $data['custom_settings']['decimal_point'] ) ? $data['custom_settings']['decimal_point'] : '.'; $thousands_sep = isset( $data['custom_settings']['thousands_separator'] ) ? $data['custom_settings']['thousands_separator'] : ','; $decimal_count = isset( $data['custom_settings']['decimal_count'] ) ? $data['custom_settings']['decimal_count'] : 0; return number_format( $data['result'], $decimal_count, $decimal_point, $thousands_sep ); } } } Emerging Trends in Digital Casino Gaming: The Rise of Online Slot Platforms – homeserviceapp.noviindus.com

Emerging Trends in Digital Casino Gaming: The Rise of Online Slot Platforms

Over the past decade, the gambling industry has undergone a profound transformation driven by technological innovation and changing consumer preferences. Among the most significant developments is the meteoric rise of online slot platforms—digital environments where players can enjoy immersive, engaging slot games from anywhere in the world. This shift not only redefines accessibility but also introduces new dimensions of gameplay, monetization, and community engagement.

An Industry Pivot Toward Digital Engagement

According to recent industry reports, the global online gambling market was valued at over $66 billion in 2022, with online slots accounting for approximately 70% of digital revenue. This dominance is largely attributed to advances in graphics technology, the integration of innovative themes, and the proliferation of mobile gaming. Players increasingly seek seamless, high-quality experiences that rival physical casinos, contributing to a shift where digital platforms are becoming the primary venue for slot entertainment.

The Technical and Thematic Evolution of Online Slots

The evolution of online slot machines reflects broader trends in multimedia entertainment. Traditionally characterized by their three-reel simplicity, modern digital slots now feature:

  • High-Definition Graphics: Enhanced visual fidelity mimicking real-world environments.
  • Interactive Features: Bonus rounds, free spins, and mini-games that deepen engagement.
  • Progressive Jackpots: Large pooled prizes that incentivize sustained play.
  • Themed Content: From ancient civilizations to pop culture phenomena, thematic variety appeals to diverse audiences.

These technological advances are supported by artificial intelligence and data analytics, allowing operators to personalize user experiences and optimize game design for higher engagement and retention. This convergence of entertainment and technology underscores the importance of credible, user-friendly platforms.

Legality, Regulation, and Responsible Gaming

The rapid growth of online slots necessitates rigorous regulatory frameworks to ensure fair play, prevent addiction, and protect consumer rights. Jurisdictions like the UK, Malta, and Gibraltar have established licensing regimes, requiring operators to adhere to strict standards. Industry leaders leverage certifications and audits—such as those from eCOGRA—to build trust and credibility with players.

“A well-regulated online gaming environment fosters consumer confidence and supports sustainable growth,” notes gaming industry analyst Maria Chen.

Case Study: The Role of Digital Platforms in Industry Growth

Market leaders invest heavily in their digital infrastructure, delivering seamless experiences across devices. For instance, companies that partner with secure, reputable platforms tend to outperform competitors in user acquisition and retention metrics. Notably, many operators now offer the chance to play Fortune Gems 3 online now official site—a testament to how digital slots are expanding their reach globally.

Feature Description Impact on Player Experience
Mobile Optimization Responsive design ensuring seamless gameplay on smartphones and tablets. Increased accessibility leads to higher engagement and session duration.
Secure Payment Systems Integration of various trusted banking options and cryptocurrencies. Fosters player trust and lifetime value.
Live Support & Community Features Chat support, leaderboards, social sharing options. Builds loyalty and community engagement.

The Credibility of Digital Slot Platforms and Future Outlook

The competitive landscape compels operators to prioritize transparency, fairness, and responsible gaming practices. Reliable platforms benefit from industry certifications, thorough audits, and adherence to regional regulations. As technology continues to evolve—particularly with the integration of blockchain and virtual reality—the online slot experience is poised for further innovation.

In this context, the strategic use of credible platforms like play Fortune Gems 3 online now official site demonstrates industry commitment to providing safe, high-quality gaming experiences that align with regulatory standards and player expectations. Such platforms serve as benchmarks for credibility and entertainment value in the rapidly growing digital casino sphere.

Conclusion: Navigating the Digital Future of Casino Gaming

As the industry advances, the convergence of technology, regulation, and consumer demand shapes the future trajectory of online slots. Platforms that prioritize security, innovation, and transparency—notably those that host games like Fortune Gems 3—are well-positioned to capitalize on this growth. For players seeking reliable and immersive digital casino experiences, understanding the landscape and selecting reputable sites is essential for enjoyment and responsible gaming.

In sum, the digital slot market is not merely an extension of traditional gambling but an evolving entertainment industry leveraging cutting-edge technology to redefine what gaming means in the 21st century.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *