فصل چهارم-قالب‌بندی با Blade

قالب‌ بندی با Blade

در مقایسه با اکثر زبان‌های بک‌اند، PHP در واقع به‌عنوان یک زبان قالب‌بندی عملکرد نسبتاً خوبی دارد. اما کاستی‌هایی دارد و همچنین استفاده از <?php به‌صورت درون‌خطی در سراسر کد ناخوشایند است، بنابراین انتظار می‌رود که اکثر فریمورک‌های مدرن یک زبان قالب‌بندی ارائه دهند.
Laravel یک موتور قالب‌بندی سفارشی به نام Blade ارائه می‌دهد که از موتور Razor در .NET الهام گرفته است. این موتور دارای سینتکسی مختصر، یک منحنی یادگیری سطحی، یک مدل ارث‌بری قدرتمند و شهودی، و قابلیت توسعه آسان است.
برای نگاهی سریع به نحوه نگارش قالب‌های Blade، به مثال ۴-۱ توجه کنید.
مثال ۴-۱. نمونه‌هایی از Blade

<h1>{{ $group->title }}</h1>
{!! $group->heroImageHtml() !!}

@forelse ($users as $user)
    • {{ $user->first_name }} {{ $user->last_name }}<br>
@empty
    No users in this group.
@endforelse

 

همان‌طور که مشاهده می‌کنید، Blade از آکولاد ({{ }}) برای "echo" استفاده می‌کند و یک قاعده کلی را معرفی می‌کند که در آن تگ‌های سفارشی آن، که "دستورالعمل" (directives) نامیده می‌شوند، با @ پیشوند می‌گیرند. شما از دستورالعمل‌ها برای تمامی ساختارهای کنترلی، ارث‌بری، و همچنین هر ویژگی سفارشی که می‌خواهید اضافه کنید، استفاده خواهید کرد.
سینتکس Blade تمیز و مختصر است، بنابراین در هسته خود خوشایندتر و منظم‌تر از سایر گزینه‌ها است. اما درست زمانی که در قالب‌های خود به ارث‌بری‌های تو‌در‌تو، شرط‌های پیچیده یا بازگشت (recursion) نیاز داشته باشید، Blade قدرت واقعی خود را نشان می‌دهد. درست مانند بهترین مؤلفه‌های Laravel، این موتور الزامات پیچیده اپلیکیشن را ساده و در دسترس می‌کند.
علاوه بر این، از آنجایی که تمام سینتکس‌های Blade به کد معمولی PHP کامپایل شده و سپس کش (cache) می‌شوند، عملکرد سریعی دارد و در صورت نیاز می‌توانید از کد PHP بومی در فایل‌های Blade خود استفاده کنید. با این حال، پیشنهاد می‌کنم تا حد امکان از استفاده از PHP در قالب‌های Blade خودداری کنید—معمولاً اگر نیاز به کاری دارید که نمی‌توان آن را با Blade یا یک دستورالعمل سفارشی Blade انجام داد، احتمالاً آن کار درون قالب جایگاهی ندارد.

 

استفاده از Twig با Laravel

برخلاف بسیاری از فریمورک‌های مبتنی بر Symfony، Laravel به‌صورت پیش‌فرض از Twig استفاده نمی‌کند. اما اگر به Twig علاقه‌مند هستید، یک پکیج به نام Twig Bridge وجود دارد که استفاده از Twig را به‌جای Blade در Laravel آسان می‌کند.

چاپ کردن داده‌ ها

همان&zwnj;طور که در مثال 4-1 مشاهده کردید، {{ و }} برای محصور کردن بخش&zwnj;هایی از PHP که می&zwnj;خواهید echo شوند، استفاده می&zwnj;شوند. عبارت {{ $variable }} مشابه در PHP خام است. اما یک تفاوت مهم وجود دارد که احتمالاً حدس زده&zwnj;اید: Blade تمام مقادیر echo را به&zwnj;صورت پیش&zwnj;فرض با استفاده از تابع htmlentities() در PHP ایمن&zwnj;سازی می&zwnj;کند تا از درج اسکریپت&zwnj;های مخرب جلوگیری شود. بنابراین، {{ $variable }} معادل در PHP است. اگر بخواهید بدون escape کردن مقدار را echo کنید، باید از {!! و !!} استفاده کنید. {{ و }} در هنگام استفاده از فریمورک&zwnj;های فرانت&zwnj;اند ممکن است متوجه...

برای دیدن ادامه محتوا وارد شوید

ساختارهای کنترلی

بیشتر ساختارهای کنترلی در Blade بسیار آشنا خواهند بود. بسیاری از آن‌ها دقیقاً نام و ساختار مشابهی با تگ‌های PHP دارند.

چند helper برای راحتی بیشتر وجود دارد، اما در کل، ساختارهای کنترلی در Blade خواناتر و تمیزتر از معادل‌هایشان در PHP هستند.

شرط‌ ها (Conditionals)

در ابتدا، به ساختارهایی که برای منطق برنامه&zwnj;نویسی استفاده می&zwnj;شوند، نگاهی می&zwnj;اندازیم.@ifBlade از @if ($condition) استفاده می&zwnj;کند که به کامپایل می&zwnj;شود.دستورات @else, @elseif و @endif نیز دقیقاً به همان سبک PHP ترجمه می&zwnj;شوند. مثال 4-2 را مشاهده کنید:مثال 4-2: استفاده از @if، @else، @elseif و @endif @if (count($talks) === 1) There is one talk at this time period. @elseif (count($talks) === 0) There are no talks at this time period. @else There are {{ count($talks) }} talks at this time period. @endif @unless and @endunless @unless در واقع یک سینتکس جدید است که معادل مستقیمی در PHP ندارد.این دستور معکوس...

برای دیدن ادامه محتوا وارد شوید

حلقه‌ ها (Loops)

در ادامه، نگاهی به حلقه‌ها می‌اندازیم.

@for, @foreach, و @while
دستورات @for, @foreach و @while در Blade دقیقاً مانند PHP کار می‌کنند. به مثال‌های 4-4، 4-5 و 4-6 توجه کنید.
مثال 4-4: استفاده از @for و @endfor

@for ($i = 0; $i < $talk->slotsCount(); $i++)
    The number is {{ $i }}<br>
@endfor

مثال 4-5: استفاده از @foreach و @endforeach

@foreach ($talks as $talk)
    • {{ $talk->title }} ({{ $talk->length }} minutes)<br>
@endforeach

مثال 4-6: استفاده از @while و @endwhile

@while ($item = array_pop($items))
    {{ $item->orSomething() }}<br>
@endwhile

 

@forelse و @endforelse
@forelse مشابه @foreach است، اما این امکان را می‌دهد که یک مقدار پیش‌فرض (fallback) را در صورتی که مقدار مورد تکرار خالی باشد، تعریف کنید.
این ساختار را در ابتدای این فصل دیدیم. در مثال 4-7 یک نمونه دیگر آورده شده است:
مثال 4-7: استفاده از @forelse

@forelse ($talks as $talk)
    • {{ $talk->title }} ({{ $talk->length }} minutes)<br>
@empty
    No talks this day.
@endforelse

 

متغیر $loop در @foreach و @forelse

دستورات @foreach و @forelse یک قابلیت اضافی دارند که در حلقه‌های foreach در PHP وجود ندارد: متغیر $loop.
هنگامی که درون @foreach یا @forelse استفاده شود، $loop یک شیء stdClass بازمی‌گرداند که شامل خصوصیات زیر است:
index

 مقدار 0-based اندیس آیتم فعلی در حلقه (۰ یعنی اولین آیتم)

iteration

 مقدار 1-based اندیس آیتم فعلی در حلقه (۱ یعنی اولین آیتم)

remaining

 تعداد آیتم‌های باقی‌مانده در حلقه

count

 تعداد کل آیتم‌های حلقه

first و last

متغیرهای boolean که مشخص می‌کنند این اولین یا آخرین آیتم در حلقه است

even و odd

متغیرهای boolean که مشخص می‌کنند این تکرار زوج یا فرد است

depth

 عمق حلقه: ۱ برای یک حلقه معمولی، ۲ برای حلقه تو در تو، و ...

parent

 ارجاعی به متغیر $loop حلقه والد اگر این حلقه درون یک @foreach دیگر باشد، در غیر این صورت مقدار null خواهد بود.

 

نمونه‌ای از استفاده از $loop

<ul>
@foreach ($pages as $page)
    <li>{{ $loop->iteration }}: {{ $page->title }}
        @if ($page->hasChildren())
        <ul>
        @foreach ($page->children() as $child)
            <li>{{ $loop->parent->iteration }}
                .{{ $loop->iteration }}:
                {{ $child->title }}</li>
        @endforeach
        </ul>
        @endif
    </li>
@endforeach
</ul>

 

Template Inheritance (ارث‌ بری قالب‌ ها)

Blade یک ساختار برای ارث‌بری قالب‌ها فراهم می‌کند که به ویوها اجازه می‌دهد تا قالب‌های دیگر را گسترش داده، تغییر دهند و شامل کنند.
بیایید نگاهی به نحوه ساختار ارث‌بری در Blade بیندازیم.

تعریف بخش‌ها با @section/@show و @yield

در ابتدا، یک قالب سطح بالا در Blade را مانند مثال 4-8 بررسی می&zwnj;کنیم. این یک صفحه&zwnj;ی قالب عمومی است که بعداً محتوای مخصوص به هر صفحه در آن قرار خواهد گرفت.مثال 4-8: قالب Blade &lt;!-- resources/views/layouts/master.blade.php --&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;My Site | @yield('title', 'Home Page')&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;div class="container"&gt; @yield('content') &lt;/div&gt; @section('footerScripts') &lt;script src="app.js"&gt;&lt;/script&gt; @show &lt;/body&gt; &lt;/html&gt; &nbsp; این کمی شبیه یک صفحه HTML معمولی به نظر می&zwnj;رسد، اما می&zwnj;بینید که در دو جا (title و content) از @yield استفاده کرده&zwnj;ایم و در سومی (footerScripts) یک بخش تعریف کرده&zwnj;ایم. در اینجا سه دستور Blade داریم: @yield('content') به&zwnj;تنهایی، @yield('title', 'Home...

برای دیدن ادامه محتوا وارد شوید

وارد کردن تکه های ویو

حالا که اصول ارث‌بری را مشخص کردیم، چند ترفند دیگر وجود دارد که می‌توانیم انجام دهیم.

@include
اگر در یک ویو باشیم و بخواهیم ویو دیگری را وارد کنیم، چه؟ شاید یک دکمه فراخوان “ثبت‌نام” داریم که می‌خواهیم در سرتاسر سایت از آن استفاده کنیم. و شاید بخواهیم متن دکمه را هر بار که از آن استفاده می‌کنیم، سفارشی کنیم. به مثال ۴-۱۰ نگاه کنید.

Example 4-10. Including view partials with @include

<!-- resources/views/home.blade.php -->
<div class="content" data-page-name="{{ $pageName }}">
    <p>Here's why you should sign up for our app: <strong>It's Great.</strong></p>

    @include('sign-up-button', ['text' => 'See just how great it is'])
</div>

<!-- resources/views/sign-up-button.blade.php -->
<a class="button button--callout" data-page-name="{{ $pageName }}">
    <i class="exclamation-icon"></i> {{ $text }}
</a>

@include قسمت جزئی را وارد می‌کند و به صورت اختیاری داده‌هایی را به آن منتقل می‌کند. توجه داشته باشید که نه تنها می‌توانید به طور صریح داده‌ها را از طریق پارامتر دوم @include به یک جزئیات منتقل کنید، بلکه می‌توانید به هر متغیری که در فایل وارد شده و برای ویو اصلی در دسترس است نیز اشاره کنید (در این مثال، $pageName). دوباره، شما می‌توانید هر کاری که بخواهید انجام دهید، اما پیشنهاد می‌کنم همیشه هر متغیری که قصد دارید استفاده کنید را به طور صریح منتقل کنید تا شفافیت بیشتری داشته باشد.
همچنین می‌توانید از دستورات @includeIf، @includeWhen و @includeFirst استفاده کنید، همانطور که در مثال ۴-۱۱ نشان داده شده است.

مثال 4-11. وارد کردن ویوها به‌صورت شرطی

{{-- Include a view if it exists --}}
@includeIf('sidebars.admin', ['some' => 'data'])

{{-- Include a view if a passed variable is truth-y --}}
@includeWhen($user->isAdmin(), 'sidebars.admin', ['some' => 'data'])

{{-- Include the first view that exists from a given array of views --}}
@includeFirst(['customs.header', 'header'], ['some' => 'data'])

@each 

شما احتمالاً می‌توانید برخی شرایطی را تصور کنید که در آن باید از یک آرایه یا مجموعه عبور کنید و برای هر آیتم یک جزئیات را با استفاده از @include وارد کنید. برای این کار یک دستور وجود دارد: @each.
فرض کنید یک نوار کناری داریم که از ماژول‌ها تشکیل شده است و می‌خواهیم چندین ماژول را وارد کنیم، هر کدام با عنوان متفاوت. به مثال ۴-۱۲ نگاه کنید.

مثال 4-12. استفاده از پارتشیال‌های ویو در یک حلقه با دستور @each

<!-- resources/views/sidebar.blade.php -->
<div class="sidebar">
    @each('partials.module', $modules, 'module', 'partials.empty-module')
</div>

<!-- resources/views/partials/module.blade.php -->
<div class="sidebar-module">
    <h1>{{ $module->title }}</h1>
</div>

<!-- resources/views/partials/empty-module.blade.php -->
<div class="sidebar-module">
    No modules :(
</div>

به سینتکس @each توجه کنید. پارامتر اول نام جزئیات نمایی است. پارامتر دوم آرایه یا مجموعه‌ای است که باید از آن عبور کرد. پارامتر سوم نام متغیری است که هر آیتم (در اینجا، هر عنصر در آرایه $modules) به نمای مربوطه ارسال خواهد شد. و پارامتر اختیاری چهارم نمایی است که در صورتی که آرایه یا مجموعه خالی باشد نمایش داده می‌شود (یا می‌توانید یک رشته را به عنوان قالب ارسال کنید).

استفاده از کامپوننت‌ ها

&nbsp;لاراول یک الگوی دیگر برای وارد کردن محتوا بین نماها ارائه می&zwnj;دهد: کامپوننت&zwnj;ها. کامپوننت&zwnj;ها بیشترین معنا را در موقعیت&zwnj;هایی دارند که شما از جزئیات نمایی استفاده کرده و قطعات بزرگی از محتوا را به عنوان متغیر به آن&zwnj;ها ارسال می&zwnj;کنید. به مثال ۴-۱۳ نگاه کنید که یک مدل یا پاپ&zwnj;آپ را نشان می&zwnj;دهد که ممکن است به&zwnj;عنوان واکنشی به خطا یا اقدام دیگر به کاربر هشدار دهد. مثال 4-12. استفاده از پارتشیال&zwnj;های ویو در یک حلقه با دستور @each &lt;!-- resources/views/partials/modal.blade.php --&gt; &lt;div class="modal"&gt; &lt;h2&gt;{{ $title }}&lt;/h2&gt; &lt;div&gt;{{ $content }}&lt;/div&gt; &lt;div class="close button etc"&gt;...&lt;/div&gt; &lt;/div&gt; &lt;!-- in another template --&gt;...

برای دیدن ادامه محتوا وارد شوید

استفاده از استک‌ ها

 یک الگوی رایج که مدیریت آن با استفاده از گنجاندن‌های ساده Blade ممکن است دشوار باشد، زمانی است که هر نمای موجود در یک سلسله‌مراتب Blade نیاز به اضافه کردن چیزی به یک بخش خاص دارد—​تقریباً مانند اضافه کردن یک ورودی به یک آرایه.
رایج‌ترین موقعیت برای این کار زمانی است که برخی صفحات (و گاهی اوقات، به‌طور کلی‌تر، بخش‌های خاصی از یک وب‌سایت) نیاز به بارگذاری فایل‌های CSS و JavaScript خاص و منحصر به فرد دارند. فرض کنید که شما یک فایل CSS جهانی برای سایت، یک فایل CSS برای بخش «شغل‌ها»، و یک فایل CSS برای صفحه «درخواست شغل» دارید.
استک‌های Blade دقیقاً برای همین موقعیت ساخته شده‌اند. در الگوی والد خود، یک استک تعریف کنید که فقط یک مکان‌نگهدار است. سپس، در هر الگوی فرزند می‌توانید ورودی‌هایی را با استفاده از @push/@endpush به آن استک «اضافه» کنید که آنها را به انتهای استک در رندر نهایی اضافه می‌کند. همچنین می‌توانید از @prepend/@endprepend برای اضافه کردن آنها به بالای استک استفاده کنید. مثال 4-19 این موضوع را نشان می‌دهد.

مثال 4-19. استفاده از استک‌های Blade

<!-- resources/views/layouts/app.blade.php -->
<html>
<head><!-- the head --></head>
<body>
    <!-- the rest of the page -->
    <script src="/css/global.css"></script>
    <!-- the placeholder where stack content will be placed -->
    @stack('scripts')
</body>
</html>

<!-- resources/views/jobs.blade.php -->
@extends('layouts.app')

@push('scripts')
    <!-- push something to the bottom of the stack -->
    <script src="/css/jobs.css"></script>
@endpush

<!-- resources/views/jobs/apply.blade.php -->
@extends('jobs')

@prepend('scripts')
    <!-- push something to the top of the stack -->
    <script src="/css/jobs--apply.css"></script>
@endprepend

این‌ها نتیجه زیر را تولید می‌کنند:

<html>
<head><!-- the head --></head>
<body>
    <!-- the rest of the page -->
    <script src="/css/global.css"></script>
    <!-- the placeholder where stack content will be placed -->
    <script src="/css/jobs--apply.css"></script>
    <script src="/css/jobs.css"></script>
</body>
</html>

 

کامپوزرهای ویو و تزریق سرویس

&nbsp;همانطور که در فصل ۳ پوشش دادیم، انتقال داده&zwnj;ها به ویوها از طریق تعریف روت ساده است (به مثال ۴-۲۰ مراجعه کنید). مثال 4-20. یادآوری نحوه ارسال داده به ویوها Route::get('passing-data-to-views', function () { return view('dashboard') -&gt;with('key', 'value'); }); ممکن است زمانی پیش بیاید که مجبور شوید داده&zwnj;های مشابهی را بارها و بارها به چندین ویو منتقل کنید. یا ممکن است از یک پارشیال هدر یا چیزی مشابه آن استفاده کنید که به داده&zwnj;هایی نیاز دارد؛ آیا باید آن داده&zwnj;ها را از هر تعریف روتی که ممکن است آن پارشیال هدر را بارگذاری کند، منتقل کنید؟

برای دیدن ادامه محتوا وارد شوید

بایند کردن داده‌ها به ویوها با استفاده از کامپوزرهای ویو

خوشبختانه راه ساده‌تری وجود دارد. راه‌حل به نام کامپوزر ویو است و این امکان را به شما می‌دهد که هر زمان یک ویو خاص بارگذاری شد، داده‌های معینی به آن منتقل شود—بدون اینکه تعریف روت مجبور باشد آن داده‌ها را به‌طور صریح ارسال کند.
فرض کنید شما یک سایدبار در هر صفحه دارید که در یک پارشیال به نام partials.sidebar (resources/views/partials/sidebar.blade.php) تعریف شده است و سپس در هر صفحه گنجانده می‌شود. این سایدبار لیستی از آخرین هفت پستی که در سایت شما منتشر شده را نشان می‌دهد. اگر این سایدبار در هر صفحه باشد، هر تعریف روت معمولاً باید آن لیست را بگیرد و آن را به ویو ارسال کند، مانند مثال ۴-۲۱.

مثال 4-21. ارسال داده‌های سایدبار از هر مسیر (route)

Route::get('home', function () {
    return view('home')
        ->with('posts', Post::recent());
});

Route::get('about', function () {
    return view('about')
        ->with('posts', Post::recent());
});

این ممکن است به‌سرعت آزاردهنده شود. به‌جای این کار، ما از کامپوزرهای ویو استفاده خواهیم کرد تا آن متغیر را با مجموعه‌ای از ویوها به اشتراک بگذاریم. ما می‌توانیم این کار را به چندین روش انجام دهیم، پس بیایید از ساده‌ترین روش شروع کنیم و به تدریج پیش برویم.
به اشتراک‌گذاری یک متغیر به‌طور سراسری
اولین گزینه، ساده‌ترین روش: صرفاً یک متغیر را به‌طور سراسری با هر ویو در اپلیکیشن خود به اشتراک بگذارید، مانند مثال ۴-۲۲.

مثال 4-22. اشتراک‌گذاری یک متغیر به‌صورت سراسری

// Some service provider
public function boot()
{
    ...
    view()->share('recentPosts', Post::recent());
}

اگر می‌خواهید از view()->share() استفاده کنید، بهترین مکان برای این کار متد boot() یک سرویس‌پروایدر است تا این اتصال در هر بار بارگذاری صفحه انجام شود. شما می‌توانید یک سرویس‌پروایدر سفارشی به نام ViewComposerServiceProvider ایجاد کنید (برای اطلاعات بیشتر در مورد سرویس‌پروایدرها به فصل ۱۱ مراجعه کنید)، اما در حال حاضر فقط آن را در App\Providers\AppServiceProvider در متد boot() قرار دهید.
با استفاده از view()->share() متغیر برای هر ویو در سراسر اپلیکیشن در دسترس خواهد بود، بنابراین ممکن است بیش از حد باشد.
کامپوزرهای ویو با دامنه ویو با استفاده از closures
 گزینه بعدی استفاده از یک کامپوزر ویو مبتنی بر closure است تا متغیرها را با یک ویو خاص به اشتراک بگذارید، مانند مثال ۴-۲۳.

مثال 4-23. ایجاد یک View Composer مبتنی بر Closure

view()->composer('partials.sidebar', function ($view) {
    $view->with('recentPosts', Post::recent());
});

همانطور که می‌بینید، ما نام ویوی مورد نظر برای به اشتراک گذاشتن را در پارامتر اول (مثلاً partials.sidebar) تعریف کرده‌ایم و سپس یک closure به پارامتر دوم پاس داده‌ایم؛ در این closure از متد $view->with() استفاده کرده‌ایم تا یک متغیر را فقط با یک ویو خاص به اشتراک بگذاریم.

View Composerها برای چندین ویو (چندین نما)

هرجا که یک view composer به یک view خاص متصل می‌شود (مثل Example 4-23 که به partials.sidebar متصل است)، می‌توانید به جای آن یک آرایه از نام‌های ویو را پاس بدهید تا به چندین ویو متصل شوید.
همچنین می‌توانید از یک آستریکس در مسیر ویو استفاده کنید، مانند partials.* یا tasks.* :

view()->composer(
    ['partials.header', 'partials.footer'],
    function ($view) {
        $view->with('recentPosts', Post::recent());
    }
);

view()->composer('partials.*', function ($view) {
    $view->with('recentPosts', Post::recent());
});

 

View Composerهای محدود به یک ویو با استفاده از کلاس‌ها

همانطور که می‌بینید، زمانی که این composer فراخوانی می‌شود، متد compose() اجرا می‌شود، که در آن متغیر recentPosts به نتیجه اجرای متد recent() مدل Post متصل می‌شود.
مانند سایر روش‌های اشتراک‌گذاری متغیرها، این view composer نیاز به یک binding در جایی دارد. دوباره، احتمالاً شما یک custom ViewComposerServiceProvider ایجاد خواهید کرد، اما برای اکنون، همانطور که در Example 4-25 مشاهده می‌کنید، فقط آن را در متد boot() از App\Providers\AppServiceProvider قرار می‌دهیم.

مثال 4-25. ثبت یک View Composer در AppServiceProvider

public function boot(): void
{
    view()->composer(
        'partials.sidebar',
        \App\Http\ViewComposers\RecentPostsComposer::class
    );
}

توجه داشته باشید که این binding مشابه یک view composer مبتنی بر closure است، اما به جای پاس دادن یک closure، نام کلاس view composer خود را پاس می‌دهیم. حالا، هر بار که Blade ویو partials.sidebar را رندر می‌کند، به طور خودکار provider ما اجرا خواهد شد و متغیر recentPosts را که به نتایج متد recent() از مدل Post تنظیم شده است، به ویو منتقل می‌کند.

تزریق سرویس در Blade (Blade Service Injection)

سه نوع داده اصلی وجود دارد که احتمالاً ما به یک ویو تزریق می&zwnj;کنیم: مجموعه&zwnj;های داده برای تکرار، اشیاء منفرد که می&zwnj;خواهیم در صفحه نمایش دهیم، و سرویس&zwnj;هایی که داده&zwnj;ها یا ویوها را تولید می&zwnj;کنند.با یک سرویس، الگو احتمالاً شبیه به مثال 4-26 خواهد بود، جایی که یک نمونه از سرویس تجزیه و تحلیل خود را از طریق نوع&zwnj;دهی به متد در تعریف روت تزریق کرده و سپس آن را به ویو منتقل می&zwnj;کنیم. مثال 4-26. تزریق سرویس&zwnj;ها به یک ویو از طریق سازنده&zwnj;ی تعریف مسیر (Route Definition Constructor) Route::get('backend/sales', function (AnalyticsService $analytics) { return view('backend.sales-graphs') -&gt;with('analytics', $analytics); }); همانطور...

برای دیدن ادامه محتوا وارد شوید

دستورات Blade سفارشی

تمام سینتکس‌های داخلی Blade که تاکنون بررسی کردیم—@if، @unless و غیره—به عنوان دستورها شناخته می‌شوند. هر دستور Blade یک نگاشت بین یک الگو (مثلاً @if ($condition)) و یک خروجی PHP (مثلاً ) است.
دستورات فقط برای هسته نیستند؛ شما می‌توانید دستورهای خودتان را نیز ایجاد کنید. شاید فکر کنید دستورات برای ایجاد میانبرهایی به کدهای بزرگ‌تر مناسب هستند—مثلاً استفاده از @button('buttonName') و تبدیل آن به مجموعه‌ای بزرگتر از کد HTML مربوط به دکمه. این ایده بدی نیست، اما برای گسترش کد ساده مانند این، شاید بهتر باشد که یک partial view را شامل کنید.
دستورات سفارشی بیشتر زمانی مفید هستند که برخی از اشکال منطق تکراری را ساده کنند. فرض کنید از این که مجبوریم کد خود را با @if (auth()->guest()) (برای بررسی این که آیا کاربر وارد شده است یا نه) بپیچیم خسته شده‌ایم و می‌خواهیم دستور سفارشی @ifGuest ایجاد کنیم. همانند view composers، ممکن است ارزش داشته باشد که یک سرویس پرووایدر سفارشی برای ثبت این‌ها داشته باشیم، اما برای حالا، اجازه دهید که آن را فقط در متد boot() در App\Providers\AppServiceProvider قرار دهیم. به مثال 4-29 نگاه کنید تا ببینید این نگاشت چگونه خواهد بود.

مثال 4-29. متصل  کردن یک دستور سفارشی Blade در یک Service Provider

public function boot(): void
{
    Blade::directive('ifGuest', function () {
        return "<?php if (auth()->guest()): ?>";
    });
}

ما اکنون یک دستور سفارشی به نام @ifGuest ثبت کرده‌ایم که با کد PHP جایگزین خواهد شد.
<?php if (auth()->guest()): ?>.
این ممکن است کمی عجیب به نظر برسد. شما یک رشته می‌نویسید که سپس به عنوان PHP اجرا خواهد شد. اما این به این معنی است که اکنون می‌توانید جنبه‌های پیچیده، زشت، یا غیرقابل فهم، یا تکراری کد قالب‌بندی PHP خود را پنهان کرده و آن‌ها را پشت سینتکس‌های واضح، ساده و گویا قرار دهید.

کش کردن نتیجه دستور سفارشی

ممکن است وسوسه شوید که با انجام برخی عملیات در داخل دستور، عملکرد آن را سریع‌تر کنید و سپس نتیجه را در داخل رشته برگشتی قرار دهید:

Blade::directive('ifGuest', function () {
// ضد الگو! این را کپی نکنید.
    $ifGuest = auth()->guest();
    return "<?php if ({$ifGuest}): ?>";
});

 

پارامترها در دستورهای Blade سفارشی

اگر بخواهید در منطق سفارشی خود پارامتر بپذیرید، به مثال ۴-۳۰ نگاهی بیندازید.

مثال 4-30. ایجاد یک دستور Blade با پارامترها

// متصل کردن
Blade::directive('newlinesToBr', function ($expression) {
    return "<?php echo nl2br({$expression}); ?>";
});

// در عمل
<p>@newlinesToBr($message->body)</p>

پارامتر $expression که به closure ارسال می‌شود، نمایانگر هر چیزی است که در داخل پرانتزها قرار دارد. همانطور که مشاهده می‌کنید، سپس یک قطعه کد PHP معتبر تولید کرده و آن را باز می‌گردانیم.
اگر خود را در حال نوشتن مکرر منطق شرطی مشابهی دیدید، بهتر است یک دستور Blade سفارشی در نظر بگیرید.
مثال: استفاده از دستورهای Blade سفارشی برای یک اپلیکیشن چندمستأجره
فرض کنید داریم یک اپلیکیشن می‌سازیم که از چند مستأجر پشتیبانی می‌کند، به این معنی که کاربران ممکن است از آدرس‌هایی مانند www.myapp.com، client1.myapp.com، client2.myapp.com یا مکان‌های دیگر به سایت مراجعه کنند.
فرض کنید یک کلاس به نام Context نوشته‌ایم تا برخی از منطق‌های چندمستأجره خود را در آن محصور کنیم. این کلاس اطلاعات و منطق مربوط به زمینه بازدید فعلی را ضبط می‌کند، مانند اینکه کاربر احراز هویت‌شده کیست و آیا کاربر به وب‌سایت عمومی مراجعه می‌کند یا به یک زیر دامنه مشتری.
احتمالاً ما به‌طور مکرر آن کلاس Context را در نماهایمان فراخوانی کرده و بر اساس آن شرط‌ها را اجرا خواهیم کرد، مانند مثال ۴-۳۱. app('context') یک میانبر برای گرفتن یک نمونه از یک کلاس از کانتینر است که در فصل ۱۱ بیشتر در مورد آن خواهیم آموخت.

@if (app('context')->isPublic())
    &copy; Copyright MyApp LLC
@else
    &copy; Copyright {{ app('context')->client->name }}
@endif

فرض کنید بتوانیم @if (app('context')->isPublic()) را به سادگی به @ifPublic تبدیل کنیم. بیایید این کار را انجام دهیم. به مثال ۴-۳۲ نگاه کنید.

مثال 4-32. شرط‌گذاری بر اساس کانتکست با استفاده از یک دستور سفارشی Blade

// متصل کردن
Blade::directive('ifPublic', function () {
    return "<?php if (app('context')->isPublic()): ?>";
});

// در عمل
@ifPublic
    &copy; Copyright MyApp LLC
@else
    &copy; Copyright {{ app('context')->client->name }}
@endif

از آنجایی که این به یک دستور if ساده تبدیل می‌شود، هنوز می‌توانیم از دستورات @else و @endif بومی استفاده کنیم. اما اگر بخواهیم، می‌توانیم یک دستور @elseIfClient سفارشی، یا یک دستور @ifClient جداگانه، یا هر چیز دیگری که بخواهیم ایجاد کنیم.

دستورات سفارشی آسان‌ تر برای دستورات "if"

در حالی که دستورات سفارشی Blade قدرتمند هستند، رایج&zwnj;ترین استفاده از آنها برای دستورات if است. بنابراین راه ساده&zwnj;تری برای ایجاد دستورات سفارشی "if" وجود دارد: Blade::if(). مثال ۴-۳۳ نشان می&zwnj;دهد چگونه می&zwnj;توانیم مثال ۴-۳۲ را با استفاده از متد Blade::if() بازنویسی کنیم: مثال 4-33. تعریف یک دستور سفارشی "if" در Blade // Binding Blade::if('ifPublic', function () { return (app('context'))-&gt;isPublic(); }); شما از دستورات دقیقاً به همان روشی که قبلاً استفاده کرده&zwnj;اید، استفاده خواهید کرد، اما همانطور که می&zwnj;بینید، تعریف آنها کمی ساده&zwnj;تر است. به جای اینکه مجبور باشید دستورات PHP را به صورت دستی وارد کنید، می&zwnj;توانید تنها یک...

برای دیدن ادامه محتوا وارد شوید

تست

رایج&zwnj;ترین روش آزمون views از طریق آزمون&zwnj;های اپلیکیشن است، به این معنا که شما واقعاً مسیری را که نمایش&zwnj;دهنده views است فراخوانی می&zwnj;کنید و اطمینان حاصل می&zwnj;کنید که views محتوای خاصی دارند (به مثال ۴-۳۴ مراجعه کنید). شما همچنین می&zwnj;توانید دکمه&zwnj;ها را کلیک کنید یا فرم&zwnj;ها را ارسال کنید و اطمینان حاصل کنید که به صفحه خاصی هدایت می&zwnj;شوید یا اینکه خطای خاصی مشاهده می&zwnj;کنید. (در فصل ۱۲ بیشتر در مورد آزمون&zwnj;ها یاد خواهید گرفت.) مثال 4-34. تست اینکه یک ویو محتوای مشخصی را نمایش می&zwnj;دهد // EventsTest.php public function test_list_page_shows_all_events() { $event1 = Event::factory()-&gt;create(); $event2 = Event::factory()-&gt;create(); $this-&gt;get('events') -&gt;assertSee($event1-&gt;title)...

برای دیدن ادامه محتوا وارد شوید

خلاصه

 Blade موتور قالب‌سازی لاراول است. تمرکز اصلی آن بر روی نوشتار واضح، مختصر و بیانی با قابلیت ارث‌بری و گسترش قدرتمند است. براکت‌های "نمایش ایمن" آن {{ و }} هستند، براکت‌های نمایش غیر محافظت شده آن {!! و !!} هستند و مجموعه‌ای از تگ‌های سفارشی به نام دستورات وجود دارند که همه با @ شروع می‌شوند (برای مثال، @if و @unless).
شما می‌توانید یک قالب والد تعریف کنید و "حفره‌هایی" در آن برای محتوا ایجاد کنید با استفاده از @yield و @section/@show. سپس می‌توانید به نماهای فرزند خود آموزش دهید تا از آن ارث‌بری کنند با استفاده از @extends('parent.view') و بخش‌های خود را با استفاده از @section/@endsection تعریف کنند. برای ارجاع به محتوای بلوک والد از @parent استفاده می‌کنید.
کمپوزرهای نما باعث می‌شوند که هر بار که یک نمای خاص یا زیرنمای آن بارگذاری می‌شود، اطلاعات خاصی برای آن در دسترس باشد. همچنین، تزریق سرویس به نما اجازه می‌دهد تا خود نما داده‌ها را مستقیماً از کانتینر برنامه درخواست کند.