یکی از مزیت های لاراول سیستم کش آن است منظور از کش (Cache) ، ذخیره موقت داده هایی است که به طور مرتب نیاز به دریافت از منابعی مثل دیتابیس ، API و … دارند. بدین ترتیب اولین بار داده ها دریافت و ذخیره می شوند و دفعات بعدی به جای آن منبع اصلی،داده ها از کش خوانده می شوند. پس سرعت سیستم عملکرد چشمگیری پیدا می کند. پروژه شما از هر نوع که باشد (Restful API, برنامه تحت وب و …) سیستم Cache لاراول پاسخگوی نیازهای شماست. در کل لاراول می تواند هر نوع داده ای که شما بفرستید، با تاریخ انقضای مشخص، داخل کش ذخیره کند. مثل HTML, JSON, collection, Eloquent و …
بریم سر اصل مطلب. یک نمونه کد برای ذخیره پاسخ در کش لاراول شبیه این خواهد بود :
return Cache::remember($rememberKey, $minutes , function() use ($data) { return $data; });
کد ساده به نظر می رسه وساده هم هست. متد جالب remember را می بینید که باعث می شود داده های موجود برگشت داده شوند و اگر از قبل وجود ندارند، ساخته شوند یعنی دو کار مهم را با هم انجام می دهد. از $rememberKey
برای تشخیص داده مربوطه از بین داده های موجود در کش استفاده می شود و از $minutes
برای تعیین طول عمر داده ها در کش.
سوال اینجاست که “لاراول چطور تشخیص می دهد که داده را چه موقع باید در کش ذخیره کند؟”
برای پاسخ به این سوال باید سراغ عنصر مهم یعنی rememberKey برویم. بسته به مقدار متغیر $rememberKey
، لاراول می فهمد که آیا این داده قبلا کش شده است یا نه.بنابراین این مقدار خیلی مهم است و اگر شما برای کش کردن همه داده ها از یک اسم ($rememberKey
) مشابه استفاده کنید، تا زمان منقضی شدن کش، همیشه یک پاسخ واحد دریافت خواهید کرد. این واضحه.
متاسفانه برای سناریوهای دیگر خیلی واضح نیست. چون شما از اهمیت مقدار یکتا برای $rememberKey
آگاه هستید، تصمیم می گیرید از آدرس فعلی صفحه به عنوان $rememberKey
استفاده کنید.مثل این:
$url = request()->url(); return Cache::remember($url, $minutes, function () use ($data) { return $data; });
تقریبا مثل قبلی است، اما حالا $rememberKey
همان آدرس فعلی صفحه است و بدین ترتیب شما تا حدودی می توانید از متفاوت بودن $rememberKey
برای هر صفحه مطمئن بشید.
برای مثال، اگر کاربری صفحه yourdomain.com/products
را باز کند، کش با همین آدرس داده ها را ذخیره می کند. هر زمان که کاربر به آدرس دیگری رفت مثل yourdomain.com/categories
، می توانید مطمئن باشیدکه سیستم کش لاراول، مقادیر مربوطه را به درستی ذخیره کرده یا از کش می خواند.
اما ، یه دقیقه صبر کن! فرض کن در صفحات از پارامترهایی بنام Query String برای آدرس استفاده می کنید مثل : yourdomain.com/products?page=2&sort_by=price
. در این مورد، وقتی شما آدرس را از طریق کد $url = request()->url()
می گیرید، این کد پارامترهای آدرس را بر نمی گرداند و فقط آدرس اصلی را برگشت می دهد، بنابراین تغییراتی که این پارامترها باید در نتیجه برگشتی از دیتابیس ایجاد کنند، ایجاد نمی شود و همان نتیجه بدون پارامتر را خواهید دید.
Query String به همه پارامترهایی گفته می شود که در آدرس صفحه، بعد از علامت ? می آیند.
مشکلی نیست، شما باید پارامترهای موجود در آدرس را هم بازی دهید و همه چیز دوباره گل و بلبل می شود.درسته ؟ مثل این:
$fullUrl = request()->fullUrl(); return Cache::remember($fullUrl, $minutes, function () use ($data) { return $data; });
اوکی حله! یا نه ؟! صب کن یه دقیقه. اجازه بدین یه تست انجام بدیم. اگر کاربر به صفحه yourdomain.com/products?page=2&sort_by=price
برود ، باعث کش شدن داده ها و برگشت دادن پاسخ می شود. اگر کاربر به آدرس yourdomain.com/products?page=1&sort_by=name
برود با هم داده ها کش شده و نتیجه برگشت می شود. خوب عالیه! سیستم می تونه پاسخ های متفاوت رو کش و برگشت بدهد. حالا برای تست نهایی، کاربر به این آدرس می ره yourdomain.com/products?page=2&sort_by=price
و بعد به این آدرس yourdomain.com/products?sort_by=price&page=2
. اینها درخواست های متفاوتی هستند بنابراین سیستم کش همه داده های مربوط به این دو درخواست را به صورت جداگانه ذخیره می کند.فقط یک مشکل کوچک! این دو درخواست در حقیقت یک درخواست هستند فقط ترتیب پارامترها عوض شده است. ترتیب پارامترها هم مهم نیست اصلا ولی سیستم ما متوجه یکی بودن درخواست ها نیست و فقط چون آدرس ها عین هم نیستند، این دو درخواست به صورت جداگانه کش می شوند که اصلا درست نیست.
خوب حالا چیکار کنیم؟ به نظر می رسه راه حل ساده ای نداریم. ما باید پارامترهای کوئری موجود در آدرس را مرتب کنیم.
این کد همین کار را می کند:
$url = request()->url(); $queryParams = request()->query(); //Sorting query params by key (acts by reference) ksort($queryParams); //Transforming the query array to query string $queryString = http_build_query($queryParams); $fullUrl = "{$url}?{$queryString}"; return Cache::remember($fullUrl, $minutes, function () use ($data) { return $data; });
خوب جادوی کار کجاست؟ ما پارامترهای آدرس را به صورت آرایه انجمنی با استفاده از متد query()
گرفتیم و آن ها را با متد ksort
بر اساس نام پارامتر مرتب کردیم (نه مقدار). در نهایت با استفاده از پارامترهای مرتب شده، کوئری آدرس را با http_build_query
دوباره مونتاژ می کنیم و آدرس کامل را می سازیم تا به عنوان کلید کش (remember key) ازش استفاده کنیم.
بعد از این اگر کاربر به آدرس yourdomain.com/products?sort_by=price&page=2
برود، انگار به آدرس yourdomain.com/products?page=2&sort_by=price
رفته است و بدین ترتیب می توان برای درخواست های یکسان، از کش یکسان استفاده کرد حتی اگر کمی در چینش پارامترها با هم فرق داشته باشند.
اگر می خواهید کمی کد تجملی تر بنویسید می توانید آدرس کامل را هش کنید مثل این:
$url = request()->url(); $queryParams = request()->query(); ksort($queryParams); $queryString = http_build_query($queryParams); $fullUrl = "{$url}?{$queryString}"; $rememberKey = sha1($fullUrl); return Cache::remember($rememberKey, $minutes, function () use ($data) { return $data; });