Operation'lar

8 görüntülenme Markdown

Veriye yapılan her değişiklik bir operation'dan geçer. İzinlerin, demo modunun, kancaların ve hata biçiminin her ekranda değil bir kez karara bağlanmasının sebebi budur.

Genel Bakış

Operation, bir isteğin adını anabildiği bir metottur. Bir istek operation adı taşıdığında controller hiçbir sayfa üretmez. O operation'a iliştirilen yetkiyi kontrol eder, metodu çağırır ve olan biteni bir JSON yanıtına çevirir.

Metodun kendisi bir trait içinde yazılır; böylece bir controller tek bir trait kullanarak ilişkili bir operation ailesini kazanır. Controller hangi operation'ları kabul ettiğini ve her birinin neye ihtiyaç duyduğunu bildirir.

Yapı

coremio/operations/ Aile başına bir trait. Controller trait'i kullanır ve metotları controller'ın kendi metodu olur.
Kayıt Controller kabul ettiği operation'ları, her birinin ihtiyaç duyduğu yetkilerle listeler. Kayıt bir yetki iliştirir; bir metodu erişilebilir yapan şey değildir.
Operation nesnesi Dağıtıcı tarafından kurulur ve metodunuza verilir. Operation adını, kayıtlı özellikleri, demo korumasını, kanca yardımcısını ve yanıtı taşır.

Dağıtıcının Sizin İçin Yaptıkları

coremio/classes/Controllers.php, dağıtım kararı
// operation(): ad KAYITLIYSA ya da yalnızca metot olarak VARSA kabul edilir
$isOperation = isset($operations[$name]);
$isMethod    = method_exists($this, $name);
$properties  = $operations[$name] ?? [];

if ($isOperation || $isMethod) return $this->run_operation($name, $properties);

// Hiçbiri değilse son söz modüllerindir; dizi dönen bir dinleyici yanıtın kendisi olur.
foreach (Hook::run("register:admin.operations", Controllers::$cname, $name) as $hfn)
    if ($hfn && is_array($hfn)) { echo Utility::jencode($hfn); return true; }

// Yine yoksa: {"status":"error","message":"Undefined operation: <ad>"}

// run_operation(), sırayla:
//   1. kayıttaki yetkiler -> Admin::isPrivilege(), aksi hâlde throw
//   2. method_exists($this, $method),              aksi hâlde throw
//   3. $this->$method(new Operation($name, $properties));
//   4. catch (Exception $e) -> {"status":"error","message":$e->getMessage()}

Yazma biçiminizi değiştiren madde sonuncusudur. Hata yanıtı kurmazsınız; fırlatırsınız ve çağıranın okuduğu şey fırlattığınız mesajdır.

Referans

Operation Nesnesi

coremio/classes/Operation.php
public ?string $name = '';               // bu operation'ın kayıtlı adı
public static ?string $last = '';        // bu süreçte kurulan son operation

public function __construct($name = '', $properties = []);
public function demo(): void;                                        // demo modunda fırlatır
public function hook(string $name = '', array $vars = []): array;    // ['overwrite' => array] | ['output' => mixed] | []
public static function output($response): bool;                      // her zaman true döner
public static function name(): ?string;                              // $last'ı okur, $this->name'i DEĞİL
demo() Kurulum demo modundaysa fırlatır, böylece dağıtıcı standart reddi cevaplar. Yazan her operation'ın ilk satırı.
output() Dizi verilirse içerik tipi başlığıyla, okunaklı ve kaçışsız kodlamayla JSON gönderilir; dizi değilse olduğu gibi yazılır. Falsy bir yanıt hiçbir şey yazmaz ama yine true döner: output([]) çağıranın ayrıştıramayacağı boş bir gövdeyle cevaplar. Statiktir ama konvansiyon gereği örnek üzerinden çağrılır ve metot orada dursun diye döndürülür.
hook() Bir genişleme noktasını ateşler ve dinleyicilerin döndürdüğünü geri verir. Kabul edilen adlar ve cevabın biçimi için aşağıdaki sözleşmeye bakın.
name() Statiktir ve örneği değil, süreç genelindeki $last'ı okur. Operation'ın içinde ikisi aynıdır; sonradan çalışan bir dinleyicide kancanın zaten geçtiği adı tercih edin.

Kanca Sözleşmesi

İki ad paylaşılan genişleme noktalarının kısaltmasıdır, diğer her ad olduğu gibi kullanılır. Dinleyiciler her zaman üç argüman alır: controller adı, operation adı ve geçirdiğiniz değişkenler.

aynı çağrının iki tarafı
// Operation'ın içinde. "before" => filter:admin.operation.before
//                      "after"  => filter:admin.operation.after
$hook = $operation->hook('before', get_defined_vars());
if ($hook && $hook["overwrite"] ?? []) extract($hook["overwrite"]);   // dinleyici yerel değişkenlerinizi değiştirdi
if ($hook && $hook["output"] ?? false) return $operation->output($hook["output"]);

// Modülün hooks.php dosyasında. Dinleyici üç biçimden BİRİNİ döndürerek karar verir:
Hook::add('filter:admin.operation.before', 1, function ($controller, $operation, $vars) {
    if ($controller !== 'widgets' || $operation !== 'save_widget') return null;

    // 1. reddet:  hook() bu mesajla fırlatır, run_operation onu hata JSON'una çevirir
    if (!$vars["name"]) return ['status' => "error", 'message' => "Name is required"];

    // 2. değiştir: bunlar extract() ile operation'ın yerel değişkenleri olur
    if ($vars["name"] === "x") return ['overwrite_vars' => ['name' => "X"]];

    // 3. cevapla:  operation işi yapmak yerine bunu döndürür
    return ['output' => ['status' => "successful", 'id' => 0]];
});
filter:admin.operation.before Girdi okunup doğrulandıktan sonra, hiçbir şey yazılmadan önce ateşlenir. Reddetmenin ya da değerleri değiştirmenin olağan yeri.
filter:admin.operation.after İş bitip yanıt dizisi kurulduktan sonra ateşlenir; böylece bir dinleyici çağıranın gördüğü yanıtı değiştirebilir.
register:admin.operations Ne kayıtlı ne de metot olan bir ad için yedek. Dizi döndüren bir dinleyici isteği kendisi cevaplar; bir modülün çekirdek ekrana operation eklemesinin yolu budur.
filter:api.response Her dizi yanıtta output() tarafından, referansla ve ikinci argümanda operation adıyla ateşlenir. Bir alan eklemek ya da çıkarmak için son şans.

Yanıt

Başarı ve başarısızlık tek bir alanı paylaşır, böylece çağıran yalnız status'a bakarak dallanır. Hata biçimini dağıtıcı sizin için üretir; başarı biçimi ise geçirdiğiniz şeydir ve konvansiyon gereği aynı alanı artı ekranın ihtiyacını taşır.

hat üzerindeki iki biçim
{ "status": "successful", "id": 42, "message": "Changes saved" }

{ "status": "error", "message": "Name is required" }

Örnek

coremio/operations/AdminWidgets.php
namespace WISECP\Operations;

use Exception;
use Filter;
use Language;
use Operation;

trait AdminWidgets
{
    public function save_widget(Operation $operation): bool
    {
        // Demo modu her yazmayı reddeder; bu her zaman ilk satırdır.
        $operation->demo();

        $id   = (int) Filter::init("POST/id", "rnumbers");
        $name = Filter::init("POST/name", "hclear");

        if (!$name) throw new Exception(Language::gc("widgets/error-name-required"));

        $hook = $operation->hook('before', get_defined_vars());
        if ($hook && $hook["overwrite"] ?? []) extract($hook["overwrite"]);
        if ($hook && $hook["output"] ?? false) return $operation->output($hook["output"]);

        $saved = $id
            ? $this->model->update($id, ['name' => $name])
            : $this->model->add(['name' => $name]);

        if (!$saved) throw new Exception(Language::gc("widgets/error-save-failed"));

        $response = ['status' => "successful", 'id' => (int) ($id ?: $saved)];

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

        return $operation->output($response);
    }
}

Diğer taraf, controller'ın kendi adresine yapılan sıradan bir form gönderimidir; operation adı bir alan olarak taşınır.

istek ve yanıt
curl -X POST "$PANEL/widgets" \
     -H "X-Requested-With: XMLHttpRequest" \
     -d "operation=save_widget&id=42&name=Sidebar"

# {"status":"successful","id":42}
# {"status":"error","message":"You don't have privileges to access this operation."}

Tuzaklar

Demo koruması isteğe bağlı değildir

O olmadan demo kurulumu yazmayı gerçekten yapar ve yalnızca reddetmiş gibi görünür. En başta, herhangi bir okuma ya da doğrulamadan önce durur; böylece hiçbir şey onu atlayamaz.

Kaydı olmayan metodun yetkisi de yoktur

Dağıtıcı yalnızca var olan bir metodu boş özellik dizisiyle çalıştırır; boş yetki listesi de kontrolün tamamen atlanması demektir. Controller'ın bir trait'ten kazandığı her public metot bir giriş noktasıdır: ya kaydedin ya da controller'a koymayın.

Çevrilmiş mesaj fırlatın

Fırlattığınız mesaj isteği yapan kişiye gösterilir. Sabit bir metin, kurulumun herkese tek bir dilde cevap vermesi demektir.

Çıktıyı döndürün, yalnızca çağırmayın

output() yanıtı yazar ve true döner; metodu durdurmaz. Döndürülmeyen bir çağrıdan sonrası yine çalışır ve JSON'un ardına ikinci bir gövde yazabilir.

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.