Kavramlar, Fikirler, Stratejiler
Kavramlar, Fikirler, StratejilerWordPress Siteniz, Temanız veya Eklentiniz için GraphQL Şemasını Eşleştirme

WordPress Siteniz, Temanız veya Eklentiniz için GraphQL Şemasını Eşleştirme

Mevcut WordPress siteniz için GraphQL kullanmaya başlamaya karar verdiniz. Harika! İster yeni ister mevcut işlevsellik için kullanılsın, GraphQL'in temel veri katmanıyla etkileşime girmesi gerekecektir; bunun için uygulamanızın veri modelini (WordPress sitenizdeki, temanızdaki veya eklentinizdeki özel PHP kodu olsun) GraphQL şemasına eşleştirmeniz gerekecektir.

Eşleştirme nasıl yapılmalıdır? Her şey bir kerede yapılmak zorunda mıdır? Mevcut veri modelinin birebir kopyası olmalı mıdır? Süreçte uygunsuz bir ismi düzeltmek ne olacak? Teknik borç konusunda ise, muhafaza edilmeli mi yoksa üstesinden mi gelinmeli?

Mevcut bir WordPress uygulamasının veri modelini bir GraphQL şemasına eşleştirmek için bazı stratejileri inceleyelim.

Şemayı kendi hızınızda eşleştirin

Bir uygulamaya GraphQL eklemek ya hep ya hiç değildir. Aynı uygulama, gerektiği sürece GraphQL'in diğer API'lerle bir arada var olacağı birden fazla API tarafından aynı anda beslenebilir. Örneğin, mevcut işlevselliği REST ile çalışmaya devam ettirebilir ve yalnızca tüm yeni işlevsellik için GraphQL'i dahil edebiliriz.

GraphQL'e tam geçiş yapmak istiyorsanız, bunun hepsinin bir kerede gerçekleşmesi gerekmez. Mevcut işlevsellik yavaş ama istikrarlı bir şekilde GraphQL'e taşınabilir; ta ki bir gün GraphQL uygulamadaki tek API haline gelene kadar.

Bu nedenle, GraphQL şemasının tamamını ilk gün oluşturabilseniz de bunu yapmanız gerekmez: Herhangi bir zamanda, yalnızca işlevselliğin gerektirdiği varlıkların şemada mevcut olması gerekir (kendi türleri, alanları ve arayüzleri aracılığıyla). Bunları zamanla, kademeli bir biçimde eşleyebilirsiniz.

Arayüzün uygulama yükünü taşımasına izin vermeyin

GraphQL sunucusu, uygulamanın verilerine erişim mantığını uygulayacaktır. Bunu, post verilerini almak için get_posts çağırmak gibi WordPress'ten işlevsellik çağırarak yapacaktır. Bu katmanda, resolver'ları karşılamak için PHP kodu bulunmaktadır.

Ancak bir GraphQL şeması bir arayüzdür: API'deki verilere erişim sözleşmelerini bildirir. Uygulama ayrıntılarını önemsemez: WordPress hakkında, get_posts işlevi hakkında, wp_posts veritabanı tablosu hakkında veya SQL queries hakkında hiçbir şey bilmez.

Bu nedenle, katmanlar arasında bilgi sızıntısını mümkün olduğunca önlemeliyiz.

Bu önemlidir, çünkü veri modeli çoğu zaman uygulamasıyla kirlenmiş olacaktır. WordPress, resimler gibi medya dosyalarını temsil etmek için "attachment" CPT ile buna açık bir örnek sağlar.

Bir Custom Post Type olduğu için, bir resim bir post olarak ele alınır. Daha sonra, medya dosyalarını şu alanları içeren Post türünü kullanarak temsil etmek için cazip bir fikre kapılabiliriz:

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

Ancak bu uygulama için uygun olmayabilir. "content" alanının anlamı bir post için açıktır, ancak bir resim için değildir. Büyük ihtimalle orada yer almamalıdır.

Bir resim, WordPress'te mevcut mantığı yeniden kullanabilmek ve mevcut wp_posts tablosunda saklanabilmek için pratik olduğundan bir CPT olarak modellenmiştir.

Ancak pratik, doğru anlamına gelmez ve sonunda teknik borca yol açabilir (yani, bozucu bir değişiklik üretmeden düzeltilemeyen ve bu nedenle gereğinden uzun süre uygulamada tutulan yetersiz kod).

Mümkün olduğunca, uygulamamızda teknik borç tutmak istemiyoruz. Fırsat buldukça onu düzeltmeliyiz. Veri modelini GraphQL şemasıyla eşleştirmek böyle bir fırsat sunarak, veri arayüzü katmanındaki sorunu düzeltmemize olanak tanır.

(Teknik borç uygulama düzeyinde varlığını sürdürecektir, dolayısıyla sorunu tamamen çözmüyoruz, ancak imkânlarımız dahilinde hafifletiyoruz.)

Bu fikri pratiğe dökelim. Medya dosyalarını temsil eden bir Post türüne sahip olmak yerine, bir resim varlığı için anlamlı olan yalnızca özellikleri içeren bir Media türüne sahip olmak daha mantıklıdır:

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

Perde arkasında, uygulama düzeyinde, alan resolver'ı Media türündeki girdileri çözmek için get_posts işlevini çalıştırmaya devam edecektir, ancak bu GraphQL şemasının ilgisi dahilinde değildir.

GraphQL şemasını veritabanı diyagramından ayırın

WordPress, bu veritabanı varlık-ilişki diyagramı üzerine uygulanmaktadır:

WordPress'te veritabanı varlık-ilişki diyagramı

GraphQL şemasını veritabanı diyagramına dayandırmalıyız, ancak bire bir kopyasını oluşturmaya çalışmamalıyız. Bunun nedeni, hem GraphQL şemasının hem de veritabanı diyagramının diğerine uygulanmayacak belirli ön koşullar veya kısıtlamalarla oluşturulmuş olmasıdır.

Önceki bölüm buna bir örnek göstermektedir; wp_posts tablosu resim CPT'nin verilerini depolarken GraphQL'de Post ve Media olmak üzere iki ayrı tür bulunacaktır.

Başka bir örneği ele alalım: kategoriler. WordPress'te bir post bir kategoriye (veya daha fazlasına) sahip olabilir ve herhangi bir CPT kendi kategorisini de oluşturabilir. Örneğin, "event" adlı bir CPT'nin "event_category" adlı bir kategorisi olacaktır.

Hem post kategorileri hem de etkinlik kategorileri wp_terms tablosunda saklanır. Bu, WordPress'in SQL sorgusunu yürütürken bir veya diğer kategori türünden satırları getirmesini kolaylaştırır.

Bu nedenle, hem postlar hem de etkinlikler tarafından başvurulan Category türü aracılığıyla kategorileri eşlemek için cazip bir fikre kapılabiliriz:

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

Ancak bir post her zaman post kategorilerini içerecek, bir etkinlik ise her zaman etkinlik kategorilerini içerecektir. Bu iki kategori türünün verileri aynı veritabanı tablosunda saklanabilir, ancak uygulama düzeyinde birbirine karışmayacaktır. Post kategorisi ve etkinlik kategorisi iki ayrı varlıktır.

GraphQL'in statik bir tür sistemi vardır. GraphQL'den en iyi şekilde yararlanmak için, uygulama düzeyindeki farklı varlıkların GraphQL şemasında farklı türler kullanılarak modellenmesi gerekir.

Bu durumda, kategorileri GraphQL şemasına eşlerken her biri için farklı bir tür oluşturmalıyız: PostCategory ve EventCategory. Ardından Post türü yalnızca PostCategory'ye, Event türü ise yalnızca EventCategory'ye atıfta bulunacaktır:

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

Şemada tüm kategorileri kapsayan bir varlığa sahip olmak istiyorsak, bu bir Category arayüzü aracılığıyla gerçekleştirilebilir:

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

Bu şekilde, API'ye erişen kullanıcılar, veritabanı diyagramında nasıl eşlendiğinden ve veritabanında nasıl saklandığından bağımsız olarak hangi verilerin alınacağı konusunda net bir anlayışa sahip olacaktır.

Nihai GraphQL şemasına sahip olduğumuzda, şeklinin WordPress veritabanı diyagramına bir ölçüde benzediğini, ancak ondan açıkça farklı olduğunu görebiliriz:

GraphQL şeması

Statik tiplemeyi izleyerek alan adlandırmasını uyarlayın

Alanlar, mümkün olduğunca uygulamadaki aynı adlandırmaya uymalıdır.

Örneğin, wp_insert_post işleviyle bir post oluşturabiliriz ve postun "title" ve "content" özellikleri vardır. Bu adlar GraphQL şeması için de uygundur (hafif değişiklikler gerektirebilirler), bu nedenle bunları korumalıyız:

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

Ancak bu her zaman böyle olmak zorunda değildir. Daha önce gördüğümüz gibi, özel postlar kendi varlıklarına ayrıştırılmalıdır. Ardından, get_posts işlevi herhangi bir CPT'nin bir listesini alırken, şemanın kök türündeki eşdeğer bir posts alanı yalnızca Post türündeki varlıkları alacak, ancak Page türündekini almayacaktır (bu da bir CPT'dir):

type QueryRoot {
  posts: [Post]!
}

Peki, tüm postların ve sayfaların listesini nasıl alırız? CustomPostUnion birlik türü altında eşlenen herhangi bir CPT'nin varlıklarını alan başka bir alan olan customPosts aracılığıyla:

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

Önemli ders şudur: GraphQL şeması için seçtiğimiz adlandırma, alınan varlığın türüne uyarlanmalıdır. Ve GraphQL'in güçlü türleri nedeniyle, bu tür uygulama ve API katmanlarında farklı olabilir.

Bu durumda, WordPress'te bir "post" herhangi bir "custom post type" anlamına gelebilirken, GraphQL'de bir "post" zorunlu olarak bir Post'tur. Bir alan özel postları alıyorsa, GraphQL şemasındaki alan posts değil customPosts olarak adlandırılmalıdır. Benzer şekilde, bir giriş özel bir post için bir ID alıyorsa, postID değil customPostID olarak adlandırılmalıdır.

customPosts alanı için eşleştirme

Bu ders, örneğin yorumlara uygulanır. Bir yorum yalnızca postlara değil, herhangi bir CPT'ye eklenebilir. Ardından, Comment türü bunu customPost alanını içererek (ve post değil) açıkça belirtmelidir:

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

Önceden tanımlanmış dize değerlerini enum'lara dönüştürün, mümkünse büyük harf kullanın

Numaralandırma türleri, kural olarak, büyük harfle tanımlanır. Örneğin, graphql.org belgelerinden şu örnek verilmektedir:

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

Yeni bir enum türü oluşturmamız gerektiğinde, tanımlı sabitleri için büyük harf kullanmalıyız. Ancak uygulamanın veri modelini taşırken, bir enum aracılığıyla eşleyebileceğimiz ancak değerleri küçük harfli dizeler olan belirli önceden tanımlanmış değer kümeleriyle karşılaşabiliriz.

Bir örnek vermek gerekirse, WordPress'teki postların aşağıdaki değerlerden birini içeren bir "status" özelliği vardır:

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

Bu özelliği şemaya eşlerken, Post.status alanı şöyle bir String döndürebilir:

type Post {
  status: String!
}

Ancak status zorunlu olarak bu önceden tanımlanmış değerlerden biri olacağından ve başkası olmayacağından, onu bir enum olarak eşlemeyi tercih ederiz:

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

Şimdi bir sorunumuz olabilir: PUBLISH enum'u uygulamada "publish" değil "PUBLISH" dize değerine dönüştürülecektir.

Beklenen küçük harf yerine büyük harfli bir değer kullanmak, uygulamadaki mantığı bozabilir. Gerçekten de WordPress'te aşağıdaki kodu çalıştırmak işe yaramamaktadır:

// Bu yalnızca yayımlanmış postları değil, tüm postları alacaktır
$published_posts = get_posts([
  "post_status" => "PUBLISH",
]);

Bu durumda, kuralla kolaylığı değiştirmeyi düşünebiliriz; sabitleri eşlemek için yine de bir enum kullanabiliriz, ancak küçük harfle:

enum Status {
  publish
  draft
  pending
  trash
}

Başka bir deyişle, doğru olmak ile pratik olmak arasında bir orta yol bulabiliriz. GraphQL şemasını oluştururken en iyi uygulamaları kullanmalıyız, ancak mantıklı geldiğinde bunlardan sapmaya kendimize izin vermeliyiz.