This website uses cookies to personalize your experience. By using this website you agree to our cookie policy.

Creating a results page (not live filtering), sorted

Home Forums Product Support Forums Ajax Search Pro for WordPress Support Creating a results page (not live filtering), sorted

Viewing 15 posts - 1 through 15 (of 16 total)
  • Author
    Posts
  • #55109
    info_piT4info_piT4
    Participant

    Good afternoon,
    I am very glad I found this plugin! I am trying to figure out the following on my staging:
    someone types in a keyword in the search and presses enter.

    What I am trying to create is a nicely sorted results page. Like with found pages on top, product categories below that, and products underneath that.
    Both product categories and Pages should only be links, but the products should have images prices etc.
    I don’t know if I need to create grouped results (I already have), and is there somehow a way to present those in different layouts (2 groups with links and one with a product layout)?

    I tried the loop grid video, but I found out it’s for live filtering on the page only? Or am I wrong?

    I also tried just putting an product archive and searched product results widget on an elementor page, but then the first 2 pages are empty (because they are not products but pages and product categories).

    Do you understand my problem? And can you help me?
    Example of an empty page because I chose the wrong format
    https://prnt.sc/P6HRw3wgwFFF

    Kind regards,
    Nancy

    #55115
    Ernest MarcinkoErnest Marcinko
    Keymaster

    Hi Nancy,

    Thank you very much for your kind words!

    The results page layout is outside of the scope of the plugin, unfortunately search plugins can’t control that – it’s controlled by the theme or the page builder you are using.

    Most modern themes have options to customize the results page, or if you are using a page builder then that should do it too.

    However please note that WordPress generally is very limited to what it’s capable of displaying on the search results pages. Unless you are really good at custom coding, I would suggest against trying to make groups of results on the results page, in the past 15 years I have only seen 2 cases where they made it work and it was extremely difficult and involved a lot of coding.

    #55178
    info_piT4info_piT4
    Participant

    Thank you for your answer. I’m quite stubborn so I’ll try to make it work. Do you have working examples/sites of those who do made it work?

    Thanks in advance for your time .

    #55183
    Ernest MarcinkoErnest Marcinko
    Keymaster

    I looked through the tickets but I couldn’t find either of them, I don’t recall their site names unfortunately. They didn’t share their code with me though, so it wouldn’t help either way.

    If I were to solve this I would approach it like this:
    – Loop through the main archive query and create an array of the groups based on the defined criteria
    – Each group should contain the IDs of the posts/products for every group
    – Generate sub-queries for each group
    – If using a page builder, then name the queries and hook into them to override the IDs from the generated groups

    Then figure out a way to perhaps use pagination, but that has to be also custom coded as the original paniation will not work correctly because of the subqueries and the divided result counts.

    #55194
    info_piT4info_piT4
    Participant

    Well, made a first attempt, with the help of your lovely knowledgebase and ai question input field. And a bit of my AI assistant to clean it up and help me.

    I made a search.php

    <?php
    get_header();
    
    global $asp_cats, $asp_products, $asp_pages, $asp_other;
    
    $search_query = get_search_query();
    $p_asid       = isset($_GET['p_asid']) ? (int) $_GET['p_asid'] : 0;
    $asp_active   = isset($_GET['asp_active']) ? (int) $_GET['asp_active'] : 0;
    
    // Alleen ASP gebruiken als markers kloppen (pas 8 aan als jouw instance anders is)
    $use_asp = ($asp_active === 1 && $p_asid === 8);
    
    // Safeguards
    $asp_cats     = is_array($asp_cats) ? $asp_cats : [];
    $asp_products = is_array($asp_products) ? $asp_products : [];
    $asp_pages    = is_array($asp_pages) ? $asp_pages : [];
    $asp_other    = is_array($asp_other) ? $asp_other : [];
    ?>
    <div class="custom-search-results-container">
      <div class="container">
        <div class="search-header">
          <h1 class="search-title">
            <?php echo $search_query
              ? sprintf( esc_html__('Search Results for: "%s"', 'textdomain'), esc_html($search_query) )
              : esc_html__('Search Results', 'textdomain'); ?>
          </h1>
          <div class="search-form-wrapper">
            <?php echo do_shortcode('[wpdreams_ajaxsearchpro id=8]'); ?>
          </div>
        </div>
    
        <?php if (!$use_asp): ?>
          <p><?php _e('Ajax Search Pro is not active on this request (missing asp_active=1 & p_asid).', 'textdomain'); ?></p>
        <?php else: ?>
    
          <div class="search-results-columns">
            <!-- LEFT: Categories -->
            <div class="search-col categories-col">
              <?php if (!empty($asp_cats)): ?>
                <h2><?php _e('Product Categories', 'textdomain'); ?></h2>
                <ul class="category-list">
                  <?php foreach ($asp_cats as $c): ?>
                    <?php
                      $name  = $c->title ?? '';
                      $link  = $c->link  ?? '#';
                      $thumb = $c->image ?? '';
                    ?>
                    <li class="category-item">
                      <a href="<?php echo esc_url($link); ?>">
                        <?php if ($thumb): ?>
                          <img src="<?php echo esc_url($thumb); ?>" alt="<?php echo esc_attr($name); ?>">
                        <?php endif; ?>
                        <span><?php echo esc_html($name); ?></span>
                      </a>
                    </li>
                  <?php endforeach; ?>
                </ul>
              <?php endif; ?>
            </div>
    
            <!-- RIGHT: Products + Pages -->
            <div class="search-col products-pages-col">
              <?php if (!empty($asp_products)): ?>
                <h2><?php _e('Products', 'textdomain'); ?></h2>
                <div class="products-grid">
                  <?php foreach ($asp_products as $p): ?>
                    <?php
                      $title = $p->title ?? '';
                      $link  = $p->link  ?? '#';
                      $img   = $p->image ?? '';
                      $price_html = '';
                      if (!empty($p->id) && function_exists('wc_get_product')) {
                        $product_obj = wc_get_product((int)$p->id);
                        if ($product_obj) $price_html = $product_obj->get_price_html();
                      }
                    ?>
                    <div class="product-card">
                      <a href="<?php echo esc_url($link); ?>">
                        <?php if ($img): ?>
                          <img src="<?php echo esc_url($img); ?>" alt="<?php echo esc_attr($title); ?>">
                        <?php endif; ?>
                        <h3><?php echo esc_html($title); ?></h3>
                        <?php if ($price_html): ?>
                          <div class="product-price"><?php echo wp_kses_post($price_html); ?></div>
                        <?php endif; ?>
                      </a>
                    </div>
                  <?php endforeach; ?>
                </div>
              <?php endif; ?>
    
              <?php if (!empty($asp_pages)): ?>
                <h2><?php _e('Pages & Posts', 'textdomain'); ?></h2>
                <ul class="pages-list">
                  <?php foreach ($asp_pages as $pg): ?>
                    <li>
                      <a href="<?php echo esc_url($pg->link ?? '#'); ?>"><?php echo esc_html($pg->title ?? ''); ?></a>
                    </li>
                  <?php endforeach; ?>
                </ul>
              <?php endif; ?>
            </div>
          </div><!-- /columns -->
    
          <?php if (!empty($asp_other)): ?>
            <div class="search-section other-results">
              <h2><?php _e('Overige resultaten', 'textdomain'); ?></h2>
              <ul class="other-list">
                <?php foreach ($asp_other as $o): ?>
                  <li><a href="<?php echo esc_url($o->link ?? '#'); ?>"><?php echo esc_html($o->title ?? ''); ?></a></li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>
    
        <?php endif; ?>
      </div>
    </div>
    
    <?php get_footer(); ?>' 
    
    The instance has ID 8 by the way
    
    put this in the functions.php
    
    

    // Zet deze op true als je een var_dump van de ASP-results wilt
    add_filter(‘asp_results’, function($results, $id) {
    if ($id != 8) return $results; // Alleen search instance ID 8 gebruiken

    global $asp_cats, $asp_products, $asp_pages, $asp_other;
    $asp_cats = $asp_products = $asp_pages = $asp_other = [];

    foreach ($results as $r) {
    // Categorieën
    if (
    (isset($r->taxonomy) && $r->taxonomy === ‘product_cat’) ||
    (isset($r->post_type) && $r->post_type === ‘product_cat’)
    ) {
    $asp_cats[] = $r;
    continue;
    }

    // Producten
    if (isset($r->post_type) && $r->post_type === ‘product’) {
    $asp_products[] = $r;
    continue;
    }

    // Pagina’s of blogposts
    if (isset($r->post_type) && in_array($r->post_type, [‘page’, ‘post’], true)) {
    $asp_pages[] = $r;
    continue;
    }

    // Rest = overige resultaten
    $asp_other[] = $r;
    }

    return $results; // Nodig zodat ASP verder werkt
    }, 10, 2);’

    And CSS

    `/* Custom Search Results Page Styles */

    /* Container grid */
    /* 2 kolommen lay-out */
    .custom-search-results-container .search-results-columns {
    display: grid;
    grid-template-columns: 25% 1fr;
    gap: 2rem;
    align-items: start;
    }

    /* Lists reset & basis */
    .custom-search-results-container .category-list,
    .custom-search-results-container .pages-list,
    .custom-search-results-container .other-list {
    list-style: none;
    margin: 0;
    padding: 0;
    }

    /* Categorieën links */
    .custom-search-results-container .category-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 12px;
    }
    .custom-search-results-container .category-item img {
    width: 40px; height: 40px;
    object-fit: cover; border-radius: 4px;
    }

    /* Product grid rechts */
    .custom-search-results-container .products-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px,1fr));
    gap: 1.5rem;
    }
    .custom-search-results-container .product-card img {
    width: 100%; height: auto; display: block; border-radius: 4px;
    }
    .custom-search-results-container .product-card h3 {
    font-size: 1rem; margin: .5rem 0 0;
    }
    .custom-search-results-container .product-price {
    margin-top: .25rem;
    }

    /* Secties spacing */
    .custom-search-results-container .search-section { margin: 0 0 2rem; }
    .custom-search-results-container .search-section h2 { margin: 0 0 1rem; }

    /* Overige resultaten klein & subtiel */
    .custom-search-results-container .other-results { font-size: .9rem; color: #666; }
    .custom-search-results-container .other-results h2 {
    font-size: 1rem; margin-bottom: .5rem; color: #999;
    }

    /* Responsief */
    @media (max-width: 992px) {
    .custom-search-results-container .search-results-columns {
    grid-template-columns: 1fr;
    }
    }’

    Somehow my xstore theme kept going back to the normal wp results.
    I still have to tweak it, and maybe I’ll put it back into an Elementor grid or loop.
    It could also be that I made a mess of my code because a few full days of trying made me cross eyed.Sorry.

    I’m open for suggestions. this is on my staging by the way, not on production 😉

    #55196
    Ernest MarcinkoErnest Marcinko
    Keymaster

    Looks good to me. This should be good for a long time, I don’t see any immediate problems with it. WHile using globals is not the best, but in this case I don’t think you can get by it any other way. This should be all right.

    #55260
    info_piT4info_piT4
    Participant

    Ok made some changes, I still need to let my colleague test it, but maybe someone with xstore and asp can use it as a base.
    It also has the suggested keywords now. it uses the first suggestion, and searches with it. if there are results it should display them, if not, it should say no results.

    functions.php

    // ===== Ajax Search Pro: gedeelde instellingen =====
    if ( ! defined('BG_ASP_INSTANCE_ID') ) {
        define('BG_ASP_INSTANCE_ID', 8);
    }
    /*CHANGE THE 8 TO YOUR INSTANCE ID */
    /* ---------- Shortcodes renderers ---------- */
    add_shortcode('asp_categories', function () {
        global $asp_cats;
        if (empty($asp_cats) || !is_array($asp_cats)) return '';
        ob_start(); ?>
        <ul class="asp-cat-list">
          <?php foreach ($asp_cats as $c):
              $name  = isset($c->title) ? $c->title : '';
              $link  = isset($c->link)  ? $c->link  : '#';
              $thumb = isset($c->image) ? $c->image : '';
          ?>
            <li class="asp-cat-item">
              <a href="<?php echo esc_url($link); ?>">
                <?php if ($thumb): ?><img src="<?php echo esc_url($thumb); ?>" alt="<?php echo esc_attr($name); ?>"><?php endif; ?>
                <span><?php echo esc_html($name); ?></span>
              </a>
            </li>
          <?php endforeach; ?>
        </ul>
        <?php
        return ob_get_clean();
    });
    
    add_shortcode('asp_products', function () {
        global $asp_products;
        if (empty($asp_products) || !is_array($asp_products)) return '';
        ob_start(); ?>
        <div class="asp-products-grid">
          <?php foreach ($asp_products as $p):
              $title = isset($p->title) ? $p->title : '';
              $link  = isset($p->link)  ? $p->link  : '#';
              $img   = isset($p->image) ? $p->image : '';
              $price_html = '';
              if (!empty($p->id) && function_exists('wc_get_product')) {
                $obj = wc_get_product((int)$p->id);
                if ($obj) $price_html = $obj->get_price_html();
              }
          ?>
            <a class="asp-product-card" href="<?php echo esc_url($link); ?>">
              <?php if ($img): ?><img src="<?php echo esc_url($img); ?>" alt="<?php echo esc_attr($title); ?>"><?php endif; ?>
              <h3><?php echo esc_html($title); ?></h3>
              <?php if ($price_html): ?><div class="asp-price"><?php echo wp_kses_post($price_html); ?></div><?php endif; ?>
            </a>
          <?php endforeach; ?>
        </div>
        <?php
        return ob_get_clean();
    });
    
    add_shortcode('asp_pages_posts', function () {
        global $asp_pages;
        if (empty($asp_pages) || !is_array($asp_pages)) return '';
        ob_start(); ?>
        <ul class="asp-pages-list">
          <?php foreach ($asp_pages as $pg):
              $title = isset($pg->title) ? $pg->title : '';
              $link  = isset($pg->link)  ? $pg->link  : '#';
          ?>
            <li><a href="<?php echo esc_url($link); ?>"><?php echo esc_html($title); ?></a></li>
          <?php endforeach; ?>
        </ul>
        <?php
        return ob_get_clean();
    });
    
    add_shortcode('asp_other', function () {
        global $asp_other;
        if (empty($asp_other) || !is_array($asp_other)) return '';
        ob_start(); ?>
        <ul class="asp-other-list">
          <?php foreach ($asp_other as $o):
              $title = isset($o->title) ? $o->title : '';
              $link  = isset($o->link)  ? $o->link  : '#';
          ?>
            <li><a href="<?php echo esc_url($link); ?>"><?php echo esc_html($title); ?></a></li>
          <?php endforeach; ?>
        </ul>
        <?php
        return ob_get_clean();
    });
    
    /* ---------- Resultaat-splitsing voor tabs (client-side ASP run) ---------- */
    add_filter('asp_results', function($results, $search_id){
        if ((int)$search_id !== (int)BG_ASP_INSTANCE_ID) return $results;
        if (!wp_doing_ajax()) return $results;               // alleen live overlay vullen
    
        if (empty($results)) {
            // Niets doen -> niet per ongeluk server-side arrays leegmaken
            return $results;
        }
    
        global $asp_cats, $asp_products, $asp_pages, $asp_other;
        $asp_cats = $asp_products = $asp_pages = $asp_other = [];
    
        foreach ($results as $r) {
            $pt = isset($r->post_type) ? $r->post_type : '';
            $tx = isset($r->taxonomy)  ? $r->taxonomy  : '';
    
            if ($tx === 'product_cat' || $pt === 'product_cat') {
                $asp_cats[] = $r;
            } elseif ($pt === 'product') {
                // optioneel: if (empty($r->image)) { ..thumbnail ophalen.. }
                $asp_products[] = $r;
            } elseif (in_array($pt, ['page','post'], true)) {
                $asp_pages[] = $r;
            } else {
                $asp_other[] = $r;
            }
        }
        return $results;
    }, 10, 2);
    
    /* ---------- DEBUG helper (enkelvoudig!) ---------- */
    $GLOBALS['bg_asp_debug'] = [];
    if ( ! function_exists('bg_dbg') ) {
      function bg_dbg($k, $v){ $GLOBALS['bg_asp_debug'][$k] = $v; }
    }
    
    /* ---------- (Optioneel) suggested phrases cache voor debugging ---------- */
    $GLOBALS['bg_asp_suggested'] = [];
    add_filter('asp_suggested_phrases', function($phrases, $search_id){
        $phrases = is_array($phrases) ? array_values(array_filter(array_map('trim', $phrases))) : [];
        $GLOBALS['bg_asp_suggested'][(int)$search_id] = $phrases;
        return $phrases;
    }, 10, 2);
    
    /* ---------- Kandidaten generator (NL heuristics) ---------- */
    function bg_asp_candidates($orig){
        $w = mb_strtolower(trim((string)$orig));
        $c = [];
        if ($w !== '') $c[] = $w;
    
        // NL heuristics
        if (preg_match('~kosten$~u', $w)) {
            $c[] = preg_replace('~kosten$~u', 'ing', $w);   // bezorgkosten -> bezorging
            $c[] = preg_replace('~kosten$~u', 'en', $w);    // bezorgkosten -> bezorgen
            $c[] = 'verzendkosten';
            $c[] = 'verzending';
        }
        if (strpos($w, 'bezorg') !== false) {
            $c[] = 'bezorging';
            $c[] = 'bezorgen';
        }
    
        // 1e woord als laatste fallback
        $parts = preg_split('~\s+~u', $w, -1, PREG_SPLIT_NO_EMPTY);
        if (!empty($parts)) $c[] = $parts[0];
    
        // Uniek + schoon
        $c = array_values(array_unique(array_filter(array_map('trim', $c))));
        return $c;
    }
    
    /* ---------- Server-side ASP-run (vult óók tabs) ---------- */
    // ===================== Server-side ASP-run (vult óók tabs) =====================
    function bg_asp_run_fallback_query($phrase){
        $phrase  = trim((string)$phrase);
        $out_ids = [];
    
        global $asp_cats, $asp_products, $asp_pages, $asp_other;
        $asp_cats     = is_array($asp_cats) ? $asp_cats : [];
        $asp_products = is_array($asp_products) ? $asp_products : [];
        $asp_pages    = is_array($asp_pages) ? $asp_pages : [];
        $asp_other    = is_array($asp_other) ? $asp_other : [];
    
        try {
            if (class_exists('\\WPDRMS\\ASP\\Query\\SearchQuery')) {
                $sq = new \WPDRMS\ASP\Query\SearchQuery([
                    's'               => $phrase,
                    '_ajax_search'    => false,
                    'posts_per_page'  => 50
                ], (int)BG_ASP_INSTANCE_ID);
    
                $rows = isset($sq->posts) && is_array($sq->posts) ? $sq->posts : [];
    
                foreach ($rows as $res) {
                    $d   = isset($res->asp_data) ? $res->asp_data : (object)[];
                    $id  = isset($d->id) ? (int)$d->id : (isset($res->ID) ? (int)$res->ID : 0);
                    $ct  = isset($d->content_type) ? $d->content_type : '';
                    $pt  = isset($d->post_type)    ? $d->post_type    : ( $id ? get_post_type($id) : '' );
                    $tx  = isset($d->taxonomy)     ? $d->taxonomy     : '';
    
                    // Termen (categorieën)
                    if ($ct === 'term' || $tx === 'product_cat' || $pt === 'product_cat') {
                        $link = ($tx && $id && function_exists('get_term_link')) ? get_term_link($id, $tx) : '#';
                        if (!is_wp_error($link) && $link) $d->link = $link;
    
                        $asp_cats[] = (object)[
                            'id'=>$id,'title'=>$d->title ?? '','link'=>$d->link ?? '#','image'=>$d->image ?? '',
                            'post_type'=>$pt,'taxonomy'=>$tx
                        ];
                        continue;
                    }
    
                    // Producten (robuster): direct product óf variatie → parent
                    $is_product = ($pt === 'product' || $pt === 'product_variation');
                    if (!$is_product && $id) {
                        $gpt = get_post_type($id);
                        $is_product = ($gpt === 'product' || $gpt === 'product_variation');
                        $pt = $pt ?: $gpt;
                    }
    
                    if ($is_product) {
                        // map variatie → parent voor de Woo-loop
                        if (function_exists('wc_get_product')) {
                            $p = wc_get_product($id);
                            if ($p && $p->is_type('variation')) {
                                $parent_id = $p->get_parent_id();
                                if ($parent_id) $id = (int)$parent_id;
                            }
                        }
                        $asp_products[] = (object)[
                            'id'=>$id,'title'=>$d->title ?? ( $id ? get_the_title($id) : '' ),
                            'link'=>$d->link ?? ( $id ? get_permalink($id) : '#' ),
                            'image'=>$d->image ?? '','post_type'=>'product','taxonomy'=>''
                        ];
                        if ($id) $out_ids[] = $id;
                        continue;
                    }
    
                    // Pagina/bericht
                    if (in_array($pt, ['page','post'], true)) {
                        $asp_pages[] = (object)[
                            'id'=>$id,'title'=>$d->title ?? ( $id ? get_the_title($id) : '' ),
                            'link'=>$d->link ?? ( $id ? get_permalink($id) : '#' ),
                            'image'=>$d->image ?? '','post_type'=>$pt,'taxonomy'=>''
                        ];
                        continue;
                    }
    
                    // Overige
                    $asp_other[] = (object)[
                        'id'=>$id,'title'=>$d->title ?? '','link'=>$d->link ?? '#',
                        'image'=>$d->image ?? '','post_type'=>$pt,'taxonomy'=>$tx
                    ];
                }
            }
        } catch (\Throwable $e) { /* optioneel loggen */ }
    
        // Laat Woo de zichtbaarheid bepalen: filter IDs naar zichtbare + unieke
        $out_ids = bg_wc_filter_visible_products($out_ids);
    
        // Eventuele kale WP zoekfallback op producten (optioneel)
        if (empty($out_ids)) {
            $wpq = new WP_Query([
                'post_type'      => 'product',
                'post_status'    => ['publish'],
                's'              => $phrase,
                'posts_per_page' => 50,
                'fields'         => 'ids'
            ]);
            $out_ids = bg_wc_filter_visible_products( is_array($wpq->posts) ? $wpq->posts : [] );
        }
    
        return ['ids' => $out_ids, 'filled' => !empty($out_ids)];
    }
    
    function bg_asp_fill_tabs_from_wd_asp(){
        if (!function_exists('wd_asp')) return;
    
        $results = (isset(wd_asp()->results) && method_exists(wd_asp()->results, 'getResults'))
            ? wd_asp()->results->getResults()
            : [];
    
        if (empty($results) || !is_array($results)) return;
    
        global $asp_cats, $asp_products, $asp_pages, $asp_other;
        $asp_cats     = is_array($asp_cats) ? $asp_cats : [];
        $asp_products = is_array($asp_products) ? $asp_products : [];
        $asp_pages    = is_array($asp_pages) ? $asp_pages : [];
        $asp_other    = is_array($asp_other) ? $asp_other : [];
    
        foreach ($results as $r) {
            $obj = (object)[
                'id'        => isset($r->id) ? (int)$r->id : 0,
                'title'     => isset($r->title) ? $r->title : '',
                'link'      => isset($r->link)  ? $r->link  : '#',
                'image'     => isset($r->image) ? $r->image : '',
                'post_type' => isset($r->post_type) ? $r->post_type : '',
                'taxonomy'  => isset($r->taxonomy)  ? $r->taxonomy  : ''
            ];
    
            if ($obj->taxonomy === 'product_cat' || $obj->post_type === 'product_cat') {
                $asp_cats[] = $obj;
            } elseif ($obj->post_type === 'product') {
                $asp_products[] = $obj;
            } elseif (in_array($obj->post_type, ['page','post'], true)) {
                $asp_pages[] = $obj;
            } else {
                $asp_other[] = $obj;
            }
        }
    }
    function bg_wc_filter_visible_products(array $ids){
        if (!function_exists('wc_get_product')) return array_values(array_unique(array_map('intval',$ids)));
    
        $out = [];
        foreach (array_unique(array_map('intval', $ids)) as $id) {
            $p = wc_get_product($id);
            if (!$p) continue;
    
            // Variatie -> parent
            if ($p->is_type('variation')) {
                $parent_id = $p->get_parent_id();
                if ($parent_id) {
                    $p = wc_get_product($parent_id);
                    if (!$p) continue;
                    $id = $parent_id;
                }
            }
            if (method_exists($p, 'is_visible') ? $p->is_visible() : true) {
                $out[] = $id;
            }
        }
        return array_values(array_unique($out));
    }
    /* ---------- Main hijack van resultatenpagina ---------- */
    add_action('pre_get_posts', function($q){
        if ( is_admin() || !$q->is_main_query() || !is_search() ) return;
        if ((int)($_GET['asp_active'] ?? 0) !== 1 || (int)($_GET['p_asid'] ?? 0) !== (int)BG_ASP_INSTANCE_ID) return;
        if (!function_exists('wd_asp')) return;
    
        unset($GLOBALS['bg_asp_predicted']); // reset melding
        $orig = isset($_GET['s']) ? trim((string)$_GET['s']) : '';
        $q->set('post_type', 'product');
    
        // 1) live ASP results → IDs
        $ids = [];
        if (isset(wd_asp()->results) && method_exists(wd_asp()->results, 'getResults')) {
            foreach ((array) wd_asp()->results->getResults() as $r) {
                if (!empty($r->id) && (($r->post_type ?? '') === 'product' || ($r->post_type ?? '') === 'product_variation')) {
                    $ids[] = (int)$r->id;
                }
            }
        }
        $ids = bg_wc_filter_visible_products($ids);
        if (!empty($ids)) {
            $q->set('post__in', $ids);
            $q->set('orderby', 'post__in');
            if (function_exists('bg_asp_fill_tabs_from_wd_asp')) bg_asp_fill_tabs_from_wd_asp();
            return;
        }
    
        // 2) server-side run op ORIGINELE term
        $base = bg_asp_run_fallback_query($orig);
        if (!empty($base['ids'])) {
            $q->set('post__in', $base['ids']);
            $q->set('orderby', 'post__in');
            return;
        }
    
        // 3) kandidaten (alleen als origineel niets oplevert)
        foreach (bg_asp_candidates($orig) as $cand) {
            if (mb_strtolower($cand) === mb_strtolower($orig)) continue;
            $out = bg_asp_run_fallback_query($cand);
            if (!empty($out['ids'])) {
                $q->set('post__in', $out['ids']);
                $q->set('orderby', 'post__in');
                $GLOBALS['bg_asp_predicted'] = ['original' => $orig, 'used' => $cand];
                return;
            }
        }
    
        // 4) nog niets
        $q->set('post__in', [0]);
    }, 20);
    
    /* ---------- Zoek-LIKE uitzetten als we post__in sturen ---------- */
    add_filter('posts_search', function($search, $q){
        if (
            $q->is_main_query()
            && $q->is_search()
            && $q->get('post_type') === 'product'
            && !empty($q->get('post__in'))
        ) {
            return ''; // Laat post__in leidend zijn
        }
        return $search;
    }, 20, 2);
    // 1) Sla de keyword suggestions op in een global
    add_filter('asp/suggestions/keywords', function($keywords, $phrase){
        global $asp_suggestions;
        $asp_suggestions = $keywords; // eerste suggestie bewaren
        return $keywords;
    }, 10, 2);
    
    // 2) Controleer bij een search.php render of er geen resultaten zijn, en trigger fallback
    add_action('template_redirect', function() {
        if (!is_search()) return;
        if (!defined('BG_ASP_INSTANCE_ID')) return;
    
        global $asp_products, $asp_cats, $asp_pages, $asp_other, $asp_suggestions;
    
        // Safeguard arrays
        $asp_products = is_array($asp_products) ? $asp_products : [];
        $asp_cats     = is_array($asp_cats) ? $asp_cats : [];
        $asp_pages    = is_array($asp_pages) ? $asp_pages : [];
        $asp_other    = is_array($asp_other) ? $asp_other : [];
    
        // Check: geen resultaten in alle tabs
        $total_results = count($asp_products) + count($asp_cats) + count($asp_pages) + count($asp_other);
        if ($total_results > 0) return; // resultaten, niks doen
    
        // Check: hebben we een suggestie?
        if (empty($asp_suggestions) || !is_array($asp_suggestions)) return;
        $first_suggestion = trim($asp_suggestions[0]);
        if (!$first_suggestion) return;
    
        // Redirect naar dezelfde zoekpagina met nieuw zoekwoord
        $url = add_query_arg('s', urlencode($first_suggestion), home_url('/'));
        // Optioneel: forceer ASP active en instance
        $url = add_query_arg([
            'asp_active' => 1,
            'p_asid'     => BG_ASP_INSTANCE_ID
        ], $url);
    
        wp_safe_redirect($url);
        exit;
    });

    search.php

    <?php
    /**
     * search.php — Tabs met Ajax Search Pro splitsing
     * - Producten-tab: gebruikt standaard Woo/XStore archive loop (met wrappers)
     * - Andere tabs: eenvoudige weergave via gesplitste ASP-globals
     */
    
    defined('ABSPATH') || exit;
    get_header();
    
    if ( ! defined('BG_ASP_INSTANCE_ID') ) define('BG_ASP_INSTANCE_ID', 8);
    
    global $asp_cats, $asp_products, $asp_pages, $asp_other;
    
    // Query markers
    $search_query = get_search_query();
    $p_asid       = isset($_GET['p_asid']) ? (int) $_GET['p_asid'] : 0;
    $asp_active   = isset($_GET['asp_active']) ? (int) $_GET['asp_active'] : 0;
    $use_asp      = ($asp_active === 1 && $p_asid === (int)BG_ASP_INSTANCE_ID);
    
    // Safeguards
    $asp_cats     = is_array($asp_cats) ? $asp_cats : [];
    $asp_products = is_array($asp_products) ? $asp_products : [];
    $asp_pages    = is_array($asp_pages) ? $asp_pages : [];
    $asp_other    = is_array($asp_other) ? $asp_other : [];
    
    // Bepaal welke tabs zichtbaar zijn
    global $wp_query;
    $has_wc_products = $use_asp && isset($wp_query) && (int)$wp_query->post_count > 0;
    $show_products   = $has_wc_products;
    $show_cats       = !empty($asp_cats);
    $show_pages      = !empty($asp_pages);
    $show_other      = !empty($asp_other);
    
    // Eerste actieve tab
    $active_tab = $show_products ? 'products' : ($show_cats ? 'cats' : ($show_pages ? 'pages' : ($show_other ? 'other' : 'none')));
    ?>
    <div class="custom-search-tabs-container">
      <div class="container">
        <div class="search-header">
          <h1 class="search-title">
            <?php echo $search_query
              ? sprintf( esc_html__('Zoekresultaten voor: “%s”', 'xstore-child'), esc_html($search_query) )
              : esc_html__('Zoekresultaten', 'xstore-child'); ?>
          </h1>
          <div class="search-form-wrapper">
            <?php echo do_shortcode('[wpdreams_ajaxsearchpro id='.intval(BG_ASP_INSTANCE_ID).']'); ?>
          </div>
        </div>
    <?php if ( isset($_GET['debug']) && current_user_can('manage_options') ) : ?>
      <pre style="white-space:pre-wrap;background:#111;color:#eee;padding:12px;margin:12px 0;max-height:60vh;overflow:auto;">
    <?php
    global $wp_query, $asp_cats, $asp_products, $asp_pages, $asp_other;
    $dump = [
      'GET'                     => $_GET,
      'BG_ASP_INSTANCE_ID'      => defined('BG_ASP_INSTANCE_ID') ? BG_ASP_INSTANCE_ID : null,
      'use_asp'                 => isset($use_asp) ? $use_asp : null,
      'wp_query->post_count'    => isset($wp_query) ? (int)$wp_query->post_count : null,
      'wp_query->found_posts'   => isset($wp_query) ? (int)$wp_query->found_posts : null,
      'asp_products_count'      => is_array($asp_products) ? count($asp_products) : null,
      'asp_cats_count'          => is_array($asp_cats) ? count($asp_cats) : null,
      'asp_pages_count'         => is_array($asp_pages) ? count($asp_pages) : null,
      'asp_other_count'         => is_array($asp_other) ? count($asp_other) : null,
      'bg_asp_predicted'        => $GLOBALS['bg_asp_predicted'] ?? null,
      'bg_asp_suggested'        => $GLOBALS['bg_asp_suggested'] ?? null,
      'bg_asp_debug'            => $GLOBALS['bg_asp_debug'] ?? null,
      'bg_asp_force_ids'        => $GLOBALS['bg_asp_force_ids'] ?? null,
    ];
    print_r($dump);
    ?>
      </pre>
    <?php endif; ?>
    <?php
     if ( ! empty($GLOBALS['bg_asp_predicted']) ) : ?>
      <p class="asp-predicted-note">
        <?php printf( esc_html__('Geen exacte matches voor “%1$s”. We tonen resultaten voor “%2$s”.', 'xstore-child'),
          esc_html($GLOBALS['bg_asp_predicted']['original']),
          esc_html($GLOBALS['bg_asp_predicted']['used'])
        ); ?>
      </p>
    <?php endif; ?>
    
        <?php if (!$use_asp): ?>
          <p class="asp-inactive-note"><?php esc_html_e('Ajax Search Pro is niet actief voor deze aanvraag (asp_active=1 & p_asid ontbreken).', 'xstore-child'); ?></p>
          <?php get_search_form(); ?>
        <?php else: ?>
    
          <div class="custom-tabs">
            <ul class="tab-titles" role="tablist">
              <?php if ($show_products): ?>
                <li class="tab-title <?php echo ($active_tab==='products'?'active':''); ?>" data-tab="tab-products" role="tab"><?php esc_html_e('Producten','xstore-child'); ?></li>
              <?php endif; ?>
              <?php if ($show_cats): ?>
                <li class="tab-title <?php echo ($active_tab==='cats'?'active':''); ?>" data-tab="tab-cats" role="tab"><?php esc_html_e('Categorieën','xstore-child'); ?></li>
              <?php endif; ?>
              <?php if ($show_pages): ?>
                <li class="tab-title <?php echo ($active_tab==='pages'?'active':''); ?>" data-tab="tab-pages" role="tab"><?php esc_html_e('Pagina’s','xstore-child'); ?></li>
              <?php endif; ?>
              <?php if ($show_other): ?>
                <li class="tab-title <?php echo ($active_tab==='other'?'active':''); ?>" data-tab="tab-other" role="tab"><?php esc_html_e('Overige','xstore-child'); ?></li>
              <?php endif; ?>
            </ul>
    
            <div class="tab-panels">
              <?php if ($show_products): ?>
               <div id="tab-products" class="tab-pane <?php echo ($active_tab==='products'?'active':''); ?>">
      <?php
      // === Render producten o.b.v. ASP-IDs, met Woo pagination ===
      // 1) Verzamel ASP product IDs in ASP-volgorde
      $asp_ids = [];
      foreach ($asp_products as $r) {
          if (!empty($r->id)) $asp_ids[] = (int) $r->id;
      }
    
      // 2) Als er niets is, toon nette no-products
      if (empty($asp_ids)) {
          do_action('woocommerce_before_main_content');
          do_action('woocommerce_no_products_found');
          do_action('woocommerce_after_main_content');
      } else {
          // 3) Paginering en query
          $paged    = max(1, get_query_var('paged') ? (int) get_query_var('paged') : (isset($_GET['paged']) ? (int) $_GET['paged'] : 1));
          $per_page = (int) apply_filters('loop_shop_per_page', 12); // sluit aan op Woo instelling/filter
    
          $args = [
              'post_type'      => 'product',
              'post__in'       => $asp_ids,
              'orderby'        => 'post__in',
              'posts_per_page' => $per_page,
              'paged'          => $paged,
          ];
          $q = new WP_Query($args);
    
          // 4) Woo loop-context en render
          do_action('woocommerce_before_main_content');
    
          // Zorg dat Woo weet dat dit een "search-like" loop is
          if (function_exists('wc_set_loop_prop')) {
              wc_set_loop_prop('is_search', true);
              wc_set_loop_prop('current_page', $paged);
              wc_set_loop_prop('per_page', $per_page);
              wc_set_loop_prop('total', (int) $q->found_posts);
          }
    
          if ($q->have_posts()) {
              // Tijdelijk de global $wp_query wisselen zodat Woo pagination en hooks goed werken
              global $wp_query;
              $old_wp_query = $wp_query;
              $wp_query     = $q;
    
              do_action('woocommerce_before_shop_loop');
              woocommerce_product_loop_start();
    
              while ($q->have_posts()) {
                  $q->the_post();
                  if (get_post_type() !== 'product') continue;
                  wc_get_template_part('content', 'product');
              }
    
              woocommerce_product_loop_end();
              do_action('woocommerce_after_shop_loop'); // pagination etc.
    
              // Herstel globals
              wp_reset_postdata();
              $wp_query = $old_wp_query;
          } else {
              do_action('woocommerce_no_products_found');
          }
    
          do_action('woocommerce_after_main_content');
      }
      ?>
    </div>
              <?php endif; ?>
    
              <?php if ($show_cats): ?>
                <div id="tab-cats" class="tab-pane <?php echo ($active_tab==='cats'?'active':''); ?>">
                  <ul class="asp-cat-list">
                    <?php foreach ($asp_cats as $c):
                      $name  = isset($c->title) ? $c->title : '';
                      $link  = isset($c->link)  ? $c->link  : '#';
                      $thumb = isset($c->image) ? $c->image : '';
                    ?>
                      <li class="asp-cat-item">
                        <a href="<?php echo esc_url($link); ?>">
                          <?php if ($thumb): ?><img src="<?php echo esc_url($thumb); ?>" alt="<?php echo esc_attr($name); ?>"><?php endif; ?>
                          <span><?php echo esc_html($name); ?></span>
                        </a>
                      </li>
                    <?php endforeach; ?>
                  </ul>
                </div>
              <?php endif; ?>
    
              <?php if ($show_pages): ?>
                <div id="tab-pages" class="tab-pane <?php echo ($active_tab==='pages'?'active':''); ?>">
                  <ul class="asp-pages-list">
                    <?php foreach ($asp_pages as $pg): ?>
                      <li><a href="<?php echo esc_url(isset($pg->link)?$pg->link:'#'); ?>"><?php echo esc_html(isset($pg->title)?$pg->title:''); ?></a></li>
                    <?php endforeach; ?>
                  </ul>
                </div>
              <?php endif; ?>
    
              <?php if ($show_other): ?>
                <div id="tab-other" class="tab-pane <?php echo ($active_tab==='other'?'active':''); ?>">
                  <ul class="asp-other-list">
                    <?php foreach ($asp_other as $o): ?>
                      <li><a href="<?php echo esc_url(isset($o->link)?$o->link:'#'); ?>"><?php echo esc_html(isset($o->title)?$o->title:''); ?></a></li>
                    <?php endforeach; ?>
                  </ul>
                </div>
              <?php endif; ?>
            </div>
          </div>
    
        <?php endif; ?>
      </div>
    </div>
    
    <?php
    // Klein tabs-script inline
    add_action('wp_footer', function(){ ?>
    <script>
    (function(){
      var wrap = document.querySelector('.custom-tabs');
      if(!wrap) return;
      var titles = wrap.querySelectorAll('.tab-title');
      var panes  = wrap.querySelectorAll('.tab-pane');
    
      function activate(id){
        titles.forEach(function(t){ t.classList.toggle('active', t.getAttribute('data-tab')===id); });
        panes.forEach(function(p){ p.classList.toggle('active', p.id===id); });
      }
    
      titles.forEach(function(t){
        t.addEventListener('click', function(e){
          e.preventDefault();
          activate(this.getAttribute('data-tab'));
        });
      });
    })();
    </script>
    <?php }, 5);
    
    get_footer();
    

    And CSS

    body.search .sidebar-enabled.sidebar-left {
      display: none !important;
    }
    /* Maak content breder als sidebar weg is */
    body.search .container.sidebar-mobile-off_canvas.content-page {
        max-width: 100% !important;
        padding-left: 15px;
        padding-right: 15px;
    }
    body.custom-search-page .col-md-9.col-md-push-3 {
        width: 100% !important;
        float: none !important;
    }
    body.is-search-page .col-md-3.col-md-pull-9.sidebar-enabled.sidebar.sidebar-left {
        display: none !important;
    }
    .custom-tabs { margin-top: 1.5rem; }
    .custom-tabs .tab-titles { display:flex; gap:1rem; list-style:none; padding:0; margin:0 0 1rem; border-bottom:1px solid rgba(0,0,0,.08); }
    .custom-tabs .tab-title { padding:.5rem 0; cursor:pointer; position:relative; color:inherit; opacity:.7; }
    .custom-tabs .tab-title.active { opacity:1; }
    .custom-tabs .tab-title.active::after {
      content:""; position:absolute; left:0; right:0; bottom:-1px; height:2px; background: currentColor;
    }
    .custom-tabs .tab-panels .tab-pane { display:none; }
    .custom-tabs .tab-panels .tab-pane.active { display:block; }
    
    .asp-cat-list, .asp-pages-list, .asp-other-list { list-style:none; padding:0; margin:0; }
    .asp-cat-item { display:flex; align-items:center; gap:.5rem; margin:0 0 12px; }
    .asp-cat-item img { width:40px; height:40px; object-fit:cover; border-radius:4px; }

    As always, test it, change it, improve it. Do not blindly use it haha. Maybe someone can use it as their base to make their own template.

    #55261
    info_piT4info_piT4
    Participant

    Double entry

    • This reply was modified 9 months, 2 weeks ago by info_piT4info_piT4. Reason: Double
    #55273
    info_piT4info_piT4
    Participant

    I got an error when posting my code, so I hope you can see the message above

    #55274
    Ernest MarcinkoErnest Marcinko
    Keymaster

    It’s all good 🙂

    #55319
    info_piT4info_piT4
    Participant

    Question. Is it possible to place the settings box ([wpdreams_asp_settings id=8 element='div']) on the search.php but not show it on the front page? Or do I need to create two search instances for this?

    I already tried <?php echo do_shortcode(‘[wpdreams_asp_settings id='.intval(BG_ASP_INSTANCE_ID).'element="div"]‘); ?> in search.php
    But that doesn’t show anything, and the turning the settings by default on on the front page is of course going to result in displaying the settings on default on the front page.

    Sorry if this is a silly question.

    #55321
    Ernest MarcinkoErnest Marcinko
    Keymaster

    There are no silly questions.

    You can still do it with a single instance + a bit of custom CSS.

    First, make sure to turn on the settings visibility, so it’s visible. Then via custom CSS you can forcefully hide it on the front page like so:

    div.asp_ss {
       display: none !important;
    }

    If you only add this to the front page, the settings will still be visible on the search.php.

    #55338
    info_piT4info_piT4
    Participant

    You cannot access this content.

    #55356
    Ernest MarcinkoErnest Marcinko
    Keymaster

    Thank you for your kind words 🙂 No worries, I am more than happy to answer.

    We didn’t have subscription based pricing up until this year (only lifetime deals), but we were asked by so many customers that we added it. I don’t think we will ever up the price for existing subscriptions, it wouldn’t make any sense, even though we didn’t promise that.
    We have many thousand lifetime license (non-subscription) customers, who bought the plugin years ago and many of them keep getting new lifetime licenses to this day, even though the price does change from time to time. It only makes sense to change the price for new purchases, bumping existing ones feels like a money grab, unless there is a really good reason for it.
    If the sales drop, then it usually means the product is not good enough, it rarely ever means it’s because of the price.

    #55362
    info_piT4info_piT4
    Participant

    You cannot access this content.

Viewing 15 posts - 1 through 15 (of 16 total)
  • You must be logged in to reply to this topic.