Skip to content

Search Logic Aware of Relationships

Masalah

Default WordPress search ('s' argument) hanya cari di title dan body content. Tidak aware terhadap relationships antar post types.

Contoh: Search "biology" tidak menampilkan professors yang mengajar biology (karena hubungan lewat ACF Relationship field, bukan keyword di title/content).

Strategi: Work Backwards

  1. Fokus pada end goal dulu (tampilkan related professors)
  2. Hardcode dulu → buat dynamic kemudian
  3. Reverse engineer solusi step by step

Step 1: Query Relationship (Hardcoded)

php
// search-route.php — di bawah main while loop, sebelum return

$programRelationshipQuery = new WP_Query(array(
  'post_type' => array('professor', 'event'),
  's'         => sanitize_text_field($data['term']),
  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key'     => 'related_programs',
      'compare' => 'LIKE',
      'value'   => '"97"'  // ← hardcoded ID biology program
    )
  )
));

Step 2: Loop & Push Results

php
while ($programRelationshipQuery->have_posts()) {
  $programRelationshipQuery->the_post();

  if (get_post_type() == 'event') {
    // ... sama seperti event if-block di main loop (copy-paste)
  }

  if (get_post_type() == 'professor') {
    array_push($results['professors'], array(
      'title'     => get_the_title(),
      'permalink' => get_the_permalink(),
      'image'     => get_the_post_thumbnail_url(0, 'professorLandscape')
    ));
  }
}

Step 3: Hapus Duplikat

Karena 2 query (main + relationship), satu post bisa muncul di keduanya.

php
// Sebelum return $results:
$results['professors'] = array_values(array_unique($results['professors'], SORT_REGULAR));
$results['events'] = array_values(array_unique($results['events'], SORT_REGULAR));
  • array_unique($array, SORT_REGULAR) — hapus duplikat (compare sub-items dalam associative array)
  • array_values() — reset numeric keys (hapus gaps seperti 0, 2 jadi 0, 1)

Step 4: Dynamic ID (Bukan Hardcoded)

Konsep:

Main query sudah return program results dengan ID. Gunakan ID tersebut untuk meta_query.

Build meta_query secara Programmatic:

php
if ($results['programs']) {
  $programsMetaQuery = array('relation' => 'OR');
  
  foreach ($results['programs'] as $item) {
    array_push($programsMetaQuery, array(
      'key'     => 'related_programs',
      'compare' => 'LIKE',
      'value'   => '"' . $item['id'] . '"'
    ));
  }

  $programRelationshipQuery = new WP_Query(array(
    'post_type'  => array('professor', 'event'),
    'meta_query' => $programsMetaQuery
  ));

  while ($programRelationshipQuery->have_posts()) {
    $programRelationshipQuery->the_post();

    if (get_post_type() == 'event') {
      // ... push ke $results['events']
    }

    if (get_post_type() == 'professor') {
      // ... push ke $results['professors']
    }
  }

  // Remove duplicates
  $results['professors'] = array_values(array_unique($results['professors'], SORT_REGULAR));
  $results['events'] = array_values(array_unique($results['events'], SORT_REGULAR));
}

Penjelasan foreach:

php
foreach ($results['programs'] as $item) {
  // $item = current program object {'title': '...', 'id': 97}
  // Buat filter array untuk setiap program
  // Push ke $programsMetaQuery
}

relation => 'OR':

Tanpa ini, WordPress default = AND (semua conditions harus terpenuhi). Kita mau OR (salah satu match sudah cukup).

Step 5: Hindari False Positives (ACF Body Content)

Masalah:

Jika program Biology punya text "works closely with the math program" di content, search "math" akan pull biology professors juga.

Solusi: Buat ACF Field untuk Body Content

  1. Custom Fields → Add New → "Main Body Content"
  2. Field type: WYSIWYG Editor
  3. Location: Post Type = Program
  4. Position: High (after title)
  5. Publish

Update Template:

php
// single-program.php
// Ganti:
<?php the_content(); ?>
// Dengan:
<?php the_field('main_body_content'); ?>

Hapus Default Editor dari Program CPT:

php
// mu-plugins/university-post-types.php
register_post_type('program', array(
  'supports' => array('title', 'thumbnail'),  // ← hapus 'editor'
  // ...
));

Dengan ini, WordPress search ('s' argument) tidak akan search di ACF field, jadi tidak ada false positives.

Step 6: Campuses (Reverse Direction Relationship)

Perbedaan:

  • Professors/Events → punya field related_programs (arah: dari post → program)
  • Campuses → program yang punya field related_campus (arah: dari program → campus)

Solusi: Langsung Ambil di Main While Loop

php
// Di dalam if (get_post_type() == 'program') block:
if (get_post_type() == 'program') {
  array_push($results['programs'], array(
    'title'     => get_the_title(),
    'permalink' => get_the_permalink(),
    'id'        => get_the_ID()
  ));

  // Ambil related campuses langsung dari program
  $relatedCampuses = get_field('related_campus');

  if ($relatedCampuses) {
    foreach ($relatedCampuses as $campus) {
      array_push($results['campuses'], array(
        'title'     => get_the_title($campus),
        'permalink' => get_the_permalink($campus)
      ));
    }
  }
}

Penjelasan:

  • get_field('related_campus') → array of campus post objects
  • get_the_title($campus) — pass $campus object agar dapat data campus, bukan program
  • get_the_permalink($campus) — sama, pass object untuk campus yang benar
  • Tidak perlu query terpisah karena data sudah ada di program post

jQuery-Free Version (Bonus)

File Search.js bisa diganti versi tanpa jQuery:

jQueryNative JS Replacement
$('.selector')document.querySelector('.selector')
$('.selector') (multiple)document.querySelectorAll('.selector')
.on('click', fn).addEventListener('click', fn)
.addClass('x').classList.add('x')
.removeClass('x').classList.remove('x')
.html('...').innerHTML = '...'
$.getJSON(url, fn)axios.get(url) (with await)
.val().value

Axios Install:

bash
npm install axios
js
import axios from 'axios';

// Di getResults():
async getResults() {
  const response = await axios.get(universityData.root_url + "/wp-json/university/v1/search?term=" + this.searchField.value);
  const results = response.data;
  // ... sisanya sama
}

Keuntungan: Bundle JS lebih kecil (tanpa jQuery library), download lebih cepat untuk visitors.