Uygulamalar nasıl çalışır?

Şu anda Nette dokümantasyonunun temel bölümünü okuyorsunuz. Web uygulamalarının nasıl çalıştığının tüm ilkelerini, bir isteğin doğduğu andan PHP betiğinin çalışmasını bitirdiği ana kadar A'dan Z'ye öğreneceksiniz. Okuduktan sonra şunları anlayacaksınız:

  • her şeyin nasıl çalıştığını
  • Bootstrap'ın, Presenter'ın ve DI konteynerinin ne olduğunu
  • dizin yapısının nasıl göründüğünü

Dizin yapısı

WebProject adlı örnek web uygulaması iskeletini açın. Okurken sözü geçen dosyalara bakabilirsiniz.

Dizin yapısı aşağı yukarı şöyle görünür:

web-project/
├── app/                      ← uygulama dizini
│   ├── Core/                 ← çalışma için gerekli çekirdek sınıflar
│   │   └── RouterFactory.php ← URL adreslerinin yapılandırması
│   ├── Presentation/         ← presenter'lar, şablonlar vb.
│   │   ├── @layout.latte     ← layout şablonu
│   │   └── Home/             ← Home presenter'ının dizini
│   │       ├── HomePresenter.php ← Home presenter sınıfı
│   │       └── default.latte ← default eylemi için şablon
│   └── Bootstrap.php         ← başlatıcı sınıf Bootstrap
├── assets/                   ← kaynaklar (SCSS, TypeScript, kaynak görseller)
├── bin/                      ← komut satırından çalıştırılan betikler
├── config/                   ← yapılandırma dosyaları
│   ├── common.neon
│   └── services.neon
├── log/                      ← günlüklenen hatalar
├── temp/                     ← geçici dosyalar, önbellek, …
├── vendor/                   ← Composer ile kurulan kütüphaneler
│   ├── ...
│   └── autoload.php          ← kurulu tüm paketlerin otomatik yüklenmesi
├── www/                      ← genel dizin, projenin document root'u
│   ├── assets/               ← derlenmiş statik dosyalar (CSS, JS, görseller, ...)
│   ├── .htaccess             ← mod_rewrite kuralları
│   └── index.php             ← uygulamayı başlatan ilk dosya
└── .htaccess                 ← www dışındaki tüm dizinlere erişimi yasaklar

Dizin yapısını istediğiniz gibi değiştirebilir, klasörleri yeniden adlandırabilir veya taşıyabilirsiniz; tamamen esnektir. Nette ayrıca akıllı otomatik algılamaya sahiptir ve uygulamanın konumunu, URL tabanı dahil, kendiliğinden tanır.

Biraz daha büyük uygulamalarda presenter ve şablon klasörlerini alt dizinlere ayırabilir ve sınıfları modül dediğimiz isim alanlarına gruplayabiliriz.

www/ dizini projenin genel dizinini, yani document-root'unu temsil eder. Uygulama tarafında başka bir şey yapılandırmaya gerek kalmadan yeniden adlandırabilirsiniz. Yalnızca document-root'un bu dizini göstermesi için hosting'i yapılandırmanız gerekir.

WebProject'i, Nette dahil, doğrudan Composer ile de indirebilirsiniz:

composer create-project nette/web-project

Linux veya macOS'ta log/ ve temp/ dizinleri için yazma izinlerini ayarlayın.

WebProject uygulaması çalışmaya hazırdır; hiçbir şey yapılandırmaya gerek yoktur ve www/ klasörüne giderek doğrudan tarayıcıda görüntüleyebilirsiniz.

HTTP isteği

Her şey, kullanıcının tarayıcısında bir sayfa açmasıyla başlar. Tarayıcı sunucuya bir HTTP isteği gönderir. Bu istek, genel dizin www/ içinde yer alan tek bir PHP dosyasını, yani index.php'yi hedefler. İsteğin https://example.com/product/123 adresine geldiğini varsayalım. Uygun sunucu yapılandırması sayesinde bu URL de index.php dosyasına eşlenir ve o dosya çalıştırılır.

Görevi şudur:

  1. ortamı hazırlamak
  2. factory'yi elde etmek
  3. isteği işleyen Nette uygulamasını çalıştırmak

Ne factory'si? Traktör üretmiyoruz, web siteleri yapıyoruz! Sabırlı olun, birazdan açıklanacak.

“Ortamın hazırlanması” derken, örneğin hataları günlüklemek veya görselleştirmek için harika bir araç olan Tracy'nin etkinleştirilmesini kastediyoruz. Üretim sunucusunda hataları günlükler, geliştirme ortamında ise doğrudan gösterir. Dolayısıyla hazırlık, sitenin üretim modunda mı yoksa geliştirme modunda mı çalıştığının belirlenmesini de kapsar. Nette bunun için akıllı otomatik algılama kullanır: siteyi localhost'ta çalıştırırsanız geliştirme modunda çalışır. Hiçbir şey yapılandırmanız gerekmez ve uygulama hem geliştirmeye hem de canlı yayına hemen hazırdır. Bu adımlar Bootstrap sınıfı bölümünde ayrıntılı olarak yapılır ve anlatılır.

Üçüncü nokta (evet, ikinciyi atladık, ama ona döneceğiz) uygulamanın başlatılmasıdır. Nette'te HTTP isteklerinin işlenmesinden Nette\Application\Application sınıfı (bundan sonra Application) sorumludur. Yani uygulamayı çalıştır dediğimizde, tam olarak bu sınıfın bir nesnesi üzerinde adı yerinde olan run() metodunu çağırmayı kastediyoruz.

Nette bir akıl hocası gibi davranır ve sizi kanıtlanmış yöntemlere göre temiz uygulamalar yazmaya yönlendirir. Bunların en yerleşiklerinden biri kısaca DI denilen bağımlılık enjeksiyonudur. Şu anda sizi DI'yi açıklayarak yormak istemiyoruz; bunun için ayrı bir bölüm var. Önemli sonucu şudur: anahtar nesneler genellikle DI konteyneri (veya DIC) olarak bilinen bir nesne factory'si tarafından oluşturulur. Evet, daha önce sözü geçen factory budur. Application nesnesini de bizim için o üretir, bu yüzden önce konteynere ihtiyacımız var. Onu Configurator sınıfıyla elde ederiz, Application nesnesini oluşturmasını sağlarız, üzerinde run() metodunu çağırırız ve böylece Nette uygulaması başlar. Tam olarak bu, index.php dosyasında olan şeydir.

Nette Application

Application sınıfının tek bir görevi vardır: HTTP isteğine yanıt vermek.

Nette'te yazılan uygulamalar, presenter denilen birçok parçaya bölünür (başka framework'lerde “controller” terimiyle karşılaşabilirsiniz, aslında aynı şeydir). Bunlar, her biri sitenin belirli bir sayfasını temsil eden sınıflardır: örneğin ana sayfa, bir e-ticaret sitesindeki ürün, giriş formu, site haritası beslemesi vb. Bir uygulamada birden binlerce presenter olabilir.

Application, geçerli isteği hangi presenter'ın işleyeceğine karar vermesi için önce router denilen bileşene sorar. Sorumluluğu router belirler. Girdi olarak gelen https://example.com/product/123 URL'sini inceler ve yapılandırmasına göre bu işin örneğin Product presenter'ına ait olduğuna, onun da id: 123 olan ürün için show eylemini gerçekleştirmesi gerektiğine karar verir. Presenter + eylem çiftini iki nokta üst üste ile ayırarak yazmak iyi bir alışkanlıktır, örneğin Product:show.

Böylece router, URL'yi Presenter:eylem çiftine + parametrelere dönüştürdü; bizim durumumuzda Product:show + id: 123. Böyle bir router'ın neye benzediğini app/Core/RouterFactory.php dosyasında görebilirsiniz, ayrıntılı olarak da Yönlendirme bölümünde anlatıyoruz.

Devam edelim. Application artık presenter'ın adını biliyor ve ilerleyebilir. Bunu, Product presenter'ının kodunu içeren ProductPresenter sınıfının bir örneğini oluşturarak yapar. Daha doğrusu, presenter'ı oluşturmasını DI konteynerinden ister, çünkü nesne oluşturmak onun işidir.

Presenter şöyle görünebilir:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $repository,
	) {
	}

	public function renderShow(int $id): void
	{
		// model'den veriyi al ve şablona aktar
		$this->template->product = $this->repository->getProduct($id);
	}
}

İsteğin işlenmesini presenter devralır. Görev açıktır: id: 123 ile show eylemini yürütmek. Presenter terminolojisinde bu, $id parametresinde 123 alan renderShow() metodunun çağrılması demektir.

Bir presenter birden fazla eylemi işleyebilir, yani birden fazla render<Eylem>() metoduna sahip olabilir. Ancak presenter'ları tek veya olabildiğince az eylemle tasarlamanızı öneririz.

Böylece renderShow(123) metodu çağrıldı. Kodu hayali bir örnektir, ama verinin şablona nasıl aktarıldığını, yani $this->template'e yazılarak aktarıldığını gösterir.

Ardından presenter bir yanıt döndürür. Bu bir HTML sayfası, bir görsel, bir XML belgesi, diskten bir dosyanın gönderilmesi, JSON veya belki başka bir sayfaya yönlendirme olabilir. Önemlisi, nasıl yanıt verileceğini açıkça belirtmezsek (ki ProductPresenter'da durum budur), yanıt bir şablonun HTML sayfasına render edilmesi olacaktır. Neden? Çünkü vakaların %99'unda bir şablon render etmek isteriz. Bu yüzden presenter, işimizi kolaylaştırmak için bu davranışı varsayılan olarak benimser. Nette'in özü budur.

Hangi şablonun render edileceğini belirtmemiz bile gerekmez; framework yolu kendiliğinden çıkarır. show eylemi durumunda, ProductPresenter sınıfıyla aynı dizinde yer alan show.latte şablonunu yüklemeyi dener. Layout'u da @layout.latte dosyasında bulmaya çalışır (şablon arama hakkında daha fazla ayrıntı).

Sonra şablonlar render edilir. Bununla presenter'ın ve tüm uygulamanın görevi tamamlanır. Şablon yoksa 404 hata sayfası döndürülür. Presenter'lar hakkında daha fazlasını Presenter'lar sayfasında öğrenebilirsiniz.

Emin olmak için, tüm süreci biraz farklı bir URL ile özetleyelim:

  1. URL https://example.com
  2. Uygulama başlar, DI konteyneri oluşturulur ve Application::run() çalıştırılır.
  3. Router, URL'yi Home:default çiftine çözer.
  4. HomePresenter sınıfının bir örneği oluşturulur.
  5. renderDefault() metodu çağrılır (varsa).
  6. Şablon, örneğin default.latte, layout ile birlikte, örneğin @layout.latte, render edilir.

Az önce birçok yeni kavramla karşılaşmış olabilirsiniz, ama bunların anlamlı olduğuna inanıyoruz. Nette'te uygulama geliştirmek son derece basittir.

Şablonlar

Şablonlardan söz açılmışken, Nette Latte şablon sistemini kullanır. Bu yüzden şablon dosyalarının uzantısı .latte'dir. Latte, hem PHP için en güvenli şablon sistemi olduğu hem de en sezgisel olduğu için tercih edilir. Yeni çok şey öğrenmeniz gerekmez; PHP bilgisi ve birkaç etiket yeterlidir. İhtiyacınız olan her şeyi dokümantasyonda bulacaksınız.

Şablonda başka presenter'lara ve eylemlere şöyle bağlantı oluşturursunuz:

<a n:href="Product:show $productId">ürün detayı</a>

Gerçek URL yerine tanıdık Presenter:eylem çiftini yazın ve gerekli parametreleri ekleyin. İşin püf noktası, Nette'e bu niteliği işlemesini söyleyen n:href'tir. Şunu üretecektir:

<a href="/product/456">ürün detayı</a>

URL üretimini yukarıda sözü geçen router üstlenir. Nette'teki router'lar olağanüstüdür, çünkü yalnızca URL'den Presenter:eylem çiftine dönüşümü değil, tersini de yapabilirler: presenter adından, eylemden ve parametrelerden URL üretebilirler. Bu sayede, Nette'te tamamlanmış bir uygulamanın tümündeki URL formatını, şablonlarda veya presenter'larda tek bir karakteri bile değiştirmeden, yalnızca router'ı değiştirerek tamamen dönüştürebilirsiniz. Bu ayrıca, aynı içeriğin farklı URL'lerde var olmasını otomatik olarak engelleyerek SEO'yu (arama motoru optimizasyonunu) iyileştiren, Nette'e özgü bir başka özellik olan kanonikleştirmeyi mümkün kılar. Birçok programcı bu yeteneği hayranlıkla karşılıyor.

Etkileşimli bileşenler

Presenter'lar hakkında size bir şey daha söylememiz gerekiyor: yerleşik bir bileşen sistemine sahiptirler. Daha deneyimli olanlar buna benzer bir şeyi Delphi'den veya ASP.NET Web Forms'tan hatırlayabilir; React veya Vue.js de bir ölçüde ilgili kavramlar üzerine kurulmuştur. PHP framework'leri dünyasında bu tamamen eşsiz bir özelliktir.

Bileşenler, sayfalara (yani presenter'lara) gömdüğümüz bağımsız, yeniden kullanılabilir birimlerdir. Bunlar formlar, datagrid'ler, menüler, anketler, kısacası yeniden kullanmanın anlamlı olduğu her şey olabilir. Kendi bileşenlerimizi oluşturabilir veya açık kaynak bileşenlerin geniş seçkisinden yararlanabiliriz.

Bileşenler, uygulama geliştirmeye yaklaşımı temelden etkiler. Sayfaları önceden hazırlanmış birimlerden oluşturmak için yeni olanaklar açarlar. Ve Hollywood ile de ortak bir yanları vardır.

DI konteyneri ve yapılandırma

DI konteyneri, yani nesne factory'si, tüm uygulamanın kalbidir.

Merak etmeyin, önceki satırların düşündürebileceği gibi sihirli bir kara kutu değildir. Gerçekte, Nette tarafından üretilen ve önbellek dizininde saklanan oldukça sıradan bir PHP sınıfıdır. createServiceAbcd() gibi adlandırılmış, her biri belirli bir nesneyi oluşturup döndürebilen birçok metot içerir. Evet, uygulamayı çalıştırmak için index.php'de ihtiyaç duyduğumuz Nette\Application\Application örneğini üreten bir createServiceApplication__application() metodu da vardır. Ayrıca tek tek presenter'ları oluşturan metotlar da bulunur, vb.

DI konteynerinin oluşturduğu nesnelere, bir nedenle, servis denir.

Bu sınıfın gerçekten özel yanı, onu sizin programlamamanızdır; framework programlar. Gerçekten PHP kodunu üretir ve diske kaydeder. Siz yalnızca konteynerin hangi nesneleri ve tam olarak nasıl oluşturabilmesi gerektiğine dair talimat verirsiniz. Bu talimatlar yapılandırma dosyalarına yazılır; bu dosyalar NEON formatını kullanır ve bu yüzden .neon uzantısı taşır.

Yapılandırma dosyaları yalnızca DI konteynerine talimat vermeye yarar. Yani örneğin session bölümünde expiration: 14 days seçeneğini belirtirseniz, DI konteyneri oturumu temsil eden Nette\Http\Session nesnesini oluştururken onun setExpiration('14 days') metodunu çağırır ve böylece yapılandırmayı gerçeğe dönüştürür.

Neyin yapılandırılabileceğini ve kendi servislerinizi nasıl tanımlayacağınızı anlatan koca bir bölüm sizi bekliyor.

Servis oluşturmaya biraz daldığınızda autowiring terimiyle karşılaşacaksınız. Bu, hayatınızı inanılmaz kolaylaştıracak bir özelliktir. Nesneleri, siz hiçbir şey yapmadan, ihtiyaç duyduğunuz yere (örneğin sınıflarınızın constructor'larına) otomatik olarak aktarabilir. Nette'teki DI konteynerinin küçük bir mucize olduğunu keşfedeceksiniz.

Sırada ne var?

Nette uygulamalarının temel ilkelerini ele aldık. Şimdilik yüzeysel bir bakış oldu, ama yakında daha derine inecek ve zamanla harika web uygulamaları oluşturacaksınız. Peki bundan sonra nereye? İlk uygulamanızı oluşturun eğitimini denediniz mi?

Yukarıda anlatılanların yanı sıra Nette, koca bir kullanışlı sınıf cephaneliği, bir veritabanı katmanı ve daha fazlasını sunar. Dokümantasyonu tıklayarak gezmeyi deneyin. Ya da blog'a uğrayın. Birçok ilginç şey keşfedeceksiniz.

Framework size bol keyif getirsin 💙

versiyon: 4.x