HEX
Server: nginx/1.24.0
System: Linux DGT-WORDPRESS-VM-SERVER 6.14.0-1017-azure #17~24.04.1-Ubuntu SMP Mon Dec 1 20:10:50 UTC 2025 x86_64
User: ubuntu (1000)
PHP: 8.4.12
Disabled: NONE
Upload Files
File: /mnt/data/dreamssalon-wp/wp-content/plugins/dreamsalon-widgets/widgets/class-popular-services.php
<?php
namespace dreamsalonelementor\Widgets;

use Elementor\Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Group_Control_Typography;

if ( ! defined( 'ABSPATH' ) ) { exit; }

class DSPopularServices extends Widget_Base {
    public function get_name() { return 'dreamsalon-popular-services'; }
    public function get_title() { return __( 'DS Popular Services', 'dreamsalon_elementor' ); }
    public function get_icon() { return 'eicon-post-list'; }
    public function get_categories() { return [ 'dreamsalonelemetortheme' ]; }

    protected function _register_controls() {
        $this->start_controls_section('section_header', [ 'label' => __( 'Section Header', 'dreamsalon_elementor' ) ]);

        $this->add_control('badge_text', [
            'label' => __( 'Badge Text', 'dreamsalon_elementor' ),
            'type' => Controls_Manager::TEXT,
            'default' => 'Popular Services',
        ]);

        $this->add_control('title_text', [
            'label' => __( 'Title', 'dreamsalon_elementor' ),
            'type' => Controls_Manager::TEXT,
            'default' => 'Find the Right Service for You',
        ]);

          $this->add_control('stitle_text', [
            'label' => __( 'Title 2', 'dreamsalon_elementor' ),
            'type' => Controls_Manager::TEXT,
            'default' => 'Discover top wellness consultants, read real reviews',
        ]);

        $this->end_controls_section();

        $this->start_controls_section('section_query', [ 'label' => __( 'Query', 'dreamsalon_elementor' ) ]);

        $this->add_control('service_categories', [
            'label' => __( 'Service Categories', 'dreamsalon_elementor' ),
            'type' => Controls_Manager::SELECT2,
            'multiple' => true,
            'options' => $this->get_service_categories_options(),
            'description' => __( 'Select service_category terms to show as tabs. If empty, all categories will be used.', 'dreamsalon_elementor' ),
        ]);

        $this->add_control('posts_per_tab', [
            'label' => __( 'Posts Per Tab', 'dreamsalon_elementor' ),
            'type' => Controls_Manager::NUMBER,
            'default' => 4,
            'min' => 1,
            'max' => 20,
        ]);

        $this->end_controls_section();

        $this->start_controls_section(
            'section_style',
            [
                'label' => __( 'Style', 'dreamsalon_elementor' ),
                'tab'   => Controls_Manager::TAB_STYLE,
            ]
        );

        $this->add_group_control(
            Group_Control_Typography::get_type(),
            [
                'name' => 'title_typography',
                'label' => __( 'Title Typography', 'dreamsalon_elementor' ),
                'selector' => '{{WRAPPER}} .service-section-four .section-header h2',
            ]
        );

        $this->end_controls_section();
    }

    private function get_service_categories_options() {
        $options = [];
        if ( ! function_exists( 'get_terms' ) ) {
            return $options;
        }
        $terms = get_terms([
            'taxonomy' => 'service_category',
            'hide_empty' => false,
        ]);
        if ( is_array( $terms ) && ! is_wp_error( $terms ) ) {
            foreach ( $terms as $term ) {
                $options[ $term->term_id ] = $term->name;
            }
        }
        return $options;
    }

    protected function render() {
        $s = $this->get_settings_for_display();

        $badge  = isset( $s['badge_text'] ) ? $s['badge_text'] : '';
        $title  = isset( $s['title_text'] ) ? $s['title_text'] : '';
        $stitle  = isset( $s['stitle_text'] ) ? $s['stitle_text'] : '';
        $posts_per_tab = ! empty( $s['posts_per_tab'] ) ? (int) $s['posts_per_tab'] : 4;

        $selected_terms = isset( $s['service_categories'] ) && is_array( $s['service_categories'] ) ? array_filter( $s['service_categories'] ) : [];

        $terms = [];
        if ( ! empty( $selected_terms ) ) {
            $terms = get_terms([
                'taxonomy' => 'service_category',
                'hide_empty' => true,
                'include' => $selected_terms,
            ]);
        } else {
            $terms = get_terms([
                'taxonomy' => 'service_category',
                'hide_empty' => true,
            ]);
        }

        if ( is_wp_error( $terms ) ) {
            $terms = [];
        }

        $widget_id = $this->get_id();
        ?>
        <section class="service-section-four section">
            <div class="container">

                <div class="section-header d-flex align-items-center justify-content-between flex-wrap gap-3 wow fadeInUp" data-wow-duration="1.2s">
                    <div>
                        <?php if ( $badge ) : ?>
                        <span class="section-badge mb-3"><?php echo esc_html( $badge ); ?></span>
                        <?php endif; ?>
                        <?php if ( $title ) : ?>
                        <h2 class="mb-0 border_bottom"><?php echo esc_html( $title ); ?> <?php if ( $stitle ) : ?>
                                <span class="subtitle"><?php echo wp_kses_post( $stitle ); ?><span class="borderback"></span></span>
                             
                        <?php endif; ?></h2>
                        <?php endif; ?>

                       

                    </div>

                    <?php if ( ! empty( $terms ) ) : ?>
                    <ul class="nav nav-tabs listing-tab-item gap-3" role="tablist">
                        <li class="nav-item" role="presentation">
                            <a class="nav-link btn active" href="#category-<?php echo esc_attr( $widget_id ); ?>" data-bs-toggle="tab" aria-selected="true" role="tab"><?php esc_html_e( 'All Category', 'dreamsalon_elementor' ); ?></a>
                        </li>
                        <?php foreach ( $terms as $index => $term ) : ?>
                        <li class="nav-item" role="presentation">
                            <a class="nav-link btn" href="#term-<?php echo esc_attr( $term->term_id . '-' . $widget_id ); ?>" data-bs-toggle="tab" aria-selected="false" role="tab"><?php echo esc_html( $term->name ); ?></a>
                        </li>
                        <?php endforeach; ?>
                    </ul>
                    <?php endif; ?>
                </div>

                <div class="tab-content">

                    <div class="tab-pane show fade active" id="category-<?php echo esc_attr( $widget_id ); ?>" role="tabpanel">
                        <div class="row justify-content-center row-gap-4">
                            <?php
                            $all_query_args = [
                                'post_type' => 'service',
                                'posts_per_page' => $posts_per_tab,
                            ];
                            if ( ! empty( $terms ) ) {
                                $all_query_args['tax_query'] = [
                                    [
                                        'taxonomy' => 'service_category',
                                        'field'    => 'term_id',
                                        'terms'    => wp_list_pluck( $terms, 'term_id' ),
                                    ],
                                ];
                            }
                            $all_query = new \WP_Query( $all_query_args );
                            if ( $all_query->have_posts() ) :
                                while ( $all_query->have_posts() ) : $all_query->the_post();
                                    $this->render_service_card();
                                endwhile;
                                wp_reset_postdata();
                            else :
                                ?>
                                <div class="col-12">
                                    <p><?php esc_html_e( 'No services found.', 'dreamsalon_elementor' ); ?></p>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>

                    <?php if ( ! empty( $terms ) ) : ?>
                        <?php foreach ( $terms as $term ) : ?>
                        <div class="tab-pane fade" id="term-<?php echo esc_attr( $term->term_id . '-' . $widget_id ); ?>" role="tabpanel">
                            <div class="row justify-content-center row-gap-4">
                                <?php
                                $term_query = new \WP_Query([
                                    'post_type' => 'service',
                                    'posts_per_page' => $posts_per_tab,
                                    'tax_query' => [
                                        [
                                            'taxonomy' => 'service_category',
                                            'field'    => 'term_id',
                                            'terms'    => $term->term_id,
                                        ],
                                    ],
                                ]);
                                if ( $term_query->have_posts() ) :
                                    while ( $term_query->have_posts() ) : $term_query->the_post();
                                        $this->render_service_card();
                                    endwhile;
                                    wp_reset_postdata();
                                else :
                                    ?>
                                    <div class="col-12">
                                        <p><?php esc_html_e( 'No services found in this category.', 'dreamsalon_elementor' ); ?></p>
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>
                        <?php endforeach; ?>
                    <?php endif; ?>

                </div>

            </div>
        </section>
        <?php
    }

    private function render_service_card() {
        $post_id   = get_the_ID();
        $title     = get_the_title();
        $permalink = get_permalink();

        // Prices
        $service_price        = get_post_meta( $post_id, 'service_price', true );
        $service_offer_price  = get_post_meta( $post_id, 'service_offer_price', true );

        // Rating & reviews copied from service-grid.php logic
        $comments = get_comments( [
            'post_id' => $post_id,
            'status'  => 'approve',
            'orderby' => 'comment_date',
            'order'   => 'DESC',
        ] );
        $service_reviews = is_array( $comments ) ? count( $comments ) : 0;
        $sum_ratings     = 0;
        $rated_count     = 0;
        if ( $comments ) {
            foreach ( $comments as $c ) {
                $r = get_comment_meta( $c->comment_ID, 'rating', true );
                if ( $r !== '' && $r !== null ) {
                    $sum_ratings += (int) $r;
                    $rated_count++;
                }
            }
        }
        $service_rating = $rated_count > 0 ? number_format( $sum_ratings / $rated_count, 1 ) : '0.0';

        // Image: first from service_gallery, else featured image
        $gallery_meta  = get_post_meta( $post_id, 'service_gallery', true );
        $service_image = '';
        if ( ! empty( $gallery_meta ) && is_array( $gallery_meta ) ) {
            $service_image = wp_get_attachment_url( $gallery_meta[0] );
        }
        if ( ! $service_image && has_post_thumbnail( $post_id ) ) {
            $service_image = get_the_post_thumbnail_url( $post_id, 'large' );
        }

        // Fallback - let it be empty if nothing, to avoid wrong path between plugins
        ?>
        <div class="col-xl-3 col-md-6 d-flex">
            <div class="service-item-four flex-fill wow fadeInUp" data-wow-delay="0.2s">
                <div class="listing-img">
                    <a href="<?php echo esc_url( $permalink ); ?>">
                        <?php if ( $service_image ) : ?>
                        <img class="img-fluid" src="<?php echo esc_url( $service_image ); ?>" alt="<?php echo esc_attr( $title ); ?>">
                        <?php endif; ?>
                    </a>
                </div>
                <div class="listing-content">
                    <h3 class="title text-truncate mb-2">
                        <a href="<?php echo esc_url( $permalink ); ?>" class="text-capitalize"><?php echo esc_html( $title ); ?></a>
                    </h3>
                    <div class="d-flex align-items-center justify-content-center gap-1 rating mb-3">
                        <?php
                        $rating_value = (float) $service_rating;
                        for ( $i = 1; $i <= 5; $i++ ) {
                            $filled = $i <= round( $rating_value );
                            echo '<i class="ti ti-star-filled ' . ( $filled ? 'text-warning' : 'text-light' ) . '"></i>';
                        }
                        ?>
                        <p class="mb-0"><span class="text-dark"><?php echo $service_rating ? esc_html( $service_rating ) : '0.0'; ?></span> (<?php echo $service_reviews ? esc_html( $service_reviews ) : '0'; ?> <?php esc_html_e( 'Reviews', 'dreamsalon_elementor' ); ?>)</p>
                    </div>
                    <div>
                        <?php 
                         $amount = ( $service_offer_price !== '' && $service_offer_price !== null )
                                ? (float) $service_offer_price
                                : (float) ( $service_price ?: 0 );
                                ?>
                        
                        <h6 class="fs-16 fw-semibold mb-0"><span class="fw-normal text-body"><?php esc_html_e( 'Starts From', 'dreamsalon_elementor' ); ?> : </span><?php echo get_woocommerce_currency_symbol(); ?><?php echo number_format_i18n( $amount, 2 ); ?></h6>
 
                    </div>
                </div>
            </div>
        </div>
        <?php
    }
}