Settings API ile WordPress Ayar Sayfası Oluşturma

Bir eklenti geliştirirken en çok canımı sıkan şeylerden biri, ayar sayfalarını sıfırdan kurmaktı. Yıllarca $_POST kontrolü yapıp, nonce doğrulamasını unutup, sonra güvenlik açıkları yüzünden başımın belaya girdiğini hatırlıyorum. WordPress’in Settings API’si tam olarak bu kaosa son vermek için var. Ama itiraf etmeliyim: dokümantasyon ilk bakışta kafayı karıştırıyor. Bu yazıda sizi o karmaşadan kurtaracak şekilde, gerçek bir eklenti senaryosu üzerinden Settings API’yi baştan sona anlatacağım.

Settings API Nedir ve Neden Kullanmalısınız

Settings API, WordPress’in 2.7 sürümünden beri var olan ama çoğu geliştirici tarafından ya görmezden gelinen ya da yanlış kullanılan bir sistemdir. Temel olarak şunları sizin yerinize yapar:

  • Form verilerinin doğrulanması ve temizlenmesi
  • Nonce kontrolü
  • Capability check (yetki kontrolü)
  • Verilerin wp_options tablosuna kaydedilmesi
  • Başarı/hata mesajlarının gösterilmesi
  • WordPress admin temasıyla uyumlu sayfa çıktısı

Peki neden doğrudan update_option() çağırıp geçmiyoruz? Çünkü Settings API, WordPress ekosistemiyle entegrasyonu sağlıyor. Başka eklentiler, temalar veya WP-CLI sizin ayarlarınıza düzgün şekilde erişebiliyor. Ayrıca güvenlik katmanı zaten hazır geliyor.

Temel Kavramlar: Settings, Sections ve Fields

Settings API üç ana bileşenden oluşuyor:

Settings (Ayarlar): register_setting() ile kaydettiğiniz seçenek adları. Bunlar wp_options tablosunda saklanıyor.

Sections (Bölümler): Ayar sayfasını mantıksal gruplara bölen başlıklardır. add_settings_section() ile ekleniyor.

Fields (Alanlar): Her bölümün altına yerleştirdiğiniz form elemanlarıdır. add_settings_field() ile tanımlanıyor.

Bu üçü arasındaki ilişkiyi bir restoran menüsü gibi düşünebilirsiniz: Menü sayfası (setting page) var, içinde kategoriler (sections) var, her kategorinin altında yemekler (fields) var.

Gerçek Dünya Senaryosu: Basit Bir Bildirim Eklentisi

Diyelim ki “WP Bildirim Yöneticisi” adında bir eklenti geliştiriyorsunuz. Bu eklenti:

  • E-posta bildirimleri gönderebiliyor
  • Bildirim göndereceği adresleri ayarlanabiliyor
  • Belirli olaylar için bildirim açıp kapatabiliyor
  • Özel bir API anahtarı kullanabiliyor

Bu senaryoyu baştan sona Settings API ile kuracağız.

Eklenti Dosya Yapısı

wp-content/plugins/
└── wp-bildirim-yoneticisi/
    ├── wp-bildirim-yoneticisi.php
    ├── includes/
    │   └── class-settings.php
    └── assets/
        └── admin.css

Ana Eklenti Dosyası

<?php
/**
 * Plugin Name: WP Bildirim Yöneticisi
 * Plugin URI:  https://example.com
 * Description: WordPress için gelişmiş bildirim yönetimi
 * Version:     1.0.0
 * Author:      Ahmet Yilmaz
 * Text Domain: wp-bildirim
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

define( 'WP_BILDIRIM_VERSION', '1.0.0' );
define( 'WP_BILDIRIM_PATH', plugin_dir_path( __FILE__ ) );

require_once WP_BILDIRIM_PATH . 'includes/class-settings.php';

function wp_bildirim_init() {
    $settings = new WP_Bildirim_Settings();
    $settings->init();
}
add_action( 'plugins_loaded', 'wp_bildirim_init' );

Settings Sınıfının Kurulumu

Şimdi asıl işin yapıldığı sınıfı yazalım. Burası her şeyin birbirine bağlandığı yer:

<?php

class WP_Bildirim_Settings {

    private $option_group = 'wp_bildirim_options';
    private $option_name  = 'wp_bildirim_settings';
    private $page_slug    = 'wp-bildirim-ayarlar';

    public function init() {
        add_action( 'admin_menu',    array( $this, 'add_menu_page' ) );
        add_action( 'admin_init',    array( $this, 'register_settings' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
    }

    public function add_menu_page() {
        add_options_page(
            'Bildirim Yöneticisi Ayarları',
            'Bildirim Yöneticisi',
            'manage_options',
            $this->page_slug,
            array( $this, 'render_settings_page' )
        );
    }

    public function get_options() {
        $defaults = array(
            'bildirim_email'     => get_option( 'admin_email' ),
            'api_key'            => '',
            'yeni_yorum_bildir'  => '1',
            'yeni_uye_bildir'    => '0',
            'bildirim_limiti'    => 10,
            'ozel_mesaj'         => '',
        );
        return wp_parse_args(
            get_option( $this->option_name, array() ),
            $defaults
        );
    }
}

wp_parse_args() kullanımına dikkat edin. Bu, kayıtlı değerlerle varsayılanları birleştiriyor. Eklentiyi ilk kurduğunuzda get_option() boş dizi dönecek, wp_parse_args() bu durumda varsayılan değerleri devreye sokuyor.

register_settings Metodu

Bu metod Settings API’nin kalbi. Üç farklı işlevi bir arada yürütüyor:

public function register_settings() {

    register_setting(
        $this->option_group,
        $this->option_name,
        array(
            'type'              => 'array',
            'sanitize_callback' => array( $this, 'sanitize_options' ),
        )
    );

    // BÖLÜM 1: E-posta Ayarları
    add_settings_section(
        'email_ayarlari',
        'E-posta Ayarları',
        array( $this, 'render_email_section_desc' ),
        $this->page_slug
    );

    add_settings_field(
        'bildirim_email',
        'Bildirim E-posta Adresi',
        array( $this, 'render_email_field' ),
        $this->page_slug,
        'email_ayarlari',
        array( 'label_for' => 'bildirim_email' )
    );

    add_settings_field(
        'api_key',
        'API Anahtarı',
        array( $this, 'render_api_key_field' ),
        $this->page_slug,
        'email_ayarlari',
        array( 'label_for' => 'api_key' )
    );

    // BÖLÜM 2: Bildirim Tetikleyicileri
    add_settings_section(
        'tetikleyici_ayarlari',
        'Bildirim Tetikleyicileri',
        array( $this, 'render_tetikleyici_section_desc' ),
        $this->page_slug
    );

    add_settings_field(
        'yeni_yorum_bildir',
        'Yeni Yorum Bildirimi',
        array( $this, 'render_checkbox_field' ),
        $this->page_slug,
        'tetikleyici_ayarlari',
        array(
            'label_for'  => 'yeni_yorum_bildir',
            'field_key'  => 'yeni_yorum_bildir',
            'desc'       => 'Yeni bir yorum geldiğinde bildirim al.',
        )
    );

    add_settings_field(
        'yeni_uye_bildir',
        'Yeni Üye Bildirimi',
        array( $this, 'render_checkbox_field' ),
        $this->page_slug,
        'tetikleyici_ayarlari',
        array(
            'label_for' => 'yeni_uye_bildir',
            'field_key' => 'yeni_uye_bildir',
            'desc'      => 'Yeni bir kullanıcı kayıt olduğunda bildirim al.',
        )
    );

    add_settings_field(
        'bildirim_limiti',
        'Günlük Bildirim Limiti',
        array( $this, 'render_number_field' ),
        $this->page_slug,
        'tetikleyici_ayarlari',
        array(
            'label_for' => 'bildirim_limiti',
            'field_key' => 'bildirim_limiti',
            'min'       => 1,
            'max'       => 100,
        )
    );
}

add_settings_field()‘in son parametresi olan $args array’i çok kullanışlı. Bu argümanlar render metoduna aktarılıyor, böylece aynı render metodunu birden fazla alan için kullanabiliyorsunuz.

Field Render Metodları

Her alan tipi için ayrı render metodları yazıyoruz. Bu yaklaşım kodu yönetilebilir kılıyor:

public function render_email_field( $args ) {
    $options = $this->get_options();
    $value   = isset( $options['bildirim_email'] ) ? $options['bildirim_email'] : '';
    ?>
    <input
        type="email"
        id="bildirim_email"
        name="<?php echo esc_attr( $this->option_name ); ?>[bildirim_email]"
        value="<?php echo esc_attr( $value ); ?>"
        class="regular-text"
    />
    <p class="description">
        Bildirimlerin gönderileceği e-posta adresini girin.
    </p>
    <?php
}

public function render_api_key_field( $args ) {
    $options = $this->get_options();
    $value   = isset( $options['api_key'] ) ? $options['api_key'] : '';
    ?>
    <input
        type="password"
        id="api_key"
        name="<?php echo esc_attr( $this->option_name ); ?>[api_key]"
        value="<?php echo esc_attr( $value ); ?>"
        class="regular-text"
        autocomplete="new-password"
    />
    <p class="description">
        Bildirim servisinden aldığınız API anahtarını girin.
        <a href="https://example.com/api" target="_blank">API anahtarı almak için tıklayın</a>.
    </p>
    <?php
}

public function render_checkbox_field( $args ) {
    $options   = $this->get_options();
    $field_key = $args['field_key'];
    $value     = isset( $options[ $field_key ] ) ? $options[ $field_key ] : '0';
    $desc      = isset( $args['desc'] ) ? $args['desc'] : '';
    ?>
    <label for="<?php echo esc_attr( $field_key ); ?>">
        <input
            type="checkbox"
            id="<?php echo esc_attr( $field_key ); ?>"
            name="<?php echo esc_attr( $this->option_name ); ?>[<?php echo esc_attr( $field_key ); ?>]"
            value="1"
            <?php checked( '1', $value ); ?>
        />
        <?php echo esc_html( $desc ); ?>
    </label>
    <?php
}

public function render_number_field( $args ) {
    $options   = $this->get_options();
    $field_key = $args['field_key'];
    $value     = isset( $options[ $field_key ] ) ? absint( $options[ $field_key ] ) : 10;
    $min       = isset( $args['min'] ) ? absint( $args['min'] ) : 0;
    $max       = isset( $args['max'] ) ? absint( $args['max'] ) : 999;
    ?>
    <input
        type="number"
        id="<?php echo esc_attr( $field_key ); ?>"
        name="<?php echo esc_attr( $this->option_name ); ?>[<?php echo esc_attr( $field_key ); ?>]"
        value="<?php echo esc_attr( $value ); ?>"
        min="<?php echo esc_attr( $min ); ?>"
        max="<?php echo esc_attr( $max ); ?>"
        class="small-text"
    />
    <p class="description">Günde maksimum kaç bildirim gönderilebileceğini ayarlayın.</p>
    <?php
}

Input name attribute’larına dikkat edin: option_name[field_key] formatında. WordPress bu formatı otomatik olarak array’e çeviriyor ve tek bir wp_options kaydına tüm ayarları saklıyor. Bu, onlarca ayrı get_option() çağrısı yapmak yerine tek bir çağrıyla tüm ayarlara ulaşmanızı sağlıyor.

Sanitize (Temizleme) Callback’i

Bu fonksiyon olmadan Settings API çalışır ama güvensizdir. Kaydetmeden önce her veriyi temizlemek zorundasınız:

public function sanitize_options( $input ) {

    $sanitized = array();

    // E-posta doğrulama
    if ( isset( $input['bildirim_email'] ) ) {
        $email = sanitize_email( $input['bildirim_email'] );
        if ( is_email( $email ) ) {
            $sanitized['bildirim_email'] = $email;
        } else {
            add_settings_error(
                $this->option_name,
                'gecersiz_email',
                'Geçersiz e-posta adresi girdiniz. Lütfen tekrar kontrol edin.',
                'error'
            );
            $sanitized['bildirim_email'] = get_option( 'admin_email' );
        }
    }

    // API anahtarı - sadece alfanümerik ve tire
    if ( isset( $input['api_key'] ) ) {
        $sanitized['api_key'] = sanitize_text_field( $input['api_key'] );
    }

    // Checkbox alanları - ya '1' ya da '0'
    $checkbox_fields = array( 'yeni_yorum_bildir', 'yeni_uye_bildir' );
    foreach ( $checkbox_fields as $field ) {
        $sanitized[ $field ] = isset( $input[ $field ] ) && '1' === $input[ $field ] ? '1' : '0';
    }

    // Sayı alanı
    if ( isset( $input['bildirim_limiti'] ) ) {
        $limit = absint( $input['bildirim_limiti'] );
        $sanitized['bildirim_limiti'] = ( $limit >= 1 && $limit <= 100 ) ? $limit : 10;
    }

    return $sanitized;
}

add_settings_error() çok değerli bir fonksiyon. Settings API, yönlendirme sonrasında bu hataları otomatik olarak gösteriyor. Kendi flash mesaj sisteminizi kurmanıza gerek yok.

Sayfa Render Metodu

public function render_settings_page() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>

        <?php settings_errors( $this->option_name ); ?>

        <form method="post" action="options.php">
            <?php
            settings_fields( $this->option_name );
            do_settings_sections( $this->page_slug );
            submit_button( 'Ayarları Kaydet' );
            ?>
        </form>
    </div>
    <?php
}

settings_fields() nonce, action ve option_page hidden field’larını otomatik olarak ekliyor. do_settings_sections() ise kaydettiğiniz tüm section ve field’ları sırasıyla çıktılıyor. Formun action’ı options.php olduğuna dikkat edin, bu WordPress’in kendi işlem dosyası.

Section Açıklama Metodları

public function render_email_section_desc() {
    echo '<p>E-posta gönderimi için gerekli temel ayarları buradan yapılandırabilirsiniz.</p>';
}

public function render_tetikleyici_section_desc() {
    echo '<p>Hangi olayların bildirim tetikleyeceğini ve bildirim limitlerini ayarlayın.</p>';
}

WP-CLI Entegrasyonu

Settings API kullandığınızda, ayarlarınız WP-CLI ile de yönetilebilir hale geliyor. Bu, DevOps iş akışlarında büyük kolaylık sağlıyor:

# Mevcut ayarları görüntüle
wp option get wp_bildirim_settings --format=json

# Belirli bir ayarı güncelle
wp option patch update wp_bildirim_settings bildirim_email "[email protected]"

# Tüm ayarı sıfırla
wp option delete wp_bildirim_settings

# Deployment sonrası ayarları toplu güncelle
wp option patch update wp_bildirim_settings api_key "prod-api-key-buraya" 
  && wp option patch update wp_bildirim_settings bildirim_limiti 50

Bu yaklaşım özellikle staging’den production’a geçişlerde, farklı ortamlar için farklı konfigürasyonları CI/CD pipeline’ınızdan yönetmenize olanak tanıyor.

Sık Yapılan Hatalar ve Çözümleri

Checkbox değerleri kaydedilmiyor: Checkbox işaretli olmadığında form onu hiç göndermez. Sanitize callback’inizde mutlaka varsayılan değer atayın. Yukarıdaki sanitize_options metodunda isset() kontrolüyle bunu ele aldık.

Sayfa yenilenince ayarlar sıfırlanıyor: register_setting()‘in ilk parametresi olan $option_group, settings_fields() çağrısındaki parametre ile birebir aynı olmalı. Büyük/küçük harf duyarlıdır.

Sanitize callback iki kez çalışıyor: WordPress 4.4’te düzeltilen ama bazı eski kurulumda hâlâ görülen bir sorun. Sanitize callback içinde current_filter() ile kontrol yapabilirsiniz.

Çoklu site (multisite) desteği: register_setting() ile kaydettiğiniz ayarlar varsayılan olarak o sitenin options tablosuna gider. Ağ genelinde geçerli olmasını istiyorsanız add_network_option() ve update_network_option() fonksiyonlarına bakmanız gerekir, Settings API burada yetersiz kalır.

Performans İpuçları

Ayarlarınızı tek bir option kaydında tutun. Her ayarı ayrı wp_options kaydı olarak saklamak, her sayfa yüklenişinde ek veritabanı sorgusu anlamına gelir. wp_bildirim_settings gibi tek bir array kaydıyla tüm ayarları bir sorguda alırsınız.

Sık erişilen ayarları nesne önbelleğiyle sarın:

public function get_cached_options() {
    $cached = wp_cache_get( 'wp_bildirim_options', 'options' );
    if ( false !== $cached ) {
        return $cached;
    }
    $options = $this->get_options();
    wp_cache_set( 'wp_bildirim_options', $options, 'options', 3600 );
    return $options;
}

Aslında WordPress zaten get_option() çağrılarını alloptions ile önbelleğe alıyor, ama nesne önbelleği (Redis/Memcached) olmayan ortamlarda bu ek katman faydalı olabilir.

Sekme Yapısı ile Çok Bölümlü Sayfalar

Ayar sayfalasınız büyüdükçe sekme yapısına geçmek isteyebilirsiniz. Bu durumda URL parametresiyle aktif sekmeyi yönetebilirsiniz:

public function render_settings_page() {
    $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'genel';
    $tabs = array(
        'genel'     => 'Genel Ayarlar',
        'gelismis'  => 'Gelişmiş',
        'lisans'    => 'Lisans',
    );
    ?>
    <div class="wrap">
        <h1>Bildirim Yöneticisi</h1>
        <nav class="nav-tab-wrapper">
            <?php foreach ( $tabs as $tab_key => $tab_label ) : ?>
                <a href="?page=<?php echo esc_attr( $this->page_slug ); ?>&tab=<?php echo esc_attr( $tab_key ); ?>"
                   class="nav-tab <?php echo $active_tab === $tab_key ? 'nav-tab-active' : ''; ?>">
                    <?php echo esc_html( $tab_label ); ?>
                </a>
            <?php endforeach; ?>
        </nav>
        <form method="post" action="options.php">
            <?php
            settings_fields( $this->option_group . '_' . $active_tab );
            do_settings_sections( $this->page_slug . '_' . $active_tab );
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

Bu yapıda her sekme için ayrı option_group ve page_slug kullanmanız gerekiyor.

Sonuç

Settings API, ilk bakışta fazla soyutlanmış ve karmaşık görünebilir. Register, Section, Field, Sanitize… Bir sürü parçanın birbirine bağlanması gerekiyor. Ama bu mimarinin arkasındaki mantığı kavradıktan sonra, neden sıfırdan ayar sistemi yazmak istemeyeceğinizi anlıyorsunuz.

Güvenlik, doğrulama, WP-CLI entegrasyonu, çoklu dil desteği, WordPress admin temasıyla uyum… Bunların hepsini Settings API size hazır veriyor. Sizin işiniz sadece alanları tanımlamak, render etmek ve sanitize etmek.

Bir sonraki eklenti projenizde $_POST['benim_ayarim'] yazmak yerine Settings API’yi deneyin. İlk kurulum biraz daha uzun sürer, ama uzun vadede hem daha güvenli hem de daha bakımı kolay bir yapıya kavuşursunuz. Özellikle eklentinizi WordPress.org’da yayınlamayı düşünüyorsanız ya da müşterilerinize teslim edecekseniz, plugin review sürecinde bu yapıyı görmeleri ciddi artı puan kazandırıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir