Template syntax v2
Template v2 is Zittme's recommended template syntax. It adds Zittme-specific features to Blade-style directives and remains compatible with v1 syntax in the same file.
Declaring the version
.htmlfiles:@version(2)(or<config version="2" />) on the first line.blade.phpfiles: v2 automatically (you can get IDE syntax highlighting)
@version(2)
<div class="my-skin">
<h1>{{ $title }}</h1>
</div>Output
| Syntax | Meaning |
|---|---|
{{ $var }} | Output with HTML escaping (default) |
{!! $var !!} | Output as is, without escaping |
{$var} | v1-compatible output |
Use {!! !!} only for HTML the core has already sanitized, such as editor content. Outputting user input as is creates XSS. Escaping is handled automatically and is aware of context (HTML/JS).
Filters
You can append |filter to output, and chain several of them.
{{ $title|upper }}
{{ $timestamp|date:'Y-m-d' }}
{{ $price|number_format }}
{{ $tags|join:', ' }}Main filters: autoescape escape noescape escapejs json strip_tags trim urlencode lower upper nl2br join:separator date:format number_format:decimals number_shorten link
Conditions
@if ($is_logged)
...
@elseif ($guest_allowed)
...
@else
...
@endifThere are also dedicated condition directives.
| Directive | Meaning |
|---|---|
@isset($var) ~ @endisset | If the variable exists |
@empty($var) ~ @endempty | If it is empty |
@admin ~ @endadmin | If the user is the top administrator |
@auth ~ @endauth | If logged in (@auth('manager') means management permission) |
@guest ~ @endguest | If not logged in |
@can('view') ~ @endcan | If the user has that permission (@cannot and @canany([...]) are also supported) |
@desktop / @mobile | Device detection |
Loops
@foreach ($list as $key => $item)
<li>{{ $item->title }}</li>
@endforeach
@forelse ($list as $item)
<li>{{ $item->title }}</li>
@empty
<li>항목이 없습니다</li>
@endforelse@for @while @switch/@case/@break/@default @continue are also supported.
Inside a loop you can use the $loop variable: $loop->index (from 0) $loop->iteration (from 1) $loop->count $loop->first $loop->last $loop->even $loop->odd $loop->depth $loop->parent
@foreach ($list as $item)
<li class="@if ($loop->first) is-first @endif">{{ $loop->iteration }}. {{ $item->title }}</li>
@endforeachHTML attribute helpers
<div @class(['base', 'is-on' => $active, 'is-mine' => $item->mine])>...</div>
<div @style(['color: red', 'display: none' => $hidden])>...</div>
<input type="checkbox" @checked($is_checked) />
<option value="1" @selected($val == 1)>하나</option>
<input @disabled($locked) @readonly($readonly) @required($must) />The v1 notation checked="checked"|cond="..." also keeps working.
PHP code
@php
$count = count($list);
$first = $count ? reset($list) : null;
@endphp
{@ $total = $count + 1; }- Template variables are shared state that refers to Context by default. Adding a backslash, as in
\$var, makes it a local variable of that template only. Use\$for closure parameters and use bindings as well. - Inside
{@ ... }you can also open and close blocks in theforeach (...):/endforeach;style.
Comments
{{-- 템플릿 주석: 출력에서 제거됩니다 --}}
<!--// v1 스타일 주석: 역시 제거됩니다 -->
<!-- 일반 HTML 주석: 출력에 남습니다 -->Caution: always write {{-- --}} comments on a single line. If one spans multiple lines, the parser may not remove it completely, which can cause a server error (500). This has caused real outages. Write multi-line explanations as HTML comments (<!-- -->).
Including other templates
@include('_header')
@include('sub/box')
@include('_card', ['title' => $t, 'body' => $b]) {{-- 변수 전달 --}}
<include src="_promo" if="$show_promo" /> {{-- 조건부 포함 --}}
<include src="_banner" unless="$is_admin" />- When you pass variables, the included template uses only the passed variables instead of Context (it works like a component).
- Conditional includes use the
if/when/cond/unlessattributes of the<include>tag. - The v1
<include target="..." />also works.
Loading resources: @load
@load('css/skin.css')
@load('css/skin.scss', $vars) {{-- SCSS 변수 전달 --}}
@load('js/skin.js')
@load('js/lazy.js', 'body') {{-- body 끝에서 로드 --}}
@load('^/common/js/plugins/URI.js') {{-- ^ 는 사이트 루트 --}}
@load('../lang/') {{-- 언어 파일 디렉터리 --}}
@unload('foo/bar.js') {{-- 로드 취소 --}}For CSS you can pass media and order, and for JS head/body and order, as extra arguments.
Building URLs
<a href="@url('act', 'dispMemberInfo')">내 정보</a>
<a href="{{ getUrl('', 'mid', $mid, 'act', 'dispBoardWrite') }}">글쓰기</a>@url(...) and getUrl(...) do the same thing. Just remember one rule.
- Links that only change parameters of the current page (pagination, sorting):
getUrl('page', $n)without an empty first argument - Links to a different screen: always pass
''as the first argument to build a new URL from scratch. Otherwise all the parameters of the current request come along.
If the action has a route declared, a short address is built automatically.
Other directives
| Directive | Meaning |
|---|---|
@csrf | Inserts a CSRF token field inside a form |
@json($array) | JS-safe JSON output (context-aware) |
@lang('key') | Outputs a language string (@lang('module.key')) |
@use('Namespace\Class', 'Alias') | Declares a class alias. Then use {{ Alias::method() }} |
@widget('widget_name', $args) | Inserts a widget |
@once ~ @endonce | Runs only once, even inside a loop |
@verbatim ~ @endverbatim | Template syntax is not interpreted in this section |
@dump($var) / @dd($var) | Debug output (dd stops after output) |
@push('name') ~ @endpush / @stack('name') | Collects content and outputs it in one place |
@error('validator_id') ~ @enderror | Shows form validation errors |
Not supported in v2
- Blade template inheritance (
@extends@yield@section) and slots (@slot@inject) - The v1
<block>tag, andloop/condattributes on arbitrary tags (the condition attributes of<include>are an exception)
Existing files that need v1 syntax can be left as they are. Avoid mixing both generations of syntax in one file.
Common pitfalls in practice
These items come from real outages.
- No multi-line
{{-- --}}comments (see the Comments section above). They are a frequent cause of server 500 errors. - When calling a static method directly in a template, declare an alias with
@useand call it through the alias. Calling the full namespace path directly inside an attribute value can break compilation and produce broken links like%7B...%7D. Passing values from the controller withContext::set()is the safest. Context::set()overwrites request variables with the same name. When reading form values in a proc action, reading$_POSTdirectly is safer.- CSS selector specificity:
.wrap a { color: inherit }in shared CSS is stronger than a single new.my-link. If your link color doesn't apply, raise the specificity, as in.wrap a.my-link.