Model Katmanı

7 görüntülenme Markdown

Modeller, veritabanına dokunmaya izin verilen yerdir; bir satırın satır olmaktan çıkıp uygulamanın geri kalanının kullanabileceği bir şeye dönüştüğü yer de burasıdır.

Genel Bakış

Bir model bir controller'a aittir, aynı adla bulunur ve onun için kurulur. Küçük ve tahmin edilebilir bir metot kümesi sunar; ona sahip olan controller ise bir sorgunun neye benzediğini bilmez.

Bu sınırın sebebi saflık değildir. Aynı veriye panel, müşteri paneli, API ve zamanlanmış komut satırı ihtiyaç duyar; bir sayfanın içine yazılmış sorgu ise yalnızca o sayfaya hizmet edebilir.

Yapı

Konum coremio/models/{yüzey}/{ad}.php, controller adıyla birebir eşleşir. Kuruluş sırasında yola göre yüklenir, otomatik yükleyiciyle değil.
Sınıf adı Şu sırayla denenir: \WISECP\Models\{Yüzey}\{Ad}, sonra {Ad}Model, sonra çıplak Model. Hiçbiri tutmazsa elinizde düz taban sınıf kalır ve kendi metotlarınız yoktur.
Bağlantı $this->db; ilk okumada __get() üzerinden çözülür. Hiç sorgu yapmayan bir sayfa hiç bağlantı açmaz.

Referans

Taban Sınıf

coremio/classes/Models.php
public string $pfx;                      // tablo öneki, ham SQL parçaları için
public ?Database $connection = null;
public static ?self $init = null;        // kurulan son model - WDB onun bağlantısını okur

public function __get($name);            // 'db' Database döner; diğer her ad null
protected function lang_routes(string $langTable, int $ownerId): array;   // ['en' => 'slug', 'tr' => 'slug']
public function menu_list($type, $lang, $list = [], $parent = 0);
public function link_detector($link): string;

$this->db'nin arkasındaki bağlantı fonksiyon-statik bir değişkende tutulur; yani süreçteki her model tek bir Database nesnesini paylaşır ve WDB aynı nesnenin statik cephesidir. İki giriş noktası, tek kurucu ve tek yarım kalmış sorgu durumu.

Alışıldık Metotlar

Bunlar bir arayüz değil, konvansiyondur; uymak, bir modeli görmüş okuyucunun hepsini görmüş olması demektir. Aşağıdaki imzalar daha yeni modellerin kullandığı biçimdir.

bir modelin konvansiyonel arayüzü
public function list(bool $rCount = false, array $filters = [], array $orders = [], int $start = 0, int $end = -1): array|int;
public function get(int $id): array|false;
public function add(array $data = []): int;
public function update(int $id = 0, array $data = []): bool;
public function delete(int $id): bool;
$rCount true satır sayısını int olarak döner; kolonları, sıralamayı ve sayfalamayı atlar. Tablo bileşeni aynı metodu iki kez, her biçimde bir kez çağırır.
$filters Adlandırılmış koşullar; ?? ile okunduğu için her anahtar isteğe bağlıdır. Paylaşılan tek anahtar word, yani serbest metin aramasıdır. Kalanı modele özgüdür: hizmetler status, user_id, product_id, server_id, cycle, duedate artı duedate_op gibi anahtarlar kabul eder.
$orders ['id' => 'DESC']: çıplak kolon adından yöne. Tablo takma adını model kendisi ekler, siz eklemeyin. Boş dizi modelin varsayılan sıralamasına düşer.
$start, $end Başlangıç ve satır sayısı; doğrudan limit()'e geçer. $end = -1 hiç sınır yok demektir ve varsayılandır, bu yüzden sayfayı açıkça isteyin.
Olmayan kayıt get() boş dizi değil false döner; böylece çağıran "böyle bir kayıt yok" ile "içi boş bir kayıt" arasını ayırt edebilir.

Sorgu Kurucusu

Her kurucu metot bağlantıyı döndürür, yani çağrılar zincirlenir ve zinciri build() ya da save() kapatır. Statik cephe aynı nesneye yönlendirir, tek fark şudur: onun where()'ı dört argümanda durur, örnek beşinciyi de alır.

coremio/classes/WDB.php, coremio/classes/Database.php'ye yönlendirir
public static function select($arg = '*');
public static function from($arg = '');
public static function join($type, $table, $where);                      // $type: "LEFT", "INNER", ...
public static function where($column, $mark = '', $value = '', $logical = '');
public static function group_by($arg = '');
public static function order_by($arg);
public static function limit($arg1, $arg2 = null);
public static function build($isthis = false);                           // okumayı koşar, başarısızsa falsy
public static function fetch_assoc($statement = false);                  // tüm satırlar
public static function getAssoc($statement = false);                     // tek satır
public static function getObject($statement = false);
public static function rowCounter($statement = false);                   // etkilenen/dönen satır sayısı
public static function insert($table, $data);                            // satır sayısı; kimlik lastID()'den gelir
public static function update($table = '', $data = []);                  // where() zincirle, sonra save()
public static function delete($arg = '', $arg2 = '');
public static function save($isthis = false);
public static function lastID();

// Örnek metodunda where() bir argüman daha taşır:
public function where($column = '', $mark = '', $value = '', $logical = '', $filter = '?'): self;
select() Kolon listesini tek dize olarak alıp okumayı açar. Ardından tablo, join'ler, koşullar ve sıralama gelir.
from() Tablo, takma adıyla birlikte: "knowledgebase AS t". Kurulumun tablo öneki sizin için eklenir.
join() Tip, tablo ve join koşulu düz dize olarak. O dizedeki değerler bağlanmaz, dolayısıyla kullanıcıdan gelen hiçbir şeyin orada yeri yoktur.
where() Kolon, karşılaştırma, değer ve bu koşulu sonraki koşula bağlayan operatör. Değer bağlanır. Dördüncü argümanı atlarsanız arkada bir AND kalır, o da sizin için eklenir.
build() Okumayı koşturur. Başarısızlıkta falsy döner; ev deseninin koşulsuz fetch yerine build() ? fetch : yedek olmasının sebebi budur.
fetch_assoc() Her satır ilişkisel dizi olarak; bağlantı yoksa boş dizi.
getAssoc() Tek satır. Yanına bir limit(1) koyun ki ifade tam da okumak istediğinizi çeksin.
insert() İlişkisel bir diziden yeni satır yazar ve etkilenen satır sayısını döner. Yeni kimlik bundan değil, aşağıdaki çağrıdan gelir.
update() Bir değişiklik başlatır ve bağlantıyı döner; koşullar ardından gelir, save() işler. Koşulsuz bir güncelleme tabloyu baştan yazar.
lastID() Bu bağlantıdaki son eklemenin ürettiği kimlik, int olarak.

Çevrilmiş Satırlar

İnsanlara gösterilen kayıtlar çevirilerini, kaydın kendi tablosunun adını taşıyan yandaki bir tabloda, dil başına bir satır olarak, owner_id ve lang anahtarlarıyla tutar. Bir okuma o tabloyu aktif dil için join eder ve kaydın kendi değerine düşer. Kısmen çevrilmiş bir kurulum yine de bir değer gösterir. Aynı tablo dil başına slug'ı da taşır; lang_routes()'un dil değiştirici ve alternatif bağlantılar için döndürdüğü şey odur.

Örnek

coremio/models/admin/widgets.php
namespace WISECP\Models\Admin;

use Language;
use Models;

class Widgets extends Models
{
    public function list(bool $rCount = false, array $filters = [], array $orders = [], int $start = 0, int $end = -1): array|int
    {
        $lang   = Language::selected();
        $search = $filters["word"] ?? '';
        if (!$orders || empty(array_key_first($orders))) $orders = ['id' => 'DESC'];

        $stmt = $this->db->select($rCount ? "t.id" : "t.id, COALESCE(tl.name, t.name) AS name, t.status")
            ->from("widgets AS t")
            ->join("LEFT", "widgets_lang AS tl", "tl.owner_id = t.id AND tl.lang = '" . $lang . "'");

        // Dördüncü argüman: BU koşulu bir sonrakine bağlayan operatör.
        if ($search) $stmt->where("COALESCE(tl.name, t.name)", "LIKE", "%" . $search . "%", "&&");
        $stmt->where("t.status", "!=", "deleted");

        if ($rCount) return $stmt->build() ? $stmt->rowCounter() : 0;

        $order = array_key_first($orders);
        $stmt->order_by("t." . $order . " " . strtoupper($orders[$order]));
        if ($end != -1) $stmt->limit($start, $end);

        return $stmt->build() ? $stmt->fetch_assoc() : [];
    }

    public function get(int $id): array|false
    {
        if (!$id) return false;

        return $this->db->select("t.*, COALESCE(tl.name, t.name) AS name")
            ->from("widgets AS t")
            ->join("LEFT", "widgets_lang AS tl", "tl.owner_id = t.id AND tl.lang = '" . Language::selected() . "'")
            ->where("t.id", "=", $id)
            ->limit(1)
            ->build() ? $this->db->getAssoc() : false;
    }

    public function add(array $data = []): int
    {
        return $this->db->insert("widgets", $data) ? $this->db->lastID() : 0;
    }

    public function update(int $id = 0, array $data = []): bool
    {
        return (bool) $this->db->update("widgets", $data)->where("id", "=", $id)->save();
    }
}

Çağıran hiçbir zaman bir sorgu görmez; dallandığı şey tam olarak yukarıdaki iki biçimdir.

okuyan taraf, controller'da
$total = $this->model->list(true, ['word' => $search]);            // int
$rows  = $this->model->list(false, ['word' => $search], ['id' => 'DESC'], 0, 25);

$widget = $this->model->get($id);
if ($widget === false) return $this->page_404();                   // false = "böyle bir kayıt yok"

Tuzaklar

Değerleri bağlayın, asla birleştirmeyin

Koşullar değerlerini argüman olarak alır, böylece sürücü onları bağlar. Koşul dizesine ya da join ifadesine yapıştırılan bir değer, ilk sıra dışı girdiyi bekleyen bir enjeksiyondur.

where()'in dördüncü argümanı ileriye bakar

Bu koşulu bir öncekine bağlayan operatör değildir; bir sonrakine bağlayandır. Operatörü bir alternatifin yanlış koşuluna yazmak hangi satırların döneceğini sessizce değiştirir ve hiçbir şey hata vermez.

İhtiyacınız olan kolonları seçin

Her şeyi seçen bir getter, çağıranlarına tabloda ne varsa onu verir; sonradan eklenen ve veritabanından çıkması hiç düşünülmemiş kolonlar dahil. Böyle bir değer bir yanıta ulaşabiliyorsa, dışarı yazıldığı sınırda filtreleyin.

İş kuralları model metodu değildir

Model neyin saklandığını cevaplar. Bunun sonucunda ne olması gerektiği, panelin, müşteri panelinin, API'nin ve zamanlayıcının hepsinin erişebildiği bir yardımcıya aittir.

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.