d'] ?? '', 'mc_price_country_code' => $price_competitiveness['country_code'] ?? '', 'mc_product_currency_code' => $price_competitiveness['benchmark_price_currency_code'] ?? '', 'mc_product_price_micros' => $price_competitiveness['price_micros'] ?? '', 'mc_price_benchmark_price_micros' => $price_competitiveness['benchmark_price_micros'] ?? '', 'mc_price_benchmark_price_currency_code' => $price_competitiveness['benchmark_price_currency_code'] ?? '', 'mc_insights_suggested_price_micros' => $price_insights['suggested_price_micros'] ?? '', 'mc_insights_suggested_price_currency_code' => $price_insights['suggested_price_currency_code'] ?? '', 'mc_insights_predicted_impressions_change_fraction' => $price_insights['predicted_impressions_change_fraction'] ?? 0, 'mc_insights_predicted_clicks_change_fraction' => $price_insights['predicted_clicks_change_fraction'] ?? 0, 'mc_insights_predicted_conversions_change_fraction' => $price_insights['predicted_conversions_change_fraction'] ?? 0, 'mc_insights_effectiveness' => isset( $price_insights['effectiveness'] ) ? $this->get_effectiveness( $price_insights['effectiveness'] ) : 0, 'mc_metrics_clicks' => $performance['clicks'] ?? 0, 'mc_metrics_impressions' => $performance['impressions'] ?? 0, 'mc_metrics_ctr' => $performance['ctr'] ?? 0, 'mc_metrics_conversions' => $performance['conversions'] ?? 0, 'price_compared_with_benchmark' => $this->price_compared_with_benchmark( (int) $price_competitiveness['price_micros'], (int) $price_competitiveness['benchmark_price_micros'] ), ]; }, $mapped_data ); return $response_data; } /** * Retrieves the product thumbnail URL. * * @param int $product_id WooCommerce product ID. * @return string Product thumbnail URL or an empty string if not found. */ protected function get_product_thumbnail( int $product_id ): ?string { $thumbnail_id = get_post_thumbnail_id( $product_id ); if ( ! $thumbnail_id ) { return ''; } $thumbnail_url = wp_get_attachment_url( $thumbnail_id ); return $thumbnail_url ?? ''; } /** * Returns the key for the given effectiveness value. * * @param string $value Effectiveness value. * @return int The corresponding key if value is found, otherwise 0. */ public function get_effectiveness( $value ) { $effectiveness_map = [ 'EFFECTIVENESS_UNSPECIFIED' => 0, 'LOW' => 1, 'MEDIUM' => 2, 'HIGH' => 3, ]; return $effectiveness_map[ $value ] ?? 0; } /** * Update price benchmarks by querying the Google Content API and saving the data locally. */ public function update_price_benchmarks(): void { try { $benchmarks = $this->get_price_benchmarks_response( [] ); if ( empty( $benchmarks ) ) { return; } /** @var MerchantPriceBenchmarksQuery $query */ $query = $this->container->get( MerchantPriceBenchmarksQuery::class ); // Clear existing data before updating. $query->reload_data(); // Insert new benchmark data. foreach ( $benchmarks as $benchmark_item ) { $query->insert( $benchmark_item ); } } catch ( \Exception $e ) { do_action( 'woocommerce_gla_debug_message', $e->getMessage(), __METHOD__ ); } } /** * Compares a given price with a benchmark price. * * This function takes two prices in micros (1,000,000 micros = 1 unit of currency) * and performs a comparison to determine their relationship. * * @param int $price_micros The price to compare, in micros. * @param int $benchmark_price_micros The benchmark price to compare against, in micros. * @return bool Returns specific price compare group if the price meets the comparison criteria with the benchmark. */ private function price_compared_with_benchmark( $price_micros, $benchmark_price_micros ) { if ( empty( $price_micros ) || empty( $benchmark_price_micros ) ) { return 0; } elseif ( abs( $price_micros - $benchmark_price_micros ) <= ( $benchmark_price_micros * 0.01 ) ) { return 2; } elseif ( $price_micros < $benchmark_price_micros ) { return 1; } elseif ( $price_micros > $benchmark_price_micros ) { return 3; } } /** * Get a summary of price benchmarks. * * @return array */ public function get_summary(): array { /** @var MerchantPriceBenchmarksQuery $query */ $query = $this->container->get( MerchantPriceBenchmarksQuery::class ); // Get counts for all price comparison groups in one query. $benchmark_counts_result = $query->get_price_benchmark_counts(); // Convert raw DB results to an associative array with all groups. $benchmark_counts = $this->get_price_benchmark_counts_data( $benchmark_counts_result ); return [ 'total_products' => $benchmark_counts['total'] ?? 0, // Total products 'price_unknown' => $benchmark_counts[0] ?? 0, // Unknown/missing 'price_lower' => $benchmark_counts[1] ?? 0, // Lower price 'price_similar' => $benchmark_counts[2] ?? 0, // Similar price 'price_higher' => $benchmark_counts[3] ?? 0, // Higher price ]; } /** * Converts raw benchmark counts from the database to an associative array. * * @param array $rows Raw benchmark counts result from the database. * @return array Associative array with counts for each price comparison group and total. */ public function get_price_benchmark_counts_data( array $rows ): array { // Convert the results to a more usable format $counts = []; $total = 0; foreach ( $rows as $row ) { $price_compared_value = (int) $row['price_compared_with_benchmark']; $counts[ $price_compared_value ] = (int) $row['count']; $total += $counts[ $price_compared_value ]; } // Make sure all possible values are represented (0, 1, 2, 3) $all_values = [ 0, 1, 2, 3 ]; foreach ( $all_values as $value ) { if ( ! isset( $counts[ $value ] ) ) { $counts[ $value ] = 0; } } $counts['total'] = $total; return $counts; } /** * Retrieves formatted price benchmarks data from the local database. * * @param array $args { * Optional. Arguments to filter and paginate results. * * @type array|null $include List of product IDs to include. Default null. * @type int $page Offset for the results. Default 1. * @type int $per_page Maximum number of items returned. Default 10. * @type string $search Search string to filter results. Default null. * @type string $order Sort order: 'asc' or 'desc'. Default 'desc'. * @type string $orderby Attribute to sort by. Default 'mc_insights_effectiveness'. * } * @return array { * @type array $results List of formatted price benchmarks. * @type int $total Total number of price benchmarks available. * } */ public function get_price_benchmarks_data( array $args = [] ): array { $defaults = [ 'include' => null, 'page' => 1, 'per_page' => 10, 'search' => null, 'order' => 'desc', 'orderby' => self::DEFAULT_ORDERBY, ]; $args = wp_parse_args( array_intersect_key( $args, $defaults ), $defaults ); /** @var MerchantPriceBenchmarksQuery $query */ $query = $this->container->get( MerchantPriceBenchmarksQuery::class ); // Apply filters. if ( ! empty( $args['include'] ) && is_array( $args['include'] ) ) { $query->where( 'product_id', $args['include'], 'IN' ); } if ( ! empty( $args['search'] ) ) { $search = trim( $args['search'] ); $search_query = new \WP_Query( [ 'post_type' => [ 'product' ], 'post_status' => 'publish', 's' => $search, 'fields' => 'ids', 'posts_per_page' => -1, ] ); $product_ids = $search_query->posts; // If no products found, return empty results. if ( empty( $product_ids ) ) { return [ 'results' => [], 'total' => 0, ]; } $query->where( 'product_id', $product_ids, 'IN' ); } // Set order and orderby. $order = strtoupper( $args['order'] ); if ( ! in_array( $order, [ 'ASC', 'DESC' ], true ) ) { $order = 'DESC'; } $orderby = $this->map_orderby_to_db_value( $args['orderby'] ); $query->set_order( $orderby, $order ); // Set offset and limit. $query->set_offset( (int) ( $args['page'] - 1 ) * $args['per_page'] ); $query->set_limit( (int) $args['per_page'] ); // Get total count before limiting. $total = $query->get_count(); // Get results. $rows = $query->get_results(); // Format results. $results = []; foreach ( $rows as $row ) { $wc_product_id = (int) $row['product_id']; $product = wc_get_product( $wc_product_id ); $thumbnail = $this->get_product_thumbnail( $wc_product_id ); $results[] = [ 'product' => [ 'id' => $wc_product_id, 'thumbnail' => $thumbnail, 'title' => $product instanceof \WC_Product ? $product->get_name() : '', ], 'effectiveness' => (int) $row['mc_insights_effectiveness'] ?? 0, 'country_code' => $row['mc_price_country_code'] ?? '', 'currency_code' => $row['mc_product_currency_code'] ?? '', 'product_price' => $this->micros_to_float( (int) $row['mc_product_price_micros'] ), 'benchmark_price' => $this->micros_to_float( (int) $row['mc_price_benchmark_price_micros'] ), 'benchmark_price_currency_code' => $row['mc_price_benchmark_price_currency_code'] ?? '', 'price_gap' => $this->micros_to_float( (int) $row['mc_price_benchmark_price_micros'] - (int) $row['mc_product_price_micros'] ), 'suggested_price' => $this->micros_to_float( (int) $row['mc_insights_suggested_price_micros'] ), 'suggested_price_currency_code' => $row['mc_insights_suggested_price_currency_code'] ?? '', 'predicted_impressions_change' => $row['mc_insights_predicted_impressions_change_fraction'] ?? '', 'predicted_clicks_change' => $row['mc_insights_predicted_clicks_change_fraction'] ?? '', 'predicted_conversions_change' => $row['mc_insights_predicted_conversions_change_fraction'] ?? '', 'clicks' => (int) $row['mc_metrics_clicks'] ?? 0, 'impressions' => (int) $row['mc_metrics_impressions'] ?? 0, 'ctr' => $row['mc_metrics_ctr'] ?? 0, 'conversions' => (int) $row['mc_metrics_conversions'] ?? 0, 'price_compared_with_benchmark' => (int) $row['price_compared_with_benchmark'] ?? 0, ]; } return [ 'results' => $results, 'total' => $total, ]; } /** * Maps the orderby parameter to the corresponding database column name. * * @param string $order_by The orderby parameter from the request. * @return string The corresponding database column name. */ private function map_orderby_to_db_value( string $order_by ): string { // If the $order_by value is not in the COLUMN_MAP, use the default. if ( ! in_array( $order_by, array_keys( self::COLUMN_MAP ), true ) ) { $order_by = self::DEFAULT_ORDERBY; } return self::COLUMN_MAP[ $order_by ]; } /** * Converts a value in micros to a float representation. * * @param int $micros The value in micros (1,000,000 micros = 1 unit of currency). * @return float The converted float value. */ private function micros_to_float( int $micros ): float { // Convert micros to a float value. return round( $micros / 1000000, 2 ); } }
Warning: class_implements(): Class Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\PriceBenchmarks does not exist and could not be loaded in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 73

Warning: foreach() argument must be of type array|object, false given in /htdocs/wp-content/plugins/google-listings-and-ads/src/Internal/DependencyManagement/AbstractServiceProvider.php on line 73

Warning: Cannot modify header information - headers already sent by (output started at /htdocs/wp-content/plugins/google-listings-and-ads/src/MerchantCenter/PriceBenchmarks.php:1) in /htdocs/wp-content/plugins/the-events-calendar/src/Tribe/Views/V2/iCalendar/iCalendar_Handler.php on line 257

Warning: Cannot modify header information - headers already sent by (output started at /htdocs/wp-content/plugins/google-listings-and-ads/src/MerchantCenter/PriceBenchmarks.php:1) in /htdocs/wp-content/plugins/the-events-calendar/src/Tribe/iCal.php on line 511

Warning: Cannot modify header information - headers already sent by (output started at /htdocs/wp-content/plugins/google-listings-and-ads/src/MerchantCenter/PriceBenchmarks.php:1) in /htdocs/wp-content/plugins/the-events-calendar/src/Tribe/iCal.php on line 512

Warning: Cannot modify header information - headers already sent by (output started at /htdocs/wp-content/plugins/google-listings-and-ads/src/MerchantCenter/PriceBenchmarks.php:1) in /htdocs/wp-content/plugins/the-events-calendar/src/Tribe/iCal.php on line 513
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//The CandleLIT Experience - ECPv6.15.20//NONSGML v1.0//EN CALSCALE:GREGORIAN METHOD:PUBLISH X-WR-CALNAME:The CandleLIT Experience X-ORIGINAL-URL:https://candlelitexperience.com X-WR-CALDESC:Events for The CandleLIT Experience REFRESH-INTERVAL;VALUE=DURATION:PT1H X-Robots-Tag:noindex X-PUBLISHED-TTL:PT1H BEGIN:VTIMEZONE TZID:Europe/Paris BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:20230326T010000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:20231029T010000 END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:20240331T010000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:20241027T010000 END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:20250330T010000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:20251026T010000 END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:20260329T010000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:20261025T010000 END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART;TZID=Europe/Paris:20251030T183000 DTEND;TZID=Europe/Paris:20251030T213000 DTSTAMP:20260413T083623 CREATED:20251005T191015Z LAST-MODIFIED:20251117T230554Z UID:9437-1761849000-1761859800@candlelitexperience.com SUMMARY:Petals & Flames DESCRIPTION:Join us for an intimate\, hands-on evening of creativity\, connection\, and cozy fall vibes. Petals & Flames brings together two timeless elements — flowers and candlelight — for a workshop designed to spark inspiration and community. \nHosted at COSIGN Studios\, this workshop is perfect for women and men who enjoy creative experiences\, networking\, and supporting local businesses. \nYour Ticket Includes: \nPetite Wrapped Bouquet – Craft your own hand-tied bouquet (10–12 stems) featuring seasonal blooms and greenery\, guided by Fleurish Bouquets & Occasions. \nCustom 8oz Candle – Pour and personalize your own fall-inspired candle with Veronica\, owner of The Candle Lit Experience. \nComplimentary Food & Drinks – Enjoy delicious light bites and beverages from local DFW restaurants. \nCommunity & Networking – Connect with like-minded creatives\, entrepreneurs\, and community members in a cozy\, welcoming setting. \nPhoto-Ready Moments – Capture your experience in a beautiful space designed for connection and creativity. URL:https://candlelitexperience.com/event/petals-flames/ LOCATION:Cosign Studio\, 126 glass st\, suite 110\, Dallas\, 75207\, United States ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2025/09/IMG_1221.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20250913 DTEND;VALUE=DATE:20250914 DTSTAMP:20260413T083623 CREATED:20251005T195956Z LAST-MODIFIED:20251005T200758Z UID:9453-1757721600-1757807999@candlelitexperience.com SUMMARY:Workshop at Nkula DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/workshop-at-nkula/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/06/https___cdn.evbuc_.com_images_1102749373_567118670045_1_original.jpeg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20250719 DTEND;VALUE=DATE:20250720 DTSTAMP:20260413T083623 CREATED:20251005T195759Z LAST-MODIFIED:20251005T200758Z UID:9456-1752883200-1752969599@candlelitexperience.com SUMMARY:Pressed and Poured DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/pressed-and-poured/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9607.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20250522 DTEND;VALUE=DATE:20250523 DTSTAMP:20260413T083623 CREATED:20251005T195130Z LAST-MODIFIED:20251005T200758Z UID:9448-1747872000-1747958399@candlelitexperience.com SUMMARY:Lumiere and Luxe DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/lumiere-and-luxe/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9788.jpg END:VEVENT BEGIN:VEVENT DTSTART;TZID=Europe/Paris:20250417T080000 DTEND;TZID=Europe/Paris:20250417T170000 DTSTAMP:20260413T083623 CREATED:20251005T195720Z LAST-MODIFIED:20251005T200758Z UID:9457-1744876800-1744909200@candlelitexperience.com SUMMARY:Frost and Flame DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/frost-and-flame/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9609.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20250210 DTEND;VALUE=DATE:20250211 DTSTAMP:20260413T083623 CREATED:20251005T195630Z LAST-MODIFIED:20251005T200758Z UID:9458-1739145600-1739231999@candlelitexperience.com SUMMARY:Cocktails and candles DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/cocktails-and-candles/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9679.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20250126 DTEND;VALUE=DATE:20250127 DTSTAMP:20260413T083623 CREATED:20251005T195920Z LAST-MODIFIED:20251005T200758Z UID:9454-1737849600-1737935999@candlelitexperience.com SUMMARY:Coffee & Candles DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/coffee-candles/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9610.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20241121 DTEND;VALUE=DATE:20241122 DTSTAMP:20260413T083623 CREATED:20251005T195846Z LAST-MODIFIED:20251005T200758Z UID:9455-1732147200-1732233599@candlelitexperience.com SUMMARY:Frans “giving” Dinner DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/frans-giving-dinner/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9611.jpg END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20241110 DTEND;VALUE=DATE:20241111 DTSTAMP:20260413T083623 CREATED:20251005T195246Z LAST-MODIFIED:20251005T200759Z UID:9450-1731196800-1731283199@candlelitexperience.com SUMMARY:Brewed and Burned DESCRIPTION:Our handcrafted candles bring a gentle glow and a captivating scent that creates the perfect cozy atmosphere. Ideal for quiet nights in or intimate gatherings\, each candle is a little touch of comfort for your everyday moments. URL:https://candlelitexperience.com/event/brewed-and-burned/ CATEGORIES:Workshops ATTACH;FMTTYPE=image/jpeg:https://candlelitexperience.com/wp-content/uploads/2023/07/IMG_9676.jpg END:VEVENT END:VCALENDAR