Özel Operation Ekleme

1.6k görüntülenme Markdown

Rota ve hata biçimi icat etmeden, veri değiştirip JSON yanıt veren bir POST ucu ekleyin.

Genel Bakış

Operation, veri değiştiren ve JSON ile yanıt veren bir metottur. Var olan bir controller'a operation parametresi gönderirsiniz: rota kaydı yoktur. Controller adı çözer, yetkileri kontrol eder, fırlattığınızı hataya çevirir.

Operation'lar trait'lerde yaşar ve controller'a use ile eklenir. Metot, controller'ın modeline ve yardımcılarına erişir.

Ön Koşullar

  • Genişlettiğiniz sayfaya zaten yanıt veren bir controller, yönetici ya da müşteri tarafında.
  • Operatör panelinde bir yetki anahtarı; boş tanımı oturum açan herkes çağırabilir.
  • Bir çağıran: status, message ve redirect anahtarlarınızı panelin istek yardımcısı işler.

Yapı

ParçaNeredeNe taşır
Traitcoremio/operations, ad alanı WISECP\operationsOperation başına bir public metot
Controllercoremio/controllers, yönetici ya da websiteTrait için use ve tanım dizisi
ÇağıranBir şablon ya da kendi JavaScript'inizoperation ve alanlarınızı gönderir, JSON'u okur

Adım Adım

Metodu Yazın

  1. Bir trait'e public metot ekleyin: tek parametresi operation nesnesi, dönüşü bool.
  2. Metot yazıyorsa ilk satırda demo korumasını çağırın.
  3. Girdileri filtre yardımcısıyla okuyun, ham istek dizilerine dokunmayın; parola kendi temizleyicisini ister.
  4. Doğrulamayı fırlatarak yapın; kurulacak bir hata dönüş değeri yoktur.
  5. Yanıtı, after kancasının değiştirebileceği bir değişkenden çıktı yardımcısıyla döndürün.

Tanımlayın

  1. Trait'i içe aktarın ve controller sınıfında use ile ekleyin.
  2. Gönderilen adla anahtarlayıp yapıcıdaki tanım dizisine ekleyin.
  3. Sayfanın kullandığı yetkileri verin; kontrol metottan önce çalışır.
  4. Giriş metodunun dağıtıcıya devrettiğini doğrulayın; müşteri controller'ı bunu açıkça yapar.

Çağırın

  1. Controller'ın kendi adresine, operation adınıza ayarlı olarak gönderin.
  2. İsteği asenkron çağrı olarak gönderin; yönetici operation'ı öyle görünmeyeni reddeder.
  3. Standart anahtarlar: mesaj bildirim olur, yönlendirme gider, reload yeniler.
  4. Geri çağırmaları yalnız bu anahtarların yapamadığı iş için kullanın.

Referans

Operation Nesnesi

coremio/classes/Operation.php
class Operation
{
    public ?string $name = '';          // gönderilen operation adı
    public static ?string $last = '';   // istek başına kurulan son ad

    // Dağıtıcı kurar: $properties, tanım dizisindeki kayıttır.
    public function __construct($name = '', $properties = []);

    // Tanıtım sisteminde fırlatır. Yazan her operation'ın ilk satırı.
    public function demo(): void;

    // Kancayı ateşler ve cevapları normalize eder. 'before' ve 'after', iki genel
    // admin-operation filtresinin kısaltmasıdır; başka bir dize olduğu gibi kullanılır.
    // Dönüş: [] ya da ['overwrite' => array] ya da ['output' => mixed].
    public function hook(string $name = '', array $vars = []): array;

    // Yanıtı gönderir. Dizi, JSON başlığı kurularak JSON'a çevrilir; başka her şey
    // olduğu gibi basılır. Her zaman true döner, yani `return $op->output(...)`
    // bool dönüş tipini karşılar.
    public static function output($response): bool;

    public static function name(): ?string;
    public function assertContext(): void;
    public static function resolveContext(): bool;
}
$operation->demo() Sadece yazan operation'larda; okuma yapan bir operation bunu taşımamalı.
Operation::output() JSON içerik tipini kendisi kurar ve yanıtı API yanıt filtresinden geçirir.
$operation->hook() Controller adını, operation adını ve değişkenlerinizi geçirir. Hata durumu istisna fırlatır.
bool dönüşü Hiçbir yer okumaz; başarıyı işaret etmez.

Tanım Dizisi

controller yapıcısında
$this->operations = array_merge($this->operations, [

    // Yaygın biçim: gönderilen ad => çalıştırmak için gereken yetkiler.
    'save_acme_settings' => ['privileges' => ['SETTINGS_OPERATION']],

    // 'method', gönderilen adı başka adlı bir metoda yöneltir; iki gönderilen adın
    // tek bir uygulamayı paylaşma yolu budur.
    'acme_retry'         => ['privileges' => ['SETTINGS_OPERATION'], 'method' => 'acme_run'],

    // 'allow_navigation' asenkron istek şartını kaldırır. Yalnız adres olarak
    // açılması gereken operation'lar için (dosya indirme) ve yalnız operation'ın
    // kendi tek kullanımlık jetonuyla birlikte.
    'download_acme_log'  => ['privileges' => ['SETTINGS_OPERATION'], 'allow_navigation' => true],

    // Müşteri tarafı operation'ı genelde yetki tanımlamaz: kendini metodun içinde
    // üye oturumuna göre kapılar.
    'acme_client_action' => [],
]);
privileges Anahtar dizisi; çağıranın herhangi biri yeter. Ret, yönlendirme değil mesaj.
method Gerçekten çağrılan metot; varsayılanı gönderilen ad.
allow_navigation Operation'ı taşıma kontrolünden muaf tutar. Yalnız indirmeler, jetonla birlikte.
Tanımsız ama mevcut Controller'daki bir metot, tanım kaydı olmadan da çağrılabilir ve hiç yetki kontrolü olmadan çalışır.

Dağıtıcı Ne Yapar

AdımDavranışBaşarısız olursa
Ad temizliğiAd rota-güvenli bir dizeye filtrelenir; main bulunamadı işleyicisine giderBilinmeyen ad reddedilir
Lisans kontrolüAktif lisans ister; yardım ve yoklama operation'ı muafYetki kontrolünden önce hata
Taşıma kontrolüX-Requested-With: XMLHttpRequest taşıyan asenkron istekTanım izin vermiyorsa HTTP 403
Yetki kontrolüTanım yetki listeliyorsa çalışırEksik yetkiyi adlandıran hata
MetodunuzYeni kurulmuş operation nesnesiyle çağrılırHer istisna {"status":"error","message":"..."} olur
Yedek yolTanım ve metot yoksa ad bir kayıt kancasına sunulurDizi cevabı gönderilir
register:admin.operations Kimsenin sahiplenmediği bir operation'a cevap vermek için son şans; dizi dönüşü gönderilir.
filter:admin.operation.before before kısaltmasının kancası. Dinleyici dizi döndürür: error engeller, overwrite_vars değişkenlerinizi değiştirir, output yerine cevap verir. Boş dizi sürdürür.
filter:admin.operation.after after kısaltması; yanıt değişkeni oluştuktan sonra çalışır. Aynı dönüş biçimi, ama error istisna fırlatır.
filter:api.response Çıktı yardımcısında, her dizi yanıtta operation adı bağlamıyla çalışır. İstisnalar buraya uğramaz; yanıt referansla değişir, dönüş kullanılmaz.

Çağıranın Okuduğu Yanıt

AnahtarDeğerPanel ne yapar
statussuccessful ya da errorMesajı olan ve başarı olmayan her şey hata sayılır
messageÇevrilmiş metinBildirim, çağıran istediyse toast
redirectBir adres ya da reload ya da scriptGider, yeniler ya da script çalıştırır; mesajla bildirimi bekler
redirect_delayMilisaniyeBeklemeyi ezer: mesajla beş saniye, mesajsız anında
successToastMantıksal değerToast biçimini çağıranın tercihine rağmen zorlar
dataHerhangi bir şeyOtomatik hiçbir şey; kendi geri çağırmanız okur

Örnek

Bir entegrasyon anahtarını döndüren operation ve onu çağıran kod.

coremio/operations/AcmeSettings.php
namespace WISECP\operations;

use Operation;
use Filter;
use Exception;

trait AcmeSettings
{
    public function rotate_acme_key(Operation $operation): bool
    {
        /** @var \WISECP\controllers\admin\settings $this */

        // 1. Tanıtım sisteminde reddedin; herhangi bir şey okunmadan önce.
        $operation->demo();

        // 2. Girdiyi okuyun. Asla $_POST: filtre tipi alan başına seçilir ve bir sır,
        //    noktalaması bozulmadan kalsın diye 'password' filtresinden geçmelidir.
        $id      = (int) Filter::init('POST/id', 'numbers');
        $label   = (string) Filter::init('POST/label', 'hclear');
        $confirm = (string) Filter::init('POST/confirm_password', 'password');

        // 3. BEFORE HOOKS: okumadan sonra, doğrulamadan önce; böylece bir dinleyici
        //    girdileri değiştirebilir ya da bizim yerimize cevap verebilir.
        # BEFORE HOOKS
        $hook = $operation->hook('before', get_defined_vars());
        if ($hook && $hook['overwrite'] ?? []) extract($hook['overwrite']);
        if ($hook && $hook['output'] ?? false) return $operation->output($hook['output']);

        // 4. Fırlatarak doğrulayın. Dağıtıcı bunların her birini, sizin tarafınızda hiçbir
        //    iş olmadan {"status":"error","message":"..."} biçimine çevirir.
        if (!$id) throw new Exception(\Language::gc('admin/settings/error-missing-id'));
        if ($label === '') throw new Exception(\Language::gc('admin/settings/error-label-empty'));

        $adata = \UserManager::LoginData('admin');
        if (!\User::_password_verify('admin', $confirm, $adata['password']))
            throw new Exception(\Language::g('needs/permission-delete-item-invalid-password'));

        // 5. İşin kendisi; trait'in controller'dan devraldığı model üzerinden.
        $key = \Utility::generate_hash(48);
        $this->model->set_acme_credentials($id, ['label' => $label, 'api_key' => $key]);

        // 6. Denetim izi. Üçüncü argüman, actions dil dosyasındaki bir anahtardır.
        \User::addAction((int) $adata['id'], 'alteration', 'changed-acme-key', ['id' => $id]);

        // 7. Yanıt bir değişkene konur ki after kancası onun yerine bir şey koyabilsin.
        $response = [
            'status'   => 'successful',
            'message'  => \Language::gc('admin/settings/success-acme-key-rotated'),
            'data'     => ['masked' => substr($key, 0, 6) . str_repeat('*', 10)],
        ];

        # AFTER HOOKS
        $hook = $operation->hook('after', get_defined_vars());
        if ($hook && $hook['overwrite'] ?? []) extract($hook['overwrite']);
        if ($hook && $hook['output'] ?? false) return $operation->output($hook['output']);

        // 8. Tek çıkış. output() kodlar, başlığı kurar ve true döner.
        return $operation->output($response);
    }
}
coremio/controllers/admin/settings.php
namespace WISECP\controllers\admin;

use WISECP\operations\AcmeSettings;
use Controllers;
use Filter;

class settings extends Controllers
{
    use AcmeSettings;

    public function __construct()
    {
        parent::__construct();
        $this->checkLogin();

        // Kayıt ve erişim denetimi tek yerde. Bu kayıt olmadan da metoda ulaşılabilirdi
        // ve yetki kontrolünden geçmeden çalışırdı.
        $this->operations = array_merge($this->operations, [
            'rotate_acme_key' => ['privileges' => ['SETTINGS_OPERATION']],
        ]);
    }

    public function main()
    {
        // Ayrım noktası: gönderilen bir operation aşağıdaki sayfa metotlarına hiç ulaşmaz.
        if ($operation = Filter::init('REQUEST/operation')) return $this->operation($operation);

        // ... page_* dağıtımı buradan devam eder
        return '';
    }
}

Çağıran taraf.

panel JavaScript'i
function rotateAcmeKey(btn) {
    WcpRequest(CONTROLLER_LINK, {
        method: 'POST',
        button: btn,                       // gidiş dönüş boyunca pasifleşir ve dönen simge alır
        buttonLoader: saving_loader,
        options: { headers: { 'X-Requested-With': 'XMLHttpRequest' } },
        data: {
            operation: 'rotate_acme_key',  // beyan edilen ad, birebir
            id: ACME_ID,
            label: document.getElementById('acmeLabel').value,
            confirm_password: document.getElementById('acmeConfirm').value,
        },
        // status/message/redirect bizim için ele alınır; bu yalnız ek kısım içindir.
        afterDone: (res) => {
            if (res.status !== 'successful') return;
            document.getElementById('acmeKeyMasked').textContent = res.data.masked;
        },
    });
}

Müşteri paneli operation'ında genel before ve after kancası yoktur.

coremio/operations/ClientAcme.php
namespace WISECP\operations;

use Operation;
use Filter;
use Exception;

trait ClientAcme
{
    public function acme_disconnect(Operation $operation): bool
    {
        $operation->demo();

        // Bağlam operation'ın İÇİNDE çözülür: tanım dizisi bir kapı değil kayıttır ve
        // bir metot, kaydı olmadan da dağıtılabilir durumdadır.
        $member = \UserManager::LoginData('member');
        if (!$member) throw new Exception(\Language::gc('website/account/err-auth'));

        $uid = (int) ($member['id'] ?? 0);
        $sid = (int) Filter::init('POST/service_id', 'numbers');

        $service = \Services::get($sid);
        if (!$service || (int) ($service['owner_id'] ?? 0) !== $uid)
            throw new Exception(\Language::gc('website/account/err-not-found'));

        // Kendi alan kancalarınız, ait oldukları noktada. Bir kapı, boş olmayan bir
        // gerekçe döndürerek reddeder ve operation bunu bir hataya çevirir.
        // İki ad da modülünüzün ad alanında yaşar: asla bir çekirdek kancasını
        // ateşlemeyin ve kataloğun taşımadığı bir kanca uydurmayın.
        foreach (\Hook::run('gate:service.acme_disconnect', $sid, $uid) as $veto)
            if ($veto) throw new Exception((string) $veto);

        // set_options() diye bir helper yoktur. Seçenekler Services::set() üzerinden gidip
        // gelir, o da diziyi sizin için JSON'a çevirir: okuyun, birleştirin, haritanın
        // tamamını yazın.
        $options = $service['options'] ?? [];
        $options['acme_linked'] = 0;
        \Services::set($sid, ['options' => $options]);

        \Hook::run('action:service.acme_disconnected', $sid, $uid);

        return $operation->output([
            'status'  => 'successful',
            'message' => \Language::gc('website/account/acme-disconnected'),
        ]);
    }
}

Tuzaklar

Public bir metot zaten bir uçtur

Dağıtım, tanım kaydıyla ya da var olan bir metotla eşleşen her adı kabul eder; yardımcıları private tutun.

Aynı kural API'ye de gerekir

API kaynakları doğrudan yardımcıya ya da modele gider; sadece operation'a eklenen kural jetonla aşılabilir kalır. Kuralı iki tarafın da çağırdığı bir metoda taşıyın.

Elle kodlamak çıktı filtresini atlar

Kendi kodlamanızı yazmak yanıt filtresini ve kodlama işaretlerini atlar. Kanca dalları dahil, çıktı yardımcısıyla dönün.

Operation bir bağlantı değildir

Yönetici operation'ı, istek kendini asenkron bir çağrı olarak tanıtmıyorsa reddedilir. İndirme için muafiyeti tanımlayın ve tek kullanımlık jeton ekleyin.

Demo koruması yalnız yazmalara aittir

Liste ya da önizlemede hiçbir şeyi korumadan tanıtım sistemini bozar. Metot yazıyorsa koruma onun ilk satırı.

Faydalı oldu mu?

Geri bildiriminiz için teşekkürler!

Hâlâ Yardıma mı İhtiyacınız Var?

Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.