View Helper ها در زند فریمورک ۲

یه توضیح کوچولو
View helper ها به زبون خودم، یه سری کد هستند که از طریق تمپلیت و layout ها قابل دسترسی هستند و می شه باهاشون مثل متدی باهاشون رفتار کرد و از طریقشون یه سری کارها رو داخل تمپلیت ها انجام داد.

به تعریف دیگه ای، می شه گفت یه سری کد reusable دارید که می‌خوایید ازشون تو تمپلیت هاتون استفاده کنید و نمی خوایید هربار از طریق کنترلرتون به تمپلیت ٓتون تزریق کنیدکه در این صورت می شه براش یه view helper یا به نوعی کلاس مجزایی نوشت و ازش در هر تمپلیت ای استفاده کرد.

خود زند یه سری view helper ها واسه راه انداختن سریع کارها تو خودش داره مثل url و escapehtml و … .
اما گاهی اوقات پیش می یاد که این view helper های موجود به کارمون نمی یاد و نیازه که خودمون یه سری کد و منطق رو بتونیم از طریق تمپلیت هامون بهشون دسترسی داشته باشیم و استفاده کنیم.

در این‌گونه موارد، می شه view helper مخصوص کاری که لازم داریم رو ایجاد کرد.


شروع کار
برای ایجاد view helper تو zf2 چند راه وجود داره که، یه سری شون رو که اکثراً به کار می یان رو تو این مطلب می نویسم.

ابتدا یه کلاسی ایجاد می‌کنیم و منطقمون یا کدمون واسه کاری که می خواییم انجام بده رو توش می نویسیم و خلاص :)

البته یه چند تا نکته هست:
اولی اینه که نیازه از کلاس Zend\View\Helper\AbstractHelper ارث‌بری کنین. در حقیقت نیازه اینترفیس Zend\View\Helper\HelperInterface رو اعمال کنید که توصیه می شه همون کلاس AbstractHelper رو ازش ارث‌بری کنید که خودش همین اینترفیس رو اعمال کرده و سریع کارتون راه می افته.

نکته بعدی اینه که، به آبجکتی که تو تمپلیت هاتون بهش دسترسی دارید در حقیقت آبجکت کلاس PhpRenderer هست و برای اینکه به کلاستون که همون view helper هست مثل یه متد خود PhpRenderer دسترسی داشته باشید، نیازه که متد __invoke رو هم تو کلاستون اعمال کنید.

به عنوان مثال برای دسترسی به helper مثل url به این صورت عمل می کنیم:
$this->url($input);
که در حقیقت این $this به آبجکت کلاس PhpRenderer اشاره داره.
و این url اسمی هست که برای helper مون در نظر گرفتیم، و مقدار داده شده بهش به عنوان پارامتر به متد __invoke کلاسمون پاس داده خواهد شد.


تعیین کلاس به عنوان View Helper
خوب، حالا که کلاسمون آماده هست، نیازه که این helper نوشته شده رو به سیستم معرفی کنیم که بشه ازش استفاده کرد.

اینجا بازم یه نکته‌ای هست و اون اینه که یه چند تا راه برای معرفی helper مون داریم، که بستگی به کلاسمون داره که از کدومشون استفاده کنیم.

اولیش اینه که یه factory بنویسم و برای مواقعی هست که کلاس نوشته شده وابستگی ای به کلاس‌های دیگه ای داره که نیازه بهش داده بشه، که در حقیقت این factory یه تابع بدون نام هست که باید داخلش از این کلاس آبجکت ایجاد کرده و تنظیمش کنیم یا همون وابستگی هاش رو بهش بدیم و در نهایت آبجکت رو برگردونیم.

دومیش هم واسه وقتیه که کلاسمون وابستگی نداره راحت می شه کلاس مون رو به عنوان helper به سیستم معرفی کنیم و بهش به عنوان invokable class اشاره می شه.


نمونه کد

برای نوع factory به این شکل عمل می‌کنیم که در کلاس Module.php مون اینترفیس Zend\ModuleManager\Feature\ViewHelperProviderInterface رو اعمال کرده و یا بطور ساده متد getViewHelperConfig که بصورت پابلیک هم خواهد بود رو ایجاد می کنیم.

این متد باید آرایه ای رو برگردونه که توش آرایه دیگه ای قرار داره که به عنوان کلید (key) کلمه factories رو خواهد داشت و مقدارش هم دوباره آرایه دیگه ای خواهد بود که بصورت associative-array خواهد بود.

الان در این آرایه می تونیم اسمی واسه view helper مون تعیین کنیم که بشه با اون اسم صداش کرد و مقدارش هم تابع بی‌نامی خواهد بود که یک پارامتری هم دریافت می کنه.

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

به عنوان مثال، کد زیر رو در نظر بگیرید:
public function getViewHelperConfig()
{
    return array(
        'factories' => array(
            'sidebarMenu' => function ($helpers){
                $viewRenderer = $helpers->getServiceLocator()->get('ZfcTwigRenderer');
                return new SidebarMenu($viewRenderer);
            },
        )
    );
}

در این تابع، آرایه ای برمی گردونیم که یک عنصر به اسم factories داره که مقدارش هم آرایه دیگه ای است.
در حقیقت تمام view helper هامون رو در این آرایه دومی تعیین خواهیم کرد.

در این مثال، view helper ای به اسم sidebarMenu ایجاد شده و هدفش اینه که کلاس SidebarMenu رو از طریق view ها بهش دسترسی داشته باشیم.
ولی از اونجایی که این کلاس وابسته به کلاس دیگه ای هست، نیاز بوده که براش به factory ایجاد بشه و از طریق Service Manager (که از طریق تنها پارامتر مربوطه قابل دسترسی است) آبجکتی که کلاس اصلی مون بهش وابسته هست رو دریافت و بهش می دیم.

(پارامتری که به این تابع داده می شه از نوع Zend\View\HelperPluginManager هست و برای دسترسی به Service Manager می شه از مد getServiceLocator() استفاده کرد.)

در نهایت هم آبجکت ایجاد شده از کلاس اصلی مون یا همان SidebarMenu رو برمی گردونیم.


واسه invokable class یا راه دوم هم واسه زمانی بود که کلاس مون وابستگی نداره و می شه مستقیم به عنوان view helper تعیین کرد که برای این:
ابتدا فایل module.config.php که داخل پوشه config ماژولمون هست و معمولاً آرایه ای برمی گردونه رو باز می کنیم.

سپس به آرایه اصلی یه عنصر جدید به اسم view_helpers که مقدارش هم آرایه دیگه ای خواهد بود اضافه می کنیم.
در آرایه ایجاد شده هم عنصر دیگه ای به اسم invokables ایجاد می‌کنیم و بازم مقدارش آرایه دیگری خواهد بود.
کار اصلی مون در این آرایه نهایی خواهد بود و عنصر دیگه ای ایجاد می کنیم و نامی که براش در نظر می‌گیریم نام view helper مون خواهد بود و مقدارش رو هم برابر با اسم کلاسی که ایجاد کرده‌ایم (همراه با namespace اش) می زاریم.

به عنوان مثال، کد زیر رو در نظر بگیرید:
return array(
    'view_helpers' => array(
        'invokables' => array(
            'sidebarMenu' => 'Dashboard\View\Helper\SidebarMenu',
        ),
    ),
)
که المنت ای به اسم sidebarMenu است (و از طریق این اسم از داخل view هامون می شه به کلاس مربوطه مون دسترسی داشته باشیم) ایجاد شده و مقدارش هم آدرس کلاس مربوطه است (همراه namespace کاملش).


سوال؟
یه سؤالی که ممکنه واستون ایجاد شده باشه، که چرا تو یه روش، view helper مون رو تو فایل config تعیین کردیم و در روش بعدی در کلاس Module ؟
دلیل این کار این بود که می شه تعیین کرد فایل‌های config توسط زند کش بشن که در این صورت هر سری فایل‌های config بخاطر performace دیگه لود نمی شن و از کش خونده می شن.

ولی مسأله اینجاست که متأسفانه در حال حاضر PHP نمی تونه آبجکت‌ها و توابع بی‌نام یا همون Closure ها رو serialize و deserialize کنه در نتیجه اگه تابع بی نام رو داخل فایل config تعیین می‌کردید مشکلاتی واسه کش کردن پیش می اومد و دردسر ایجاد می‌کرد.

به همین خاطر چون در روش دومی یا همون invokabale class که مقدارهامون متن (String ) هست می شه در فایل config تعیین کرد و در نهایت کش کرد که هر بار فایل‌ها دوباره خونده نشن.
ولی در رابطه با getViewHelperConfig که در کلاس Module تعیین می شه، چه کش فعال باشه یا نه، این متد در صورتی که تعیین بشه، هر بار صدا زده می شه.



نحوه استفاده
برای استفاده یا صدا کردن کلاس مربوطه در تمپلت‌ها مون، به این صورت عمل کنیم:
$this->sidebarMenu();


توجه کنید که راه‌های دیگه ای هم به غیر از این موارد برای ایجاد view helper هست، که اگه این مطلب نیازتون رو برآورده نمی کنه توصیه می‌کنم مستندات موجود در خود سایت زند فریمورک رو مطالعه کنید که وارد جزئیات بیشتری می شه. ولی با این حال، این دو روش اکثراً جوابگو خواهد بود.


نکته: تو این مطلب در نظر گرفته شده که شما از zend-skeleton-application استفاده می کنید.


منابع
+ + + +


امیدوارم مفید بوده باشه. :)

پست‌های معروف از این وبلاگ

رویدادها در MySQL

لیست شهر و استان‌های ایران

مدیریت مخارج، برنامه‌ای برای اندروید