Twig Örnek İşlemleri
Grav temalarında Twig şablon motorunun kullanıldığından daha önce bahsetmiştim.
Bu yazıda Grav ile ilgili temel bilgiler verdiğim (dosya yapısı, konfigürasyon, sayfa tipleri vb.) yazının da bir uzantısı olarak çeşitli Twig kullanımlarına değineceğim. Bu yazıda yer alan pratikler Grav teması geliştirirken kullanılmak üzere referans görevi görecekler.
Aşağıdaki örneklerin çalışabilmesi için çalışma ortamınızda Twig’in kurulu olması gerekmekte. Daha önce macOS işletim sisteminde PHP kodlarının doğrudan nasıl çalıştırılabileceğine değinmiştim. Linux dağıtımlarında da benzer seçenekler mevcut. Önemli bir hatırlatma; Twig PHP 7.2.0 ve üzeri bir sürüm gereksinimine sahip.
O halde composer
aracılığıyla Twig kurulumuna başlayabiliriz.
composer require "twig/twig:^3.0"
Kurulumun tamamlanmasının ardından örnekleri uygulamaya başlayabilirsiniz. Şayet, Grav üzerinden örnekleri uygulayacaksanız Twig kurulumuna ihtiyacınız yok. Grav hali hazırda bu kuruluma sahip. Doğrudan kodları çalıştırmaya başlayabilirsiniz. Temel bazı hatırlatmalarla başlayalım.
Twig komutları belli bir uzantı kısıtlaması olmaksızın, normal birer metin dosyası (html, php, …) içerisinde yer alır. Genelde Grav temaları içerisinde dosyaadi.html.twig
şeklinde karşınıza çıkacaklardır. Bir şablon değişkenler ve ifadeler içerir. Bu değişkenler ve ifadeler veri ile birlikte şablon motoru tarafından işlenir ve sonuçları uygulayarak dosyanın son halini görüntülememizi sağlar. Aşağıda, bu süreci özetleyen temel bir örnek yer almakta. Kodları ayrıca TwigFiddle1 aracılığıyla da test edebilirsiniz. Yazım önerileri için Coding Standards2 sayfasına göz atabilirsiniz.
<!DOCTYPE html>
<html>
<head>
<title>{{ page.title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
<ul id="navigation">
{% for item in navigation %}
<li>{{ loop.index }}<a href="{{ item.href }}" title="{{ item.caption }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
</body>
</html>
Değişkenler (Variables)
Twig {{...}}
ile değişkenleri kullanabilmemizi sağlar. Grav temaları geliştirilirken pek çok ön tanımlı değişkeni temaya ait blueprint.yaml
dosyasında tutarız. Bu değişkenlere {{ theme.foo }}
şeklinde ulaşabiliriz3. Unutmadan, süslü parantezler değişken ifadesinin bir parçası değildir, yazdırma (print) işlemi için kullanılırlar.
{{ url("theme://assets/img/image.png") }}
Yukarıdaki örnekte url
fonksiyonu içerisindeki theme://
aktif temanın yolunu vermektedir. Dolayısıya print
sonucunda elimizde image.png
görselinin tam yolu (relative path) olacaktır. Aynı işlemi eklentiler için plugin://
ve sayfalar için page://
olarak kullanırız.
Mantıksal işlemler diğer bir ifade edile kontrol yapıları için ise tanımlarımızı {% ... %}
ile gerçekleştiririz. Güncel Twig etiketleri ve tüm mantıksal işlemler için doc/3.x4 sayfasını kullanabilirsiniz.
{% do assets.addCss('plugin://assets/css/spectre.css') %}
{% do assets.addJs('page://01.blog/assets/example.js', {loading: 'async', id: 'custom-css'}) %}
assets.addCss
ve assets.addJs
kullanımına ayrıca değineceğim. Şimdilik, kendi değişkenlerimizi kod içerisinde nasıl tanımlayabileceğimize bakalım.
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
Bir de kontrol ekleyelim. Navigasyon içerisinde aktif menü başlığını işaretlemek için şöyle bir kullanım yoluna gidebilir ve active
ifadesini bir class
olarak atayabiliriz.
<a href="{{ page.url }}" class="{% set current_page = (p.active or p.activeChild) ? 'active' : '' %}">...</a>
Filtreler (Filters)
Değişkenlere filtreler aracılığıyla müdahale edebilmekteyiz. Filtre tanımını değişkenden sonra ekleyeceğimiz pipe (|) sembolü ile başlatırız. Birden fazla filtreyi de yine bu şekilde uygulanmasını istediğimiz sıra ile ekleriz. Bu sayede bir filtreden geçen veri bir sonraki filtreye aktarılır.
{% set name = ' [My Twig Code](#)' %}
{{ name|striptags|title }}
Yukarıdaki satırda yer alan name
değişkeni strpitags
5 ile SGML/XML etiketlerinden arındırılır ve içeriğindeki bitişik boşluklar tek boşluk haline getirilir. Ardından, title
6 filtresi devreye girer ve değişkedeki kelime(ler)in ilk harfi büyük hale getirilir. Bu işlemi apply
7 ile de gerçekleştirebiliriz.
{% apply striptags|title %}
<a href="#"><i class="far fa-clock"></i> <span>My Twig Code</span></a>
{% endapply %}
Listeleme ile ilgili örnekler oluşturalım.
{% set pagination = {
'text1': 'link1',
'text2': 'link2',
} %}
{% if pagination|length > 1 %}
<ul>
{% for key,value in pagination %}
<li><a href="{{ value }}">{{ key }}</a></li>
{% endfor %}
</ul>
{% endif %}
Yukarıdaki kodda pagination
içeriğinde 1’den fazla değer var ise değerlerin liste halinde sunulması sağlanmaktadır. Liste for
döngüsü ile key
ve value
‘nun ilgili alanlara eklenmesi ile oluşur. Listeler, navigasyon, blog listeleme gibi işlemlerde benzer şekilde kullanılabilir. Şimdi, dizimizi listeleyelim.
{% set list = [1, 'domain.com/page/id?q=1', 'string', 4.0, '5#1', '6'] %}
# örnek 1
{{ list|escape('url')|join(', ') }}
# örnek 2
{% for i in list %}
<a href="#{{ i }}">{{ i|escape('url') }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
Her iki örnekte de değerler escape
8 filtresi ile işlenmektedir. İlk örnekte list
içeriği doğrudan değerlerin arasına virgül koyularak verilecektir. 2. örnekte ise list içeriği loop variable
ile birlikte kullanılmakta. Döngü boyunca virgül eklenmekte ancak döngünün son değerinden sonra if
9 ile birlikte kullanılan loop.last
condition ile virgül eklenmesi önlenmektedir.
Grav temaları oluştururken eklenen görselleri içerikler içerisinde kullanmak istediğimizde dizi olarak veri bize iletilecektir10 11 12 13. Bu tür durumlarda genelde ilk görseli first
14 ya da last
15 ile çekip kullanırız.
{% if p.media.images %}
{% set thumbUrl = p.media.images|first.quality(80).cropZoom(800,450).url %}
<a href="{{p.url}}" title="{{ p.title }}"><img class="card-img rounded-0" src="{{ thumbUrl }}" alt="{{ p.title }}" /></a>
{% endif %}
Örnekte yer alan quality
, cropZoom
ve url
Grav’a ait fonksiyonlar. Şimdilik aşina olmanız yeterli. Grav teması geliştirirken detaylarına değineceğim.
Fonksiyonlar (Functions)
İçeriğin sunumunda fonksiyonlardan16 faydalanabiliriz. Aşağıdaki örnekte range
17 fonksiyonu ile belirtilen sayı aralığını döngü olarak kullanabilmekteyiz. Aşağıdaki örnekte print
tanımında -
(dash) işaretini görmektesiniz18. Dash ile twig tarafından eklenen boşlukların eklenmemesini sağlayabiliriz11.
{% set string = 'number' %}
{% for i in range(0, 3) %}
{{- i ~ '.' ~ string -}}
{% endfor %}
Zaman ve zaman dilimi ile ilgili örneklere bakalım.
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
{{ "now"|date(timezone="UTC") }}
Kendi fonksiyonlarınızı oluşturmak isterseniz Twig Extension19 bölümüne göz atabilirsiniz.
Kontrol Yapıları (Control Structure)
Bir kontrol yapısı, şablondaki mantıksal akışı kontrol eden tüm şeyleri ifade eder. Örneğin, koşullar (if / elseif / else), döngüler (for) için ve bloklar (block) gibi şeyleri bu yapı içerisinde değerlendirebiliriz. Yazının giriş bölümünde de belirttiğim üzere, bu işlemler {% ... %}
içinde ele alınır.
If / Elseif / Else
Şablonlar içerisinde şartlı durumları ele almamızı sağlar. starts with
, ends with
ve matches
gibi operatörler20 ve ifadeler ile birlikte21 kullanılabilirler.
{% set word = 'Lorem, ipsum, dolor, sit, amet' %}
{% for w in word|replace({' ':''})|split(',') %}
{% apply spaceless %}
{% if w starts with 'i' %}
{{ w|e }} starts with i
{% endif %}
{% endapply %}
{% if w ends with 't' %}
{{ w|e }} ends with t
{% endif %}
{% if w matches '/^[a-z]+$/' %}
{{ w|e }} matched
{% endif %}
{% if w|length < 5 and w == 'sit' %}
{{ w|upper }} length is {{ w|length }}
{% endif %}
{% endfor %}
For
Twig şablonlarında döngüler için for
22 kullanılır.
{% set fruits = ['apple', 'orange', 'citrus'] %}
{% for i in 0..10 %}
{{ cycle(fruits, i) }}
{% endfor %}
Ancak bu şart değil. in
ile de true/false sonuçlarını alabilmekteyiz.
{{ 2 in [1, 2, 3] }}
{{ 'cd' in 'abcde' }}
{% if 2 not in [1, 2, 3] %}
{% if not (3 in [1, 2, 3]) %}
{{ 'cd' in 'abcde' ? 'yes' : 'no' }}
{{ 'cd' in 'abcde' ?: 'no' }} ya da {{ 'cd' in 'abcde' ? '' : 'no' }}
{{ 'cd' in 'abcde' ? 'yes' }} ya da {{ 'cd' in 'abcde' ? 'yes' : '' }}
Template İlişkileri (Include, Extend, Block)
Veriler üzerinde mantıksal işlemler yaptık. Ancak bir şablon sisteminden de bekleyeceğimiz üzere bu işlemleri tekrar tekrar yapmak manasız olacaktır; DRY.
Include
Hazırladığımız yapıları bir diğer dosya içerisine include
ile ekleyebiliriz.
{% include 'header.html' %}
{% for post in blog %}
{{ include('post.html') }}
{% endfor %}
{% include 'footer.html' %}
Include
içeriğine de müfahale edilebilmektedir. Örneğin, set
ile birlikte include
23 edilen dosya içeriğini bir değişkene atayabiliriz.
{% set content %}
{% include 'template.html' %}
{% endset %}
{# ya da #}
{% set content = include('template.html') %}
Ek olarak, filtreler ile müdahale edebiliriz.
{% filter upper %}
{% include 'template.html' %}
{% endfilter %}
{# ya da #}
{{ include('template.html')|upper }}
Ancak, include
işlemlerini statik olarak düşünmememiz gerekir. Include
ettiğimiz dosya içeriğine dışarıdan değerler iletebiliriz.
{% include 'template.html' with {'foo': 'bar'} %}
{# ya da #}
{% set vars = {'foo': 'bar'} %}
{% include 'template.html' with vars %}
{# kontroller de ekleyebiliriz #}
{% include 2 in [1, 2, 3] ? 'content.twig' : 'none.twig' %}
Son olarak, include
ile eklediğimiz dosyanın zorunluluk olmadığını da belirtmekte fayda var.
{% include 'sidebar.html' ignore missing %}
Block
Bloklar yer tutucu ve/veya değiştiriciler olarak ifade edilebilirler. Extend
başlığı altında örneklerine detaylarına yer vereceğim. Basit bir örnekle ele alalım.
<div id="content">{% block content %}Hello World!{% endblock %}</div>
content
adında bir block
tanımımız olsun ve bu dosyayı page.twig
gibi bir isimle kayıt edelim. Bir başka dosya içerisinden de extend
ile çağıralım.
{% extends "block.twig" %}
Bu satırın bulunduğu dosyanın adına da main.twig
diyelim. Bu dosya çalıştığında ekrana page.twig
içerisindeki Hello World!
metnini aktaracaktır. Çünkü biz block
için yeni bir değer iletmedik. Şimdi main.twig
dosyasına yeni bir satır daha ekleyelim.
{% block content %}It is a World!{% endblock %}
main.twig
dosyamızı yeniden çalıştırdığımızda Hello World!
yerine It is a World!
metninin geldiğini göreceksiniz. Çünkü bu metni kapsayan block
için yeni bir değer göndermiş olduk.
Extend
Az önceki örnekte de temel bir şekilde bahsi geçtiği üzere extend
24 şablonları genişletmemiz, içeriklerine müdahale edebilmemizi sağlar. Çoklu kullanılamazlar. Yani, child template
olarak ifade ettiğimiz ve block'lara yeni değerler gönderdiğimiz şablon içerisinde25 tek bir extend
tanımı yer alabilir. Ancak, çoklu yapılar için use
ile çözüm sağlanabilmektedir. Extend
ve use
için Twig: Örnek Şablon başlığına bakabilirsiniz26 27.
Makrolar
Makrolar, şablonlar içerisinde sıklıkla yinelenen alanları kendimizi tekrar etmeden dinamik bir şekilde kullanabilmemiz sağlarlar. Örnek olarak form elemanlarını değerlendirebiliriz. E-bülten, iletişim, teklif formu gibi farklı amaçlar için oluşturduğumuz formlar içerisinde çoğu zaman aynı tür, özellik ve niteliklerde elemanlar mevcut olacaktır. Bu gibi durumlarda macro
ile çözümler üretebilmekteyiz.
{% macro input(name, value, type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
{% macro textarea(name, value, rows = 10, cols = 40) %}
<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value|e }}</textarea>
{% endmacro %}
Makroları import
ile çağırabilmekteyiz.
{% import "forms.twig" as forms %}
Twig: Örnek Şablon
Aşağıdaki dosyayı base.html.twig
olarak kayıt edebiliriz.
<!DOCTYPE html>
<html>
<head>
{% block head %}
{% block head_css %}
{% do assets.add('theme://assets/css/style.css') %}
{{ assets.css()|raw }}
{% endblock %}
<title>{% block title %}{% endblock %}</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
{% use "blocks.twig" %}
<div id="footer">
{% block footer %}
© Copyright 2020 by x</a>.
{% endblock %}
</div>
{% block body_js %}
{% do assets.addJs('theme://assets/js/main.js') %}
{{ assets.js()|raw }}
{% endblock %}
</body>
</html>
Bu kodumuzu da page.html.twig
isimli bir dosya olarak kayıt altına alabiliriz.
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
{% block head_css %}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
It is a homepage.
</p>
{% endblock %}
Etiketler, fonksyonlar, filtreler ve operatörlerin tamamına Reference28 bölümünden ulaşabilirsiniz.
İleri Okumalar ve Örnekler
- Twig PHP Template
- Getting started with twig
- Templates with Twig
- Kendi Etkileyici Twig Şablonunuzu Oluşturun!
- Grav: Twig Primer
- TwigFiddle. Develop, run, stroe and access Twig code online ↩
- Coding Standards. Twig Docs ↩
- Theme Variables. Grav Documentation ↩
- Twig Documentation ↩
- striptags. Twig Docs ↩
- title. Twig Docs ↩
- apply. Twig Docs ↩
- escape. Twig Docs ↩
- if. Twig Docs ↩
- Grav: Twig Filters & Functions ↩
- Grav: Asset Manager ↩ ↩
- Grav: Theme Basics ↩
- Twig: Extending Twig ↩
- first. Twig Docs ↩
- slice. Twig Docs ↩
- Functions. Twig Docs ↩
- range. Twig Docs ↩
- Whitespace Control. Twig Docs ↩
- How to Write a custom Twig Extension. Symfony Documentation ↩
- Other Operators. Twig Docs ↩
- Comparisons. Twig Docs ↩
- for. Twig Docs ↩
- include. Twig Docs ↩
- extends. Twig Docs ↩
- Template Inheritance. Twig Docs ↩
- use. Twig Docs ↩
- macro. Twig Docs ↩
- Twig Reference. Twig Docs ↩