Konsep, Ide, Strategi
Konsep, Ide, StrategiMemetakan Skema GraphQL untuk Situs, Tema, atau Plugin WordPress Anda

Memetakan Skema GraphQL untuk Situs, Tema, atau Plugin WordPress Anda

Jadi Anda telah memutuskan untuk mulai menggunakan GraphQL untuk situs WordPress Anda yang sudah ada. Bagus! Baik digunakan untuk fungsionalitas baru maupun yang sudah ada, GraphQL perlu berinteraksi dengan lapisan data yang mendasarinya, yang mengharuskan Anda memetakan model data aplikasi (baik kode PHP kustom di situs WordPress, tema, maupun plugin Anda) ke dalam skema GraphQL.

Bagaimana pemetaan seharusnya dilakukan? Haruskah semuanya dilakukan sekaligus? Haruskah itu menjadi replika persis dari model data yang ada? Bagaimana dengan memperbaiki nama yang tidak tepat dalam prosesnya? Dan mengenai utang teknis, haruskah dibiarkan atau ditangani?

Mari kita jelajahi beberapa strategi untuk memetakan model data aplikasi WordPress yang sudah ada ke dalam skema GraphQL.

Petakan skema sesuai dengan kecepatan Anda sendiri

Menambahkan GraphQL ke sebuah aplikasi bukanlah semua atau tidak sama sekali. Aplikasi yang sama bisa ditenagai oleh beberapa API secara bersamaan, di mana GraphQL akan hidup berdampingan dengan API lain, selama yang diperlukan. Misalnya, kita bisa mempertahankan fungsionalitas yang ada yang ditenagai oleh REST, dan mengintegrasikan GraphQL hanya untuk semua fungsionalitas baru.

Jika Anda ingin melakukan migrasi penuh ke GraphQL, itu tidak harus terjadi sekaligus. Fungsionalitas yang ada bisa secara perlahan tapi pasti dimigrasikan ke GraphQL, hingga suatu hari GraphQL menjadi satu-satunya API dalam aplikasi.

Oleh karena itu, meskipun Anda bisa membuat skema GraphQL lengkap pada hari pertama, Anda tidak harus melakukannya: Pada waktu tertentu, hanya entitas yang dibutuhkan oleh fungsionalitas yang harus ada dalam skema (melalui tipe, field, dan antarmukanya). Anda bisa memetakannya seiring berjalannya waktu, secara bertahap.

Jangan biarkan antarmuka menanggung beban implementasi

Server GraphQL akan mengimplementasikan logika untuk mengakses data aplikasi. Ini akan dilakukan dengan memanggil fungsionalitas dari WordPress, seperti memanggil get_posts untuk mengambil data post. Di lapisan ini, ada kode PHP untuk memenuhi resolver.

Namun, skema GraphQL adalah sebuah antarmuka: Ini mendeklarasikan kontrak untuk mengakses data dalam API. Ia tidak peduli dengan detail implementasi: Ia tidak tahu apa pun tentang WordPress, atau tentang fungsi get_posts, tabel DB wp_posts, atau query SQL.

Karena itu, kita harus menghindari kebocoran informasi antar lapisan, sebisa mungkin.

Hal ini penting, karena model data sering kali tercoreng oleh implementasinya. WordPress memberikan contoh nyata dari hal ini dengan CPT "attachment", untuk merepresentasikan file media seperti gambar.

Karena merupakan Custom Post Type, sebuah gambar diperlakukan sebagai post. Kemudian, kita mungkin tergoda untuk merepresentasikan file media menggunakan tipe Post, yang berisi field-field ini:

type Post {
  id: ID!
  title: String
  content: String
  excerpt: String
}

Namun ini mungkin tidak tepat untuk aplikasi tersebut. Makna field "content" jelas untuk sebuah post, tetapi tidak untuk sebuah gambar. Kemungkinan besar, field itu seharusnya tidak ada di sana.

Sebuah gambar dimodelkan sebagai CPT di WordPress karena lebih mudah, sehingga bisa menggunakan kembali logika yang sudah ada, dan bisa disimpan di tabel wp_posts yang sudah ada.

Namun, mudah tidak berarti tepat, dan pada akhirnya bisa menimbulkan utang teknis (yaitu kode yang tidak memadai yang tidak bisa diperbaiki tanpa menghasilkan perubahan yang merusak, sehingga dibiarkan dalam aplikasi lebih lama dari yang seharusnya).

Sebisa mungkin, kita tidak ingin menyimpan utang teknis dalam aplikasi kita. Setiap kali ada kesempatan, kita harus memperbaikinya. Memetakan model data ke skema GraphQL memberikan kesempatan seperti itu, memungkinkan kita untuk memperbaiki masalah di lapisan antarmuka data.

(Utang teknis akan tetap ada di tingkat aplikasi, jadi kita tidak sepenuhnya memperbaiki masalah, tetapi menguranginya sesuai kemampuan kita.)

Mari terapkan ide ini dalam praktik. Alih-alih memiliki tipe Post untuk merepresentasikan file media, lebih masuk akal memiliki tipe Media, yang hanya berisi properti yang memang masuk akal untuk entitas gambar:

type Media {
  id: ID!
  src: String!
  width: Int
  height: Int
}

Di balik layar, pada tingkat implementasi, field resolver masih akan mengeksekusi fungsi get_posts untuk menyelesaikan entri bertipe Media, tetapi itu bukan urusan skema GraphQL.

Pisahkan skema GraphQL dari diagram DB

WordPress diimplementasikan di atas diagram entity-relationship DB ini:

Diagram entity-relationship DB di WordPress

Kita harus membuat skema GraphQL berbasis pada diagram DB, tetapi kita tidak boleh mencoba membuat replika 1 banding 1. Itu karena skema GraphQL maupun diagram DB dibuat dengan prasyarat atau keterbatasan tertentu, yang tidak akan berlaku untuk yang lain.

Bagian sebelumnya menunjukkan contohnya, di mana tabel wp_posts menyimpan data untuk CPT gambar, tetapi dalam GraphQL akan ada dua tipe berbeda, Post dan Media.

Mari pertimbangkan contoh lain: kategori. Di WordPress, sebuah post bisa memiliki satu kategori (atau lebih), dan CPT apa pun juga bisa membuat kategorinya sendiri. Misalnya, sebuah CPT bernama "event" akan memiliki "event_category".

Baik kategori post maupun kategori event disimpan dalam tabel wp_terms. Ini memudahkan WordPress untuk mengambil baris dari salah satu jenis kategori, saat mengeksekusi query SQL.

Oleh karena itu, kita mungkin tergoda untuk memetakan kategori melalui tipe Category, yang direferensikan oleh post maupun event:

type Category {
  id: ID!
  name: String!
}
 
type Post {
  categories: [Category]!
}
 
type Event {
  categories: [Category]!
}

Namun, sebuah post akan selalu berisi kategori post, dan sebuah event akan selalu berisi kategori event. Data untuk dua jenis kategori ini mungkin disimpan di tabel DB yang sama, tetapi tidak akan tercampur di tingkat aplikasi. Kategori post dan kategori event adalah dua entitas yang berbeda.

GraphQL memiliki sistem tipe statis. Untuk memanfaatkan GraphQL secara maksimal, entitas yang berbeda di tingkat aplikasi harus dimodelkan menggunakan tipe yang berbeda dalam skema GraphQL.

Dalam kasus ini, saat memetakan kategori ke dalam skema GraphQL, kita harus membuat tipe yang berbeda untuk masing-masing: PostCategory dan EventCategory. Kemudian, tipe Post hanya akan mereferensikan PostCategory, dan tipe Event hanya akan mereferensikan EventCategory:

type PostCategory {
  id: ID!
  name: String!
}
 
type Post {
  categories: [PostCategory]!
}
 
type EventCategory {
  id: ID!
  name: String!
}
 
type Event {
  categories: [EventCategory]!
}

Jika kita masih ingin memiliki entitas dalam skema yang mencakup semua kategori, ini bisa dicapai melalui antarmuka Category:

interface Category {
  name: String!
}
 
type PostCategory implements Category {
  id: ID!
  name: String!
}
 
type EventCategory implements Category {
  id: ID!
  name: String!
}

Dengan cara ini, pengguna yang mengakses API akan memiliki pemahaman yang jelas tentang data apa yang akan diambil, terlepas dari bagaimana data tersebut dipetakan dalam diagram DB, dan bagaimana cara penyimpanannya dalam DB.

Setelah kita memiliki skema GraphQL akhir, kita bisa menghargai bahwa bentuknya akan agak menyerupai diagram DB WordPress, namun jelas berbeda dari itu:

Skema GraphQL

Sesuaikan penamaan field mengikuti pengetikan statis

Field seharusnya, sebisa mungkin, menghormati penamaan yang sama seperti yang ada di aplikasi.

Misalnya, kita bisa membuat post dengan fungsi wp_insert_post, dan post tersebut memiliki properti "title" dan "content". Nama-nama ini juga bagus untuk skema GraphQL (meskipun mungkin perlu sedikit modifikasi), jadi kita harus mempertahankannya:

type MutationRoot {
  insertPost(title: String, content: String): Post
}
 
type Post {
  id: ID!
  title: String
  content: String
}

Tapi itu tidak selalu demikian. Seperti yang kita lihat sebelumnya, post kustom harus dipisahkan ke dalam entitas mereka sendiri. Kemudian, sementara fungsi get_posts mengambil daftar CPT apa pun, field setara posts dalam tipe root skema hanya akan mengambil entitas bertipe Post, tetapi bukan Page (yang juga merupakan CPT):

type QueryRoot {
  posts: [Post]!
}

Lalu, bagaimana kita mendapatkan daftar semua post dan halaman? Melalui field lain, customPosts, yang mengambil entitas dari CPT apa pun yang dipetakan di bawah tipe union CustomPostUnion:

union CustomPostUnion = Post | Page
 
type QueryRoot {
  customPosts: [CustomPostUnion]!
}

Pelajaran pentingnya adalah ini: Penamaan yang kita pilih untuk skema GraphQL harus disesuaikan dengan tipe entitas yang diambil. Dan karena tipe yang kuat dalam GraphQL, tipe tersebut bisa berbeda di lapisan aplikasi dan lapisan API.

Dalam kasus ini, sementara di WordPress sebuah "post" bisa berarti "custom post type" apa pun, dalam GraphQL sebuah "post" pasti adalah Post. Jika sebuah field mengambil post kustom, maka field dalam skema GraphQL harus bernama customPosts, bukan posts. Demikian pula, jika sebuah input menerima ID untuk post kustom, itu harus disebut customPostID, bukan postID.

Pemetaan untuk field customPosts

Pelajaran ini diterapkan pada komentar, misalnya. Komentar bisa ditambahkan ke CPT apa pun, bukan hanya ke post. Kemudian, tipe Comment harus memperjelas hal ini, dengan berisi field customPost (dan bukan post):

type Comment {
  id: ID!
  customPost: CustomPostUnion!
}

Ubah nilai string yang telah ditetapkan menjadi enum, gunakan huruf kapital jika memungkinkan

Tipe enumerasi, berdasarkan konvensi, didefinisikan dalam huruf kapital. Misalnya, dokumentasi dari graphql.org memberikan contoh ini:

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

Setiap kali kita harus membuat tipe enum baru, kita harus menggunakan huruf kapital untuk konstanta yang didefinisikan. Namun, saat memigrasikan model data dari aplikasi, kita mungkin menemukan kumpulan nilai yang sudah ditetapkan yang bisa kita petakan melalui enum, tetapi nilainya adalah string huruf kecil.

Sebagai contoh, post di WordPress memiliki properti "status", yang berisi salah satu dari nilai-nilai berikut:

  • "publish"
  • "pending"
  • "draft"
  • "trash"

Saat memetakan properti ini di skema, field Post.status bisa mengembalikan String, seperti ini:

type Post {
  status: String!
}

Namun, karena status pasti akan menjadi salah satu dari nilai yang sudah ditetapkan itu, dan tidak ada yang lain, kita lebih memilih memetakannya sebagai enum:

enum Status {
  PUBLISH
  DRAFT
  PENDING
  TRASH
}
 
type Post {
  status: Status!
}

Sekarang, kita mungkin punya masalah: Enum PUBLISH akan dikonversi ke nilai string "PUBLISH" dalam aplikasi, dan bukan "publish".

Menggunakan nilai huruf kapital, alih-alih yang huruf kecil yang diharapkan, logika dalam aplikasi mungkin terganggu. Memang, mengeksekusi kode berikut di WordPress tidak berfungsi:

// Ini akan mengambil semua post, bukan hanya yang dipublikasikan
$published_posts = get_posts([
  "post_status" => "PUBLISH",
]);

Dalam kasus ini, kita bisa mempertimbangkan menukar konvensi dengan kemudahan, tetap menggunakan enum untuk memetakan konstanta, tetapi dalam huruf kecil:

enum Status {
  publish
  draft
  pending
  trash
}

Dengan kata lain, kita bisa memiliki jalan tengah antara menjadi tepat dan menjadi praktis. Kita harus menggunakan praktik terbaik saat membangun skema GraphQL, tetapi memperbolehkan diri kita untuk menyimpang dari praktik tersebut setiap kali masuk akal.