Skip to content

Like Permissions, Restrictions & Completing the LikeBox

Ringkasan

Video ini membahas penambahan permission checks dan restrictions pada custom like endpoint: login check, nonce, limit 1 like per professor, validasi post type. Lalu kita mengimplementasikan real-time UI update untuk like/unlike toggle, fungsi delete like dengan wp_delete_post(), dan versi jQuery-free menggunakan Axios.


1. Permission: Login Check + Nonce

Masalah Awal:

Meskipun kita sudah menambahkan is_user_logged_in() di PHP, REST API selalu mengembalikan false — karena tanpa nonce, WordPress tidak bisa mengidentifikasi siapa yang mengirim request.

Solusi: Nonce di JavaScript

javascript
// Di create like & delete like
$.ajax({
  beforeSend: (xhr) => {
    xhr.setRequestHeader('X-WP-Nonce', universityData.nonce);
  },
  // ... rest of ajax config
});

PHP Side:

php
function createLike($data) {
  if (is_user_logged_in()) {
    // ... create post
  } else {
    die("Only logged in users can create a like.");
  }
}

Flow:

JavaScript mengirim nonce → REST API memverifikasi nonce
→ WordPress mengenali user → is_user_logged_in() = true
→ get_current_user_id() mengembalikan ID yang benar

2. Restriction: 1 Like per User per Professor

Logika:

Sebelum membuat like baru, cek apakah user saat ini sudah pernah like professor yang diminta.

php
function createLike($data) {
  if (is_user_logged_in()) {
    $professor = sanitize_text_field($data['professorId']);

    // Cek apakah like sudah ada
    $existQuery = new WP_Query(array(
      'author' => get_current_user_id(),
      'post_type' => 'like',
      'meta_query' => array(
        array(
          'key' => 'liked_professor_id',
          'compare' => '=',
          'value' => $professor
        )
      )
    ));

    // Hanya buat like jika belum ada DAN ID benar-benar milik professor
    if ($existQuery->found_posts == 0 AND get_post_type($professor) == 'professor') {
      return wp_insert_post(array(
        'post_type' => 'like',
        'post_status' => 'publish',
        'meta_input' => array(
          'liked_professor_id' => $professor
        )
      ));
    } else {
      die("Invalid professor id.");
    }
  } else {
    die("Only logged in users can create a like.");
  }
}

Penjelasan Conditions:

CheckFungsi
$existQuery->found_posts == 0User belum pernah like professor ini
get_post_type($professor) == 'professor'ID yang dikirim benar-benar milik post type professor

Kasus yang Dicegah:

❌ User mengirim ID = 999 (tidak ada)         → get_post_type() = false
❌ User mengirim ID = 50 (post type = 'post') → get_post_type() = 'post'
❌ User sudah like professor ini               → found_posts > 0
✅ Belum like + ID valid professor              → create like!

3. Real-Time UI Update (Create Like)

Setelah berhasil create like, update UI tanpa refresh:

javascript
createLike(currentLikeBox) {
  $.ajax({
    beforeSend: (xhr) => {
      xhr.setRequestHeader('X-WP-Nonce', universityData.nonce);
    },
    url: universityData.root_url + '/wp-json/university/v1/manageLike',
    type: 'POST',
    data: { professorId: currentLikeBox.data("professor") },
    success: (response) => {
      // 1. Heart berubah dari outline ke solid
      currentLikeBox.attr("data-exists", "yes");

      // 2. Increment angka like +1
      var likeCount = parseInt(currentLikeBox.find(".like-count").html(), 10);
      likeCount++;
      currentLikeBox.find(".like-count").html(likeCount);

      // 3. Simpan ID like post (untuk delete nanti)
      currentLikeBox.attr("data-like", response);

      console.log(response);
    },
    error: (response) => {
      console.log(response);
    }
  });
}

Penjelasan parseInt():

javascript
parseInt("3", 10)  // → 3 (number)
// Argumen 1: string yang ingin dikonversi
// Argumen 2: base number system (10 = desimal, yang kita pakai sehari-hari)

Increment/Decrement:

javascript
likeCount++    // Tambah 1 (untuk create)
likeCount--    // Kurang 1 (untuk delete)

4. Delete Like: PHP Server-Side

File: inc/like-route.php

php
function deleteLike($data) {
  $likeId = sanitize_text_field($data['like']);

  // PERMISSION CHECK: hanya bisa hapus post milik sendiri + tipe 'like'
  if (get_current_user_id() == get_post_field('post_author', $likeId)
      AND get_post_type($likeId) == 'like') {
    wp_delete_post($likeId, true);  // true = skip trash, hapus permanen
    return 'Congrats, like deleted.';
  } else {
    die("You do not have permission to delete that.");
  }
}

Penjelasan wp_delete_post():

ArgumenNilaiPenjelasan
1. Post ID$likeIdID post yang akan dihapus
2. Force deletetrueSkip trash, langsung hapus permanen

Permission Checks:

php
// Check 1: User harus pemilik post
get_current_user_id() == get_post_field('post_author', $likeId)

// Check 2: Post harus bertipe 'like'
get_post_type($likeId) == 'like'

KRITIS: Tanpa check ini, user bisa mengirim ID post APAPUN dan menghapusnya! Ini cara mencegah penghapusan sewenang-wenang.

get_post_field():

php
get_post_field('post_author', $likeId)  // Ambil author ID dari post tertentu
// Argumen 1: nama field database (post_author, post_title, post_status, dll.)
// Argumen 2: ID post

5. Real-Time UI Update (Delete Like)

javascript
deleteLike(currentLikeBox) {
  $.ajax({
    beforeSend: (xhr) => {
      xhr.setRequestHeader('X-WP-Nonce', universityData.nonce);
    },
    url: universityData.root_url + '/wp-json/university/v1/manageLike',
    type: 'DELETE',
    data: { like: currentLikeBox.attr("data-like") },
    success: (response) => {
      // Kebalikan dari create:
      currentLikeBox.attr("data-exists", "no");           // Heart outline
      var likeCount = parseInt(currentLikeBox.find(".like-count").html(), 10);
      likeCount--;                                         // Decrement
      currentLikeBox.find(".like-count").html(likeCount);
      currentLikeBox.attr("data-like", "");                // Kosongkan ID

      console.log(response);
    },
    error: (response) => {
      console.log(response);
    }
  });
}

6. Catatan: isset() untuk LikeBox HTML

Masalah:

Jika user belum like professor manapun, $existQuery->posts[0]->ID menyebabkan PHP warning karena array kosong.

Solusi:

php
<!-- SEBELUM (error jika belum like) -->
data-like="<?php echo $existQuery->posts[0]->ID; ?>"

<!-- SESUDAH (aman) -->
data-like="<?php if (isset($existQuery->posts[0]->ID)) echo $existQuery->posts[0]->ID; ?>"

7. jQuery-Free LikeBox

Sama seperti fitur My Notes, versi tanpa jQuery tersedia sebagai drop-in replacement.

Perubahan Utama:

jQueryVanilla JS / Axios
$.ajax()axios.post() / axios.delete()
$(e.target).closest(".like-box")e.target.closest(".like-box")
.attr("data-exists", "yes").setAttribute("data-exists", "yes")
.find(".like-count").html().querySelector(".like-count").innerHTML
parseInt(...)Sama (vanilla JS)

Nonce Global Setup (Axios):

javascript
axios.defaults.headers.common['X-WP-Nonce'] = universityData.nonce;

Set sekali di constructor, berlaku untuk semua request Axios — tidak perlu beforeSend per-request.

Constructor dengan Conditional:

javascript
constructor() {
  if (document.querySelector(".like-box")) {
    // Hanya jalankan jika ada like box di halaman
    axios.defaults.headers.common['X-WP-Nonce'] = universityData.nonce;
    this.events();
  }
}

Diagram Arsitektur Like Feature

┌─────────────────────────────────────────────────┐
│                  FRONT-END                       │
│                                                  │
│  Like Box (.like-box)                           │
│  ├── data-exists="yes/no"                       │
│  ├── data-professor="104"                       │
│  ├── data-like="142"                            │
│  └── Klik → ourClickDispatcher()                │
│       ├── data-exists="yes" → deleteLike()      │
│       └── data-exists="no"  → createLike()      │
└─────────────────┬───────────────────────────┬────┘
                  │ POST                      │ DELETE
                  ▼                           ▼
┌─────────────────────────────────────────────────┐
│  /wp-json/university/v1/manageLike              │
│                                                  │
│  POST → createLike($data)                       │
│    ├── is_user_logged_in()?                     │
│    ├── existQuery→found_posts == 0?             │
│    ├── get_post_type() == 'professor'?          │
│    └── wp_insert_post() → return new post ID    │
│                                                  │
│  DELETE → deleteLike($data)                     │
│    ├── current_user == post_author?             │
│    ├── get_post_type() == 'like'?               │
│    └── wp_delete_post($id, true)                │
└─────────────────────────────────────────────────┘