سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
آموزش بهینه‌سازی Laravel؛ افزایش سرعت و بهبود عملکرد پروژه

آموزش بهینه‌سازی Laravel؛ افزایش سرعت و بهبود عملکرد پروژه

ممدیر کل سیستم ۲۲ مهر ۱۴۰۵ ۱۴ دقیقه مطالعه ۱ بازدید
آموزش بهینه‌سازی Laravel؛ افزایش سرعت و بهبود عملکرد پروژه

سرعت بارگذاری صفحات و پاسخ‌گویی APIها یکی از عوامل مهم در کیفیت یک نرم‌افزار تحت وب است. حتی اگر یک پروژه Laravel از معماری مناسبی برخوردار باشد، کوئری‌های غیربهینه، درخواست‌های تکراری به دیتابیس، پردازش‌های سنگین و تنظیمات نامناسب سرور می‌توانند عملکرد آن را کاهش دهند.

لاراول ابزارهای متعددی برای بهبود عملکرد برنامه در اختیار توسعه‌دهندگان قرار می‌دهد؛ از کش تنظیمات و مسیرها گرفته تا Eloquent، Queue، Redis و ابزارهای بررسی کوئری‌ها.

در این مقاله با روش‌های کاربردی بهینه‌سازی Laravel آشنا می‌شویم و بررسی می‌کنیم چگونه می‌توان بدون تغییر غیرضروری در معماری پروژه، سرعت و کارایی آن را بهبود داد.

۱. ابتدا علت کندی Laravel را پیدا کنید

قبل از اعمال تغییرات، باید مشخص شود کندی برنامه از کدام بخش ناشی می‌شود. ممکن است مشکل از کوئری‌های دیتابیس، پردازش PHP، ارتباط با API خارجی، فایل‌های استاتیک یا منابع محدود سرور باشد.

برای بررسی دقیق‌تر، به این موارد توجه کنید:

  • زمان پاسخ‌گویی هر درخواست
  • تعداد کوئری‌های اجراشده برای هر صفحه
  • کوئری‌های کند دیتابیس
  • میزان مصرف CPU و RAM
  • مدت‌زمان ارتباط با APIهای خارجی
  • تعداد درخواست‌های تکراری
  • حجم فایل‌های CSS، JavaScript و تصاویر

برای مثال، اگر یک صفحه در مجموع ۳ ثانیه زمان می‌برد اما ۲٫۵ ثانیه آن صرف دریافت اطلاعات از یک API خارجی می‌شود، تغییر تنظیمات کش Route به‌تنهایی مشکل اصلی را برطرف نخواهد کرد.

در محیط توسعه می‌توانید از ابزارهایی مانند Laravel Debugbar یا ابزارهای مشابه برای بررسی کوئری‌ها و زمان اجرای درخواست استفاده کنید. این ابزارها را بدون بررسی اثرشان روی عملکرد، در محیط عمومی و production فعال نگذارید.

۲. استفاده از کش تنظیمات Laravel

لاراول تنظیمات پروژه را از فایل‌های پیکربندی و در محیط‌های مختلف از متغیرهای محیطی دریافت می‌کند. در محیط production، ایجاد کش تنظیمات می‌تواند از بارگذاری و پردازش تکراری فایل‌های پیکربندی جلوگیری کند.

برای ساخت کش تنظیمات، اجرا کنید:


php artisan config:cache

اگر تنظیمات پروژه تغییر کرد، می‌توانید کش‌های بهینه‌سازی‌شده را پاک کنید:


php artisan optimize:clear

سپس کش تنظیمات را دوباره بسازید:


php artisan config:cache

نکته مهم: پس از ساخت کش تنظیمات، مقادیر محیطی را در بخش‌های مختلف برنامه به‌شکل مستقیم و پراکنده از طریق env() فراخوانی نکنید. استفاده از env() را به فایل‌های پیکربندی محدود کنید و در بخش‌های دیگر برنامه از config() استفاده کنید.

برای مثال، این روش مناسب‌تر است:


// config/services.php

return [

    'external_api_url' => env('EXTERNAL_API_URL'),

];

سپس در کد برنامه:


$apiUrl = config('services.external_api_url');

این ساختار باعث می‌شود برنامه پس از فعال‌سازی کش تنظیمات، مقادیر موردنیاز را از پیکربندی Laravel دریافت کند.

۳. کش کردن Routeها

در پروژه‌هایی که تعداد زیادی Route دارند، می‌توان از کش مسیرها استفاده کرد تا Laravel مسیرهای تعریف‌شده را سریع‌تر بارگذاری کند.


php artisan route:cache

برای مشاهده مسیرهای ثبت‌شده نیز می‌توانید از دستور زیر استفاده کنید:


php artisan route:list

اگر پس از تغییر مسیرها یا هنگام ایجاد کش با خطا مواجه شدید، ابتدا خطا را بررسی کنید. برخی تعریف‌های Route یا ساختارهای ناسازگار ممکن است مانع ایجاد کش شوند.

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

۴. کش کردن Viewها

Laravel از Blade برای ساخت قالب‌های سمت سرور استفاده می‌کند. در محیط production می‌توانید قالب‌های Blade را از قبل کامپایل کنید:


php artisan view:cache

این کار می‌تواند بخشی از هزینه پردازش قالب‌ها را کاهش دهد، به‌خصوص در برنامه‌هایی که Viewهای زیادی دارند.

برای پاک کردن کش Viewها:


php artisan view:clear

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

۵. رفع مشکل N+1 در Eloquent

یکی از مشکلات رایج در پروژه‌های Laravel، اجرای تعداد زیادی کوئری به‌دلیل بارگذاری تنبل رابطه‌ها یا Lazy Loading است. این مشکل معمولاً با عنوان N+1 Query Problem شناخته می‌شود.

فرض کنید فهرستی از مقالات را نمایش می‌دهید و برای هر مقاله نام نویسنده را نیز دریافت می‌کنید.

کد زیر ممکن است برای دریافت نویسنده هر مقاله یک کوئری جداگانه اجرا کند:


$posts = Post::all();



foreach ($posts as $post) {

    echo $post->user->name;

}

اگر ۱۰۰ مقاله داشته باشید، بسته به نحوه دسترسی و رابطه‌ها ممکن است یک کوئری برای دریافت مقالات و تعداد زیادی کوئری برای دریافت نویسندگان اجرا شود.

راه‌حل: استفاده از Eager Loading

برای بارگذاری رابطه‌ها به‌صورت بهینه‌تر، از with() استفاده کنید:


$posts = Post::with('user')->get();



foreach ($posts as $post) {

    echo $post->user->name;

}

در این حالت Laravel معمولاً مقالات را با یک کوئری و نویسندگان مرتبط را با یک کوئری دیگر دریافت می‌کند؛ تعداد دقیق کوئری‌ها به ساختار رابطه‌ها و نحوه استفاده بستگی دارد.

اگر فقط چند ستون مشخص نیاز دارید، می‌توانید ستون‌های لازم را محدود کنید. برای مثال، در یک رابطه معمولی belongsTo:


$posts = Post::with('user:id,name')->get();

هنگام محدود کردن ستون‌ها، باید کلیدهای موردنیاز برای تطبیق رابطه نیز در نتیجه باقی بمانند.

۶. فقط داده‌های موردنیاز را از دیتابیس دریافت کنید

یکی از اشتباهات رایج، استفاده از get() یا all() برای دریافت همه ستون‌ها و همه رکوردهاست؛ درحالی‌که صفحه تنها به بخشی از اطلاعات نیاز دارد.

برای مثال:


$users = User::all();

اگر جدول کاربران بسیار بزرگ باشد، دریافت تمام رکوردها می‌تواند مصرف حافظه و زمان پاسخ را افزایش دهد.

اگر فقط نام و ایمیل کاربران فعال لازم است:


$users = User::query()

    ->where('status', 'active')

    ->select('id', 'name', 'email')

    ->get();

این روش دو مزیت دارد:

  • فقط رکوردهای موردنیاز دریافت می‌شوند.
  • تعداد ستون‌های منتقل‌شده از دیتابیس کاهش می‌یابد.

اگر هدف نمایش فهرست طولانی اطلاعات است، بهتر است از Pagination یا روش‌های پردازش دسته‌ای استفاده کنید.

۷. استفاده از Pagination برای داده‌های حجیم

نمایش هزاران رکورد در یک درخواست، هم به دیتابیس فشار وارد می‌کند و هم ممکن است حافظه PHP را بیش از حد مصرف کند.

برای نمایش صفحه‌بندی‌شده اطلاعات:


$users = User::query()

    ->orderBy('id')

    ->paginate(20);

در این مثال، در هر صفحه ۲۰ رکورد نمایش داده می‌شود.

اگر رابط کاربری به شماره صفحات نیاز ندارد و فقط دکمه‌های «قبلی» و «بعدی» کافی هستند، می‌توانید از simplePaginate() استفاده کنید:


$users = User::query()

    ->orderBy('id')

    ->simplePaginate(20);

برای پیمایش داده‌های بسیار حجیم، به‌ویژه در پردازش‌های داخلی، cursor()، lazy() یا chunkById() نیز می‌توانند گزینه‌های مناسبی باشند. انتخاب روش مناسب به نیاز برنامه و نحوه تغییر داده‌ها در حین پردازش بستگی دارد.

۸. بهینه‌سازی کوئری‌ها و ایندکس‌های دیتابیس

استفاده صحیح از Eloquent به‌تنهایی تضمین نمی‌کند که تمام کوئری‌ها سریع باشند. اگر جدول بزرگی دارید و روی ستونی که در شرط جست‌وجو استفاده می‌شود ایندکس مناسبی وجود ندارد، دیتابیس ممکن است مجبور شود تعداد زیادی رکورد را بررسی کند.

برای مثال:


$orders = Order::query()

    ->where('status', 'paid')

    ->where('user_id', $userId)

    ->orderByDesc('created_at')

    ->get();

برای بهینه‌سازی این کوئری، باید ابتدا تعداد رکوردها، توزیع مقادیر و برنامه اجرای کوئری را بررسی کنید. ممکن است یک ایندکس ترکیبی متناسب با شرط‌ها و ترتیب مرتب‌سازی مفید باشد، اما انتخاب آن باید بر اساس رفتار واقعی دیتابیس انجام شود.

در Migration لاراول می‌توان ایندکس تعریف کرد:


Schema::table('orders', function (Blueprint $table) {

    $table->index(['user_id', 'status']);

});

این نمونه صرفاً برای نشان دادن شیوه تعریف ایندکس است؛ لزوماً برای تمام کوئری‌ها بهترین ترتیب ستون‌ها نیست. ایندکس‌های اضافی نیز فضای ذخیره‌سازی مصرف می‌کنند و می‌توانند عملیات درج و به‌روزرسانی را کندتر کنند.

برای بررسی کوئری‌های کند، از ابزارهایی مانند EXPLAIN در MySQL یا PostgreSQL و امکانات ثبت کوئری‌های کند استفاده کنید.

۹. استفاده از Cache برای داده‌های پرتکرار

گاهی داده‌ای مانند تنظیمات عمومی سایت، فهرست دسته‌بندی‌ها یا اطلاعاتی که به‌ندرت تغییر می‌کنند، در درخواست‌های متعدد خوانده می‌شود. در چنین شرایطی، کش کردن نتیجه می‌تواند تعداد درخواست‌ها به دیتابیس را کاهش دهد.

برای نمونه:


use Illuminate\Support\Facades\Cache;



$categories = Cache::remember(

    'active_categories',

    now()->addMinutes(30),

    function () {

        return Category::query()

            ->where('is_active', true)

            ->orderBy('name')

            ->get();

    }

);

در این مثال، نتیجه برای ۳۰ دقیقه در Cache ذخیره می‌شود. در درخواست‌های بعدی، تا زمانی که مقدار معتبر باشد، نتیجه از کش خوانده خواهد شد.

بااین‌حال، باید برای داده‌هایی که مرتب تغییر می‌کنند، راهکار مناسبی برای پاک کردن یا به‌روزرسانی کش در نظر بگیرید. در غیر این صورت ممکن است کاربران اطلاعات قدیمی ببینند.

برای مثال، هنگام تغییر دسته‌بندی‌ها می‌توان کلید مربوطه را حذف کرد:


Cache::forget('active_categories');

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

۱۰. استفاده از Redis در پروژه Laravel

Redis یک ذخیره‌ساز داده در حافظه است که می‌تواند برای Cache، مدیریت صف‌ها و برخی کاربردهای دیگر استفاده شود. در پروژه‌هایی که بار درخواست بالایی دارند، استفاده درست از Redis ممکن است بخشی از فشار روی دیتابیس اصلی را کاهش دهد.

نمونه تنظیمات کش در فایل .env، بسته به نسخه Laravel و تنظیمات پروژه، می‌تواند به این صورت باشد:


CACHE_STORE=redis

در نسخه‌ها یا پروژه‌های قدیمی‌تر ممکن است نام متغیر تنظیمات متفاوت باشد؛ بنابراین فایل config/cache.php پروژه را بررسی کنید.

همچنین تنظیمات اتصال Redis باید در فایل‌های پیکربندی مربوطه صحیح باشند و سرویس Redis نیز در دسترس برنامه قرار داشته باشد.

نکته: نصب Redis به‌تنهایی باعث افزایش سرعت تمام پروژه‌ها نمی‌شود. ابتدا مشخص کنید کدام داده‌ها پرتکرار هستند، چه مقدار از زمان پاسخ صرف دیتابیس می‌شود و آیا هزینه نگهداری کش در این سناریو توجیه دارد یا خیر.

۱۱. انتقال پردازش‌های سنگین به Queue

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

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

برای مثال، یک Job بسازید:


php artisan make:job SendOrderConfirmation

سپس عملیات ارسال تأییدیه سفارش را در متد handle() این Job پیاده‌سازی کنید و در صورت نیاز آن را به صف ارسال کنید:


SendOrderConfirmation::dispatch($order);

برای اجرای Jobها باید یک Queue Worker فعال باشد و اتصال صف نیز متناسب با زیرساخت پروژه تنظیم شده باشد.

انتقال پردازش به صف باعث می‌شود پاسخ اولیه برنامه سریع‌تر برگردد؛ اما زمان کلی تکمیل کار پس‌زمینه همچنان به ظرفیت Workerها و منابع سرور وابسته است.

همچنین برای عملیات حساس مانند پرداخت، ثبت سفارش و ارسال پیام، باید رفتار تکرار Job، مدیریت خطا و جلوگیری از اجرای دوباره عملیات را به‌درستی طراحی کنید.

۱۲. مدیریت درخواست‌های خارجی

یکی از دلایل پنهان کندی برنامه، درخواست‌های هم‌زمان و زمان‌بر به سرویس‌های خارجی است. برای مثال، ممکن است یک صفحه برای دریافت قیمت ارز، اطلاعات حمل‌ونقل یا داده‌های یک API شخص ثالث منتظر بماند.

در این شرایط:

  • برای درخواست‌ها Timeout مناسب تعیین کنید.
  • در صورت امکان، اطلاعات پرتکرار را Cache کنید.
  • عملیات غیرضروری را از مسیر پاسخ مستقیم کاربر خارج کنید.
  • خطاها و مدت‌زمان پاسخ سرویس خارجی را ثبت کنید.
  • برای درخواست‌های تکراری و عملیات حساس، سیاست Retry مناسبی داشته باشید.

کش کردن پاسخ API باید با توجه به مدت اعتبار اطلاعات و شرایط سرویس انجام شود؛ داده‌هایی که باید لحظه‌ای باشند، نباید بدون سیاست مشخص برای مدت طولانی کش شوند.

۱۳. بهینه‌سازی فایل‌های Frontend

سرعت یک صفحه فقط به PHP و دیتابیس وابسته نیست. فایل‌های بزرگ JavaScript، CSS، تصاویر حجیم و درخواست‌های متعدد مرورگر نیز می‌توانند زمان بارگذاری صفحه را افزایش دهند.

اگر پروژه Laravel از Vite استفاده می‌کند، فایل‌های نهایی را برای محیط production بسازید:


npm run build

همچنین این موارد را بررسی کنید:

  • تصاویر را متناسب با اندازه واقعی نمایش بهینه کنید.
  • برای تصاویر از قالب‌های مناسب و فشرده استفاده کنید.
  • فایل‌های غیرضروری را از صفحات حذف کنید.
  • از کش مرورگر برای فایل‌های استاتیک استفاده کنید.
  • بارگذاری فایل‌های غیرضروری در ابتدای صفحه را کاهش دهید.

پس از اعمال تغییرات، عملکرد صفحه را در ابزارهای سنجش سرعت مرورگر بررسی کنید تا مشخص شود کدام تغییر واقعاً مؤثر بوده است.

۱۴. بررسی منابع سرور و تنظیمات PHP

گاهی کد Laravel بهینه است، اما سرور به‌دلیل کمبود منابع یا تنظیمات نامناسب عملکرد ضعیفی دارد.

مواردی که باید بررسی کنید عبارت‌اند از:

  • میزان مصرف CPU و RAM
  • فضای آزاد دیسک
  • نسخه PHP و افزونه‌های فعال
  • تنظیمات OPcache
  • تعداد Workerهای PHP-FPM، در صورت استفاده از PHP-FPM
  • وضعیت Queue Workerها
  • زمان پاسخ‌گویی دیتابیس
  • محدودیت‌های حافظه و زمان اجرای PHP

فعال بودن OPcache در محیط production می‌تواند از کامپایل مجدد مکرر فایل‌های PHP جلوگیری کند. بااین‌حال، تنظیمات دقیق آن باید متناسب با میزان حافظه سرور، الگوی استقرار و اندازه پروژه تعیین شود.

افزایش بی‌برنامه تعداد Workerها یا محدودیت حافظه لزوماً عملکرد را بهتر نمی‌کند؛ این تغییرات ممکن است مصرف RAM را افزایش دهند و فشار بیشتری به سرور وارد کنند.

۱۵. یک فرایند پیشنهادی برای بهینه‌سازی Laravel

برای اینکه بهینه‌سازی به مجموعه‌ای از تغییرات تصادفی تبدیل نشود، می‌توانید این ترتیب را دنبال کنید:

۱. زمان پاسخ و کوئری‌های کند را اندازه‌گیری کنید.

۲. مشکلات N+1 و دریافت داده‌های غیرضروری را برطرف کنید.

۳. کوئری‌ها و ایندکس‌های دیتابیس را بررسی کنید.

۴. برای داده‌های پرتکرار، سیاست کش مناسب طراحی کنید.

۵. پردازش‌های سنگین و غیرهم‌زمان را به Queue منتقل کنید.

۶. کش‌های مناسب Laravel را در محیط production فعال کنید.

۷. فایل‌های Frontend و تصاویر را بهینه کنید.

۸. مصرف منابع سرور و عملکرد برنامه را دوباره اندازه‌گیری کنید.

۹. نتایج قبل و بعد را مقایسه کنید و تغییراتی را که اثر منفی دارند، بازبینی کنید.

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

پرسش‌های متداول

چگونه سرعت Laravel را افزایش دهیم؟

ابتدا باید گلوگاه اصلی را شناسایی کنید. رفع کوئری‌های N+1، دریافت فقط داده‌های موردنیاز، استفاده صحیح از Cache، بهینه‌سازی دیتابیس و ایجاد کش تنظیمات و Routeها از اقدامات رایج هستند.

آیا دستور php artisan optimize برای بهینه‌سازی کافی است؟

خیر. این دستور می‌تواند بخشی از کش‌های مناسب Laravel را ایجاد کند، اما مشکلاتی مانند کوئری‌های سنگین، ایندکس‌های نامناسب، درخواست‌های کند به API خارجی یا کمبود منابع سرور را به‌تنهایی حل نمی‌کند.

آیا Redis همیشه سرعت Laravel را بیشتر می‌کند؟

خیر. Redis در سناریوهای مناسب می‌تواند برای Cache و Queue مفید باشد، اما نتیجه به نوع داده‌ها، الگوی دسترسی، تنظیمات و زیرساخت وابسته است. بهتر است قبل از اضافه کردن آن، گلوگاه‌های پروژه را اندازه‌گیری کنید.

مشکل N+1 در Laravel چیست؟

این مشکل زمانی رخ می‌دهد که برنامه برای هر رکورد یک یا چند کوئری اضافی اجرا کند؛ معمولاً هنگام دسترسی به رابطه‌های Eloquent. استفاده از Eager Loading با with() یکی از راهکارهای رایج برای رفع آن است.

آیا کش کردن همه اطلاعات برای افزایش سرعت مناسب است؟

خیر. کش کردن داده‌های پرتغییر یا حجیم بدون سیاست مشخص می‌تواند باعث نمایش اطلاعات قدیمی، مصرف حافظه و پیچیدگی بیشتر شود. هر داده باید بر اساس میزان تغییر، تعداد دفعات استفاده و اهمیت تازگی اطلاعات بررسی شود.

جمع‌بندی

بهینه‌سازی Laravel فقط به اجرای چند دستور Artisan محدود نمی‌شود. برای افزایش سرعت واقعی باید عملکرد برنامه، کوئری‌های دیتابیس، رابطه‌های Eloquent، سیاست Cache، پردازش‌های پس‌زمینه و منابع سرور را در کنار یکدیگر بررسی کرد.

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

اگر در حال توسعه پروژه‌های Laravel هستید، مطالعه آموزش‌های مرتبط با Eloquent، Redis، Queue و استقرار روی سرور می‌تواند به شما کمک کند برنامه‌هایی سریع‌تر، قابل نگهداری‌تر و آماده‌تر برای رشد بسازید.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه