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
- Fokus pada end goal dulu (tampilkan related professors)
- Hardcode dulu → buat dynamic kemudian
- Reverse engineer solusi step by step
Step 1: Query Relationship (Hardcoded)
// 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
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.
// 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 seperti0, 2jadi0, 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:
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:
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
- Custom Fields → Add New → "Main Body Content"
- Field type: WYSIWYG Editor
- Location: Post Type = Program
- Position: High (after title)
- Publish
Update Template:
// single-program.php
// Ganti:
<?php the_content(); ?>
// Dengan:
<?php the_field('main_body_content'); ?>Hapus Default Editor dari Program CPT:
// 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
// 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 objectsget_the_title($campus)— pass$campusobject agar dapat data campus, bukan programget_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:
| jQuery | Native 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:
npm install axiosimport 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.