در دنیای توسعه نرمافزار، ایرانسرور (IranServer) همواره به دنبال بهترین راهحلها بود تا پروژههایش را منظمتر و حرفهایتر مدیریت کند. یکی از چالشهای اخیر تیم فنی، ایجاد یک تابع slug بود که بتواند متنها را به قالبی استاندارد و یکنواخت تبدیل کند. تابعی که تمام حروف را کوچک کند، علائم نگارشی را حذف کند و فاصلهها را با خط تیره جایگزین کند. اما مشکلی وجود داشت؛ در برخی از متون، نام ایرانسرور به صورت "Iran Server"
نوشته شده بود و این باعث میشد که خروجی نهایی یکپارچه نباشد.
تیم توسعه پس از بررسیهای فراوان، تصمیم گرفت این مشکل را یک بار برای همیشه حل کند. آنها از شما میخواهند که این تابع را پیادهسازی کنید. یک رشتهی متنی حاوی کلمات انگلیسی، اعداد، و علائم نگارشی داریم. هدف این است که رشته را به یک slug تبدیل کنیم.
پروژهی اولیه را از این لینک دانلود کنید.
شما باید تابع slugify
را به شکل زیر پیادهسازی کنید تا این کار را انجام دهد:
-
) جایگزین شوند."iran"
و "server"
به صورت متوالی و بهترتیب با یک فاصله ظاهر شوند، به عبارت "iranserver"
ادغام شوند.یک فایل PHP با نام solution.php
که تابع slugify
در آن پیادهسازی شده است آپلود کنید.
ایرانسرور (IranServer) تصمیم گرفته که برای رونمایی از خدمات جدید خود، یک جلسه مهم میزگردی با حضور مشتریهای کلیدیاش برگزار کند. هدف این جلسه، بحث و بررسی درباره نیازها و تجربههای مشتریان است تا تیم ایرانسرور بتواند خدماتش را بهینهتر کند.
اما یک مشکل وجود دارد؛ ایرانسرور طی تحقیقاتش متوجه شده که اگر مشتریهایی که با هم آشنایی قبلی دارند کنار هم بنشینند، بحثهایشان بیشتر حالت تعارفی پیدا میکند و بازخورد صادقانهای ارائه نمیدهند. برای اینکه بتواند بهترین تحلیلها را داشته باشد، باید اطمینان حاصل کند که هیچ دو نفری که با هم آشنا هستند کنار هم ننشینند.
ایرانسرور میخواهد مشتریها را به شکلی روی دو میز تقسیم کند که:
پروژهی اولیه را از این لینک دانلود کنید.
تابع iranServerRoundTable
را طوری کامل کنید که خروجی خواسته شده را برگرداند:
n
، نشاندهنده تعداد افراد حاضر در مهمانی است.m
، نشاندهنده تعداد آشناییهای قبلی که میان افراد وجود دارد، است.connections
، لیستی از m
آشنایی که هر یک به صورت یک جفت مانند [A, B]
است که نشان دهنده وجود آشنایی قبلی میان دو فرد A و B است.possible
برابر با "YES"
و دو کلید table_1
و table_2
که نشاندهنده لیستی از افراد دور هر میز هستند را بازگرداندید.possible
با مقدار "NO"
بازگرداندید.JSON
باشد.مقدار کلید possible
در هر یک از شرایط ذکر شده، باید دقیقا به شکلی که نوشته شده است باشد. (بزرگی و کوچکی حروف مهم است!)
توجه: این سؤال از نوع ساختاری (Constructive) است، بنابراین پاسخهای ممکن لزوماً یکتا نیستند. با این حال، سیستم داوری پاسخهای شما را بررسی میکند تا اطمینان حاصل شود که شرایط مسئله بهدرستی رعایت شدهاند. تنها در صورتی نمره دریافت خواهید کرد که پیادهسازی شما تمامی این شرایط را برآورده کند و یکی از پاسخهای معتبر مسئله باشد.
این مثال شامل ۶ نفر است که هر یک از آنها دقیقا با دو فرد دیگر آشنایی قبلی دارد؛ به شکل زیر توجه کنید:
حال میتوانیم آنها را به صوزت زیر به دو گروه مجزا تقسیم بندی کرد:
این مثال شامل ۶ نفر است و رابط آشنایی میان آنها به شکل زیر است:
در این مثال نمیتوان افراد را طوری برا سر دو میز نشاند که شرایط خواسته شده را حفظ کند؛ چرا که در تصویر زیر فرد شماره ۵ را نمیتوان دور هیچیک از میزها نشاند!
این مثال شامل ۶ نفر است و رابط آشنایی میان آنها به شکل زیر است:
یکی از حالات مطلوب جهت تقسیم افراد به دو میز موجود به شکل زیر است:
حالت دیگری هم وجود دارد که میتوان این کار را انجام داد اما با شرط دوم (متعادل بودن میزها) را ندارد!
تصویر زیر یکی از حالاتی است که برای این مسئله موردقبول نیست:
یک فایل PHP با نام solution.php
که تابع iranServerRoundTable
در آن پیادهسازی شده است آپلود کنید.
معین پس از مدتها حضور در کوئرا، تصمیم گرفت برای ایجاد تنوع، در مصاحبه استخدامی جونیورا شرکت کند؛ در این مصاحبه از او خواستهاند که پروژهای با استفاده از ORM مخصوص Laravel انجام دهد؛ اما او معتقد است هرکسی میتواند با کمی آموزش، رانندگی را یاد بگیرد؛ ولی فقط یک مکانیک دقیقاً میداند ماشین چگونه کار میکند. بنابراین، تصمیم گرفت با هدف نشاندادن تبحر واقعی خود، بهجای استفاده از ORM آماده، یک ORM اختصاصی به زبان PHP بنویسد و آن را به افتخار خودش معین سخنور (Moein Eloquent) نامگذاری کند. مشکل اینجاست که پروژه اصلی بهاندازه کافی وقتگیر است و معین برای پیادهسازی ORM اختصاصیاش به زمان بیشتری نیاز دارد؛ از همین رو، او از شما کمک میخواهد تا در تکمیل این ORM همراهش باشید.
پروژه اولیه را از این لینک دانلود کنید.
برای راهاندازی پروژه، به پایگاهداده MySQL نیاز است. در این بخش، به راهاندازی MySQL در داکر میپردازیم. اگر تاکنون داکر را نصب نکردهاید، بهکمک این لینک آن را نصب کنید.
برای نصب و راهاندازی کانتینر MySQL بهکمک داکر، دستور زیر را در ترمینال وارد کنید تا ایمیج MySQL دانلود شود و کانتینر راهاندازی شود:
وقتی مراحل نصب کامل شد، دستور زیر را در ترمینال وارد کنید تا با سرویس MySQL در کانتینر ساختهشده ارتباط برقرار کنید:
حال دستور زیر را در محیط تعاملی MySQL وارد کنید تا پایگاهداده testdb
استفاده شود:
اکنون میتوانیم جدول users
را ایجاد کنیم؛ دستور زیر را وارد کنید تا جدول users
در پایگاهداده testdb
ساخته شود:
این پروژه شامل ۵ فایل است که ۳ فایل از قبل تکمیل شدهاند. شما باید فایلهای Model.php
و QueryBuilder.php
را پیادهسازی کنید؛ در ادامه، جزئیات هرکدام از فایلها و متدهای آنها را میتوانید مشاهده کنید:
Database.php
این فایل شامل یک کلاس بهنام Database
است که یک شی pdo
را مقداردهی اولیه میکند و میتوان با استفاده از Database::getInstance()
به آن دسترسی پیدا کرد. این فایل از قبل پیادهسازی شده و شما نیازی به تغییر در آن ندارید؛ در پروژه ارسالی شما، این فایل جایگزین میشود، بنابراین هر تغییری در آن ایجاد کنید، نادیده گرفته خواهد شد. اما اگر میخواهید نتایج کوئریهایتان ببینید، باید مقادیر موجود در این فایل را تغییر دهید تا بتوانید پروژه را روی سیستم خودتان اجرا کنید.
QueryBuilder.php
کلاس QueryBuilder
برای ساخت و اجرای کوئریهای پایگاهداده طراحی شده است. این کلاس امکاناتی مانند SELECT
، WHERE
، GROUP BY
، HAVING
، ORDER BY
، LIMIT
و عملیات CRUD را فراهم میکند. در پیادهسازی متدهای این کلاس، از دیزاین پترن Builder استفاده کنید.
where()
این متد برای افزودن شرطها به کوئریهای WHERE
استفاده میشود.
=
, >
, <
, LIKE
، و ...) وجود دارد.=
) در نظر گرفته میشود.get()
این متد کوئری نهایی را اجرا کرده و نتایج را بهصورت آرایهای از اشیا مدل برمیگرداند.
first()
این متد اولین نتیجهی کوئری را دریافت کرده و مقدار null
را در صورت خالیبودن بازمیگرداند.
sum()
, avg()
, min()
, max()
این متدها مقدار مجموع، میانگین، حداقل و حداکثر یک ستون را محاسبه میکنند.
update()
این متد دادههای جدول را بر اساس WHERE
بهروزرسانی میکند.
WHERE
تعیین نشده باشد، خطا (Exception
) ایجاد میشود.delete()
این متد رکوردهای پایگاهداده را حذف (DELETE
) میکند.
WHERE
تعیین نشده باشد، عملیات متوقف شده و خطا (Exception
) صادر میشود.Model.php
این فایل باید پیادهسازی شود. در این فایل یک کلاس انتزاعی (abstract) بهنام Model
وجود دارد که مدلهای پایگاهداده از آن ارثبری میکنند. این کلاس دو پراپرتی دارد؛ نام جدول و آرایه attributes
. نام جدول باید توسط مدلهایی که از این کلاس ارثبری میکنند Overwrite شود. آرایه attributes
برای ستکردن پراپرتیهای dynamic است. این کلاس دارای چند متد است که باید پیادهسازی شوند:
query()
این متد یک شیء از کلاس QueryBuilder
ایجاد کرده و برای اجرای کوئریهای پایگاهداده مورد استفاده قرار میگیرد.
allQuery()
این متد تمام رکوردهای موجود در جدول مرتبط با مدل را دریافت کرده و بهصورت آرایهای از اشیا برمیگرداند.
findQuery()
این متد یک رکورد خاص را بر اساس مقدار id
جستوجو میکند.
null
برمیگردد.createQuery()
این متد یک رکورد جدید در جدول مرتبط با مدل ایجاد میکند.
firstQuery()
این متد اولین رکورد موجود در جدول را دریافت کرده و بهعنوان نمونهای از مدل برمیگرداند.
hydrateQuery()
این متد یک نمونه از مدل را با دادههای ورودی مقداردهی میکند.
updateQuery()
این متد مقدار فیلدهای یک رکورد را بهروزرسانی میکند.
true
برمیگردد. false
بازمیگردد.deleteQuery()
این متد رکورد مربوطه را از پایگاهداده حذف میکند.
true
برمیگردد. false
بازمیگردد.User.php
این فایل شامل یک کلاس User
است که از کلاس Model
ارثبری میکند. در این کلاس، صرفا مقادیر مورد نیاز تعریف یا مقداردهی شدهاند. شما نیازی به پیادهسازی این فایل ندارید.
autoload.php
این فایل صرفا شامل یک رجیستر است که کلاسهای مورد نیاز را از فایلهای دیگر فراخوانی میکند. شما نیازی به پیادهسازی این فایل ندارید.
QueryBuilder.php
و Model.php
ایجاد میکنید بررسی خواهند شد؛ تغییراتی که در فایلهای دیگر پروژه ایجاد میکنید در سیستم داوری نادیده گرفته خواهند شد.پس از پیادهسازی فایلهای مورد نیاز، فایلهای QueryBuilder.php
و Model.php
را بهصورت فایل Zip ارسال کنید.
میعین (Miein) که به تازگی از کوئرا (Quera) به جونیورا (Juniora) فرار کرده است، پس از مدتی متوجه شده است که حتی جونیورا نیز نمیتواند میزبان رویاهای بلندپروازانهی او باشد! او که پس از دیدن بنر مسابقه استخدامی ایرانسرور (IranServer) در کوئرا و مراجعه به وبسایت آن، با شعار "سریع، امن و همیشه همراه در مسیر رشد کسب و کار" مواجه شده است حالا مطمئن شده که ایرانسرور همان سرزمین رویاهای او خواهد بود.
از آنجایی که رسیدن به هیچ سرزمین رویاهایی بدون چالش و دشواری امکان پذیر نیست، میعین باید چالش ایرانسروری زیر را حل کند تا بتواند مراحل استخدام در این مجموعه را با موفقیت طی کند!
پروژهی اولیه را از این لینک دانلود کنید.
برای اجرای پروژه، باید php
و composer
را از قبل نصب کرده باشید.
composer install
را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.php artisan migrate
استفاده کنید.میعین در چالش ایرانسروری باید بخشی از مدلها، مایگریشنها، سیدر و فکتوریهای ایرانسرور را در یک پروژه لاراولی پیادهسازی کند. همچنین او در نهایت قرار است تا یک مشکل رایج در کار با دیتابیسها را در بخشی از یکی از کنترلرهایی که از قبل در وبسایت ایرانسرور وجود دارد را برطرف کند! توضیح مواردی که باید پیادهسازی شوند به شکل زیر میباشد:
در این بخش میعین باید مایگریشنهای مربوط به ایجاد جداول users
، services
، invoices
و tickets
را که به ترتیب مربوط به اطلاعات کاربران، سرویسهای ارائهشده توسط ایرانسرور به هر کاربر، صورتحسابهای سرویسها و تیکتهای پشتیبانی ارسالی کاربران میباشند را پیادهسازی کند. ساختار مربوط به هر کدام از جداول در بخش زیر مشخص است:
users
و پیادهسازی مایگریشن create_users_table
users
🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
name |
string |
نام کاربر |
email |
string |
ایمیل (منحصربهفرد) |
email_verified_at |
timestamp |
زمان تأیید ایمیل |
password |
string |
رمز عبور |
remember_token |
string |
توکن احراز هویت |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
services
و پیادهسازی مایگریشن create_services_table
services
🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به جدول users |
name |
string |
نام سرویس |
price |
decimal(8,2) |
قیمت سرویس |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
invoices
و پیادهسازی مایگریشن create_invoices_table
invoices
🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
service_id |
bigint |
کلید خارجی به جدول services |
amount |
decimal(8,2) |
مبلغ صورتحساب |
paid_at |
timestamp |
زمان پرداخت |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
tickets
و پیادهسازی مایگریشن create_tickets_table
tickets
🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به جدول users |
subject |
string |
موضوع تیکت |
description |
text |
توضیحات تیکت |
status |
string |
وضعیت (open یا closed ) |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
لیست بخشی از مدلهای ایرانسرور به شرح زیر است که باید به همراه روابطشان در پروژه پیادهسازی شوند:
User
: کاربران ایرانسرورService
: سرویسهایی که ایرانسرور به هر کاربر ارائه میدهدInvoice
: صورتحسابهای پرداختی مربوط به سرویسهای ارائه شدهTicket
: تیکتهای پشتیبانی ارسال شده توسط کاربرانمیعین باید فکتوریهای زیر را در این چالش پیادهسازی کند تا در مرحله بعد با استفاده از یک دیتابیس سیدر (Seeder) دادههای تصادفی برای تست برنامه نوشته شده ایجاد کند. برای بررسی بیشتر هر کدام از فکتوریها روی بخشهای زیر کلیک کنید:
InvoiceFactory
InvoiceFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
service_id |
کلید خارجی به جدول services |
مقدار یک service_id معتبر |
amount |
مبلغ صورت حساب | عدد اعشاری بین ۱۰ تا ۱۰۰ |
paid_at |
زمان پرداخت | null یا یک تاریخ تصادفی |
ServiceFactory
ServiceFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
name |
نام سرویس | یک کلمه تصادفی |
price |
قیمت سرویس | عدد اعشاری بین ۱۰ تا ۱۰۰ |
TicketFactory
TicketFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
subject |
موضوع تیکت | یک جمله تصادفی |
description |
توضیحات تیکت | یک پاراگراف تصادفی |
status |
وضعیت تیکت | open یا closed |
UserFactory
UserFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام کاربر | نام تصادفی |
email |
ایمیل کاربر (یونیک) | ایمیل تصادفی معتبر |
email_verified_at |
زمان تأیید ایمیل | زمان فعلی |
password |
رمز عبور | کلمه password به صورت هش شده |
remember_token |
توکن یادآوری | رشته ۱۰ کاراکتری تصادفی |
یک سیدر (Seeder) با عنوان DatabaseSeeder
در پروژه اولیه وجود دارد که باید به شکل زیر برای ایجاد دادهها در دیتابیس با استفاده از فکتوریهای پیادهسازی شده مورد استفاده قرار بگیرد:
User
ایجاد میشوند.Service
خواهد داشت، پس در نهایت باید ۵۰ سرویس ثبت شود.Invoice
برای سرویسهای مختلف خود دارد. توجه داشته باشید که تعداد کل صورتحسابهای کاربر بین ۳ تا ۵ صورتحساب میباشد و این مقدار به ازای هر کدام از سرویسهای کاربر نیست.Ticket
دارد.UserController
🔗کنترلر UserController
به شکل زیر از قبل در فایلهای پروژه اولیه پیادهسازی شده است که با دریافت تمامی کاربران موجود در برنامه در ویو users.index
لیست کاربران به همراه هر کدام از سرویسهای مربوط به آنها و هر کدام از صورتحسابهای مربوط به هر کدام از آن سرویسها را نمایش میدهد. پیادهسازی انجام شده به شکل بهینهای نیست و دارای مشکل است که میعین باید این مشکل را نیز در پروژه ارسالی خود برطرف سازد:
vendor
را زیپ کرده و ارسال کنید.پس از تلاشهای فراوان معین بالاخره به سرزمین رویاهایش ایرانسرور (IranServer) رسید! از آنجایی که ایرانسرور قصد دارد به زودی سرویسهای ابری جدیدی را به کاربرانش ارائه دهد، معین قرار است همراه با سایر ایرانسروریون به توسعه پنل کاربری جدیدی با عنوان "ایرانگیت" مشغول شود که بر پایه لاراول ۱۲ است که به تازگی معرفی شده است!
از آنجایی که ایرانگیت به زودی قرار است به کاربران معرفی شود، در این سوال شما نیز قرار است تا به کمک معین و ایرانسروریون بروید تا با استفاده از لاراول ۱۲ و استارتر کیت Jetstream بخشهایی از پنل کاربری ایرانگیت را پیادهسازی کنید.
پروژهی اولیه را از این لینک دانلود کنید.
برای اجرای پروژه، باید php
، composer
و npm
را از قبل نصب کرده باشید.
composer install
را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.npm install
را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید. (توجه کنید که این پروژه از Tailwindcss استفاده میکند)npm run dev
را در مسیر پوشه اصلی پروژه اجرا کنید. در صورتی که این دستور را اجرا نکنید، نمیتوانید ویوهای ساخته شده با Tailwindcss را مشاهده کنید.php artisan migrate
استفاده کنید.php artisan test
استفاده کنید.شما در این سوال قرار است تا در پیادهسازی یک سیستم پنل کاربری بر پایه Jetstream که امکانات شخصیسازی شدهای دارد به معین و ایرانسروریون کمک کنید. توجه داشته باشید که برخی مواردی که در این سوال از شما خواسته شده تا پیادهسازی کنید ممکن است تا در این سوال به صورت عملیاتی مورد استفاده قرار نگیرند، اما در داوری سوال مورد ارزیابی قرار خواهند گرفت. توضیح مواردی که باید پیادهسازی شوند به شکل زیر میباشد:
در این بخش به معرفی پروژه ایرانگیت (IranGate) خواهیم پرداخت. بخشهایی از ایرانگیت در این پروژه اولیه این سوال از قبل پیادهسازی شدهاند و نیازی به پیادهسازی دوباره ندارند. پایه ایرانگیت همانطور که پیشتر گفته شد بر اساس استارتر کیت Jetstream میباشد و امکانات پیشفرضی که این استارتر کیت در اختیار توسعهدهندگان قرار میدهد را شامل میشود. موارد پیادهسازی شده در پروژه به شکل زیر هستند:
این پروژه بر اساس امکانات پیشفرض پیادهسازی شده در Jetstream از ویوها، کامپوننتها، روتها و ... این استارتر کیت ارثبری میکند. صفحه پیشفرض پروژه، صفحه پیشفرض لاراول ۱۲ میباشد که به شما برای شروع حل این سوال خوشآمد میگوید:
بخشی از روتهای (Routes) پیادهسازی شده در پروژه نیز به مانند روتهای پیشفرض Jetstream به شکل زیر تعریف شدهاند:
شما میتوانید همانند تصاویر زیر از امکانات پیشفرض پیادهسازی شده در پروژه برای ورود، عضویت، فراموشی رمز عبور و پنل کاربری پیشفرض استفاده کنید:
FeatureController
این کنترلر برای مدیریت دسترسی به ویژگیهای مختلف استفاده میشود. میدلور CheckFeatureAccess
که قرار است توسط شما پیادهسازی شود، بررسی میکند که کاربر دسترسی به یک ویژگی خاص با عنوان featureSlug
را دارد یا نه و پیامی مبنی بر مجوز دسترسی به آن ویژگی برمیگرداند. کنترلر و روت (Route) مربوط به آن به شکل زیر از قبل پیادهسازی شدهاند:
InvoiceController
و ویوی invoices.index
این کنترلر برای نمایش صورتحسابهای کاربران است. متد index
تمام فاکتورها را از طریق ارتباط با مدل Invoice
و برای کاربر وارد شده (auth()->user()
) بدست میآورد و در ویوی invoices.index
به نمایش میگذارد. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
PlanController
و ویوی profile.plans
این کنترلر برای مدیریت اشتراکهای کاربری و تغییرات آنها استفاده میشود. متد index
تمام اشتراکهای کاربری موجود را نمایش میدهد و متد switch
به کاربر این امکان را میدهد که اشتراک کاربری خود را تغییر دهد. اگر اشتراک جدید هزینه داشته باشد، یک صورتحساب برای پرداخت ایجاد میشود که شما باید در بخشی از پیادهسازیهای خود آنها را مدیریت کنید. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
با تغییر اشتراک کاربری، پیام موفقیت در تغییر اشتراک کاربری به نمایش در میآید و شما میتوانید در منوی کاربری بالای صفحه امکانات مختلفی که برای آن اشتراک کاربری تعریف شده است را ببینید و به آنها دسترسی پیدا کنید.
SubscriptionLogController
و ویوی subscription-logs.index
این کنترلر برای نمایش سوابق اشتراکهای کاربری است. متد index
تمام سوابق اشتراکی مربوط به کاربر فعلی را از طریق ارتباط با مدل SubscriptionLog
بدست میآورد و در ویوی subscription-logs.index
به نمایش میگذارد. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
در این بخش، مایگریشنهای مربوط به ایجاد جداول users
، plans
، subscriptions
، invoices
، logs
، coupons
، features
و سایر جداول مرتبط باید پیادهسازی شوند. این جداول برای مدیریت کاربران، اشتراکها، پرداختها، ویژگیهای پلنها و گزارشهای سیستم طراحی شدهاند. ساختار مربوط به هر کدام از جداول در بخش زیر مشخص است:
users
و پیادهسازی مایگریشن create_users_table
users
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
name |
string |
نام کاربر |
email |
string |
ایمیل (منحصربهفرد) |
password |
string |
رمز عبور |
remember_token |
string |
توکن احراز هویت |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
plans
و پیادهسازی مایگریشن create_plans_table
plans
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
name |
string |
نام اشتراک کاربری |
slug |
string |
عنوان یکتای اشتراک کاربری (منحصربهفرد) |
price |
decimal |
قیمت اشتراک کاربری |
duration |
integer |
مدت زمان اشتراک کاربری (به روز) |
is_active |
boolean |
وضعیت فعال بودن اشتراک کاربری |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
subscriptions
و پیادهسازی مایگریشن create_subscriptions_table
subscriptions
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به users |
plan_id |
bigint |
کلید خارجی به plans |
expires_at |
timestamp |
تاریخ انقضای اشتراک کاربری |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
invoices
و پیادهسازی مایگریشن create_invoices_table
invoices
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به users |
plan_id |
bigint |
کلید خارجی به plans |
payment_id |
string |
یک رشته که میتواند null باشد |
amount |
decimal |
مبلغ پرداختی |
status |
string |
وضعیت پرداخت (paid یا pending ) |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
logs
و پیادهسازی مایگریشن create_logs_table
logs
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به users |
action |
string |
نوع فعالیت انجامشده |
details |
text |
جزئیات فعالیت |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
coupons
و پیادهسازی مایگریشن create_coupons_table
coupons
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
code |
string |
کد تخفیف (منحصربهفرد) |
discount |
decimal |
مقدار تخفیف |
max_uses |
integer |
حداکثر دفعات استفاده |
expires_at |
timestamp |
تاریخ انقضای کد |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
features
و پیادهسازی مایگریشن create_features_table
features
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
name |
string |
نام ویژگی |
slug |
string |
شناسه یکتای ویژگی |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
plan_feature
و پیادهسازی مایگریشن create_plan_feature_table
plan_feature
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
plan_id |
bigint |
کلید خارجی به plans |
feature_id |
bigint |
کلید خارجی به features |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
referrals
و پیادهسازی مایگریشن create_referrals_table
referrals
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
referrer_id |
bigint |
کاربری که معرف شخص دیگر است |
referred_id |
bigint |
کاربری که معرفی شده است |
code |
string |
کد ارجاع (منحصربهفرد) |
reward |
decimal |
پاداش دادهشده به معرف |
is_used |
boolean |
وضعیت استفاده از کد (true/false ) |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
subscription_logs
و پیادهسازی مایگریشن create_subscription_logs_table
subscription_logs
:🔗نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
user_id |
bigint |
کلید خارجی به جدول کاربران |
old_subscription_level |
string |
سطح قبلی اشتراک کاربر (nullable) |
new_subscription_level |
string |
سطح جدید اشتراک کاربر (nullable) |
created_at |
timestamp |
زمان ایجاد رکورد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی رکورد |
لیست برخی از مدلهای پیادهسازی شده در سیستم به شرح زیر است که شما باید آنها را همراه با روابط بینشان در این بخش پیادهسازی کنید:
User
: مدیریت کاربران سیستم، شامل اطلاعات کاربری و روابط مرتبط.Plan
: نمایشدهندهی اشتراکهای کاربری، شامل ویژگیها و مشخصات هر پلن.Subscription
: ثبت اطلاعات اشتراک کاربران در اشتراکهای مختلف.Invoice
: صورتحسابهای مربوط به خرید اشتراک و پرداختهای کاربران.Feature
: ویژگیهایی که برای هر اشتراک کاربری قابل ارائه هستند.Coupon
: کدهای تخفیف که کاربران میتوانند برای کاهش هزینه خرید اشتراک کاربری استفاده کنند.Referral
: ثبت سیستم ارجاع کاربران و پاداشهای مربوط به معرفی کاربران جدید.Log
: ثبت اقدامات کاربران در سیستم جهت نظارت و بررسی تاریخچه فعالیتها.SubscriptionLog
: مدیریت تغییرات اشتراک کاربران و تاریخچه ارتقا یا کاهش پلن.در این بخش، فکتوریهای تعریف شده برای تولید دادههای تصادفی جهت تست برنامه معرفی شدهاند. این فکتوریها به منظور تولید دادههای آزمایشی برای جداول مرتبط در پایگاه داده استفاده میشوند:
CouponFactory
CouponFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
code |
کد تخفیف (منحصربهفرد) | یک UUID تصادفی |
discount |
مقدار تخفیف | عدد اعشاری بین ۵ تا ۵۰ |
max_uses |
حداکثر دفعات استفاده | عدد صحیح بین ۱ تا ۱۰۰ |
expires_at |
تاریخ انقضا | تاریخی بین حالا تا ۱ سال آینده |
FeatureFactory
FeatureFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام ویژگی | یک کلمه تصادفی |
slug |
شناسه یکتا (slug) | مقدار یکتای تصادفی |
InvoiceFactory
InvoiceFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
plan_id |
کلید خارجی به جدول plans |
مقدار یک plan_id معتبر |
payment_id |
شناسه پرداخت | یک UUID تصادفی |
amount |
مبلغ صورتحساب | عدد اعشاری بین ۱۰ تا ۱۰۰ |
status |
وضعیت پرداخت | paid یا failed |
LogFactory
LogFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
action |
نوع فعالیت | یک کلمه تصادفی |
details |
جزئیات فعالیت | یک آرایه JSON |
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
PlanFactory
PlanFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام اشتراک | یک کلمه تصادفی |
slug |
شناسه یکتا (slug) | مقدار یکتای تصادفی |
price |
قیمت | عدد اعشاری بین ۱۰ تا ۱۰۰ |
duration |
مدت زمان (روز) | عدد صحیح بین ۳۰ تا ۳۶۵ |
is_active |
وضعیت فعال/غیرفعال بودن اشتراک | مقدار true یا false |
ReferralFactory
ReferralFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
referrer_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
referred_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
code |
کد ارجاع (منحصربهفرد) | یک UUID تصادفی |
reward |
مقدار جایزه ارجاع | عدد اعشاری بین ۰ تا ۱۰۰ |
is_used |
وضعیت استفاده از کد | مقدار true یا false |
UserFactory
UserFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام کاربر | یک نام تصادفی |
email |
ایمیل کاربر (یونیک) | ایمیل تصادفی معتبر |
email_verified_at |
زمان تأیید ایمیل | now() یا null |
password |
رمز عبور | password هششده |
remember_token |
توکن یادآوری | رشته ۱۰ کاراکتری تصادفی |
SubscriptionFactory
SubscriptionFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
شناسه کاربر مرتبط | مقدار تصادفی از UserFactory |
plan_id |
شناسه پلن مرتبط | مقدار تصادفی از PlanFactory |
expires_at |
تاریخ انقضای اشتراک | تاریخ تصادفی بین now و +1 year |
created_at |
زمان ایجاد رکورد | now() |
updated_at |
زمان آخرین بهروزرسانی رکورد | now() |
SubscriptionLogFactory
SubscriptionLogFactory
🔗نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
شناسه کاربر مرتبط | مقدار تصادفی از UserFactory |
old_subscription_level |
سطح قبلی اشتراک کاربر | یک کلمه تصادفی |
new_subscription_level |
سطح جدید اشتراک کاربر | یک کلمه تصادفی |
created_at |
زمان ایجاد رکورد | now() |
updated_at |
زمان آخرین بهروزرسانی رکورد | now() |
درون پروژه اولیه تعدادی سیدر (Seeder) وجود دارد که باید به شکل زیر پیادهسازی شوند، در نهایت قرار است تا این سیدرها با ترتیب درستی در سیدر DatabaseSeeder
مورد استفاده قرار بگیرند. سیدرها باید به شکل زیر ایجاد شوند:
Coupon
ساخته شود.Feature
ساخته شود. همچنین یک اشتراک کاربری با عنوان (Slug) vip
که توسط سیدر مربوطه ساخته شده باید به تمام قابلیتهای ساخته شده توسط این سیدر دسترسی داشته باشد.Invoice
ساخته شود.Log
ساخته شود.Plan
ساخته شود. همچنین باید سه اشتراک کاربری دیگر توسط این سیدر به صورت زیر ساخته شود:Basic
، عنوان (Slug) basic
، قیمت (Price) 0
و مدتزمان (Duration) 30
روزPremium
، عنوان (Slug) premium
، قیمت (Price) 100
و مدتزمان (Duration) 30
روزVip
، عنوان (Slug) vip
، قیمت (Price) 200
و مدتزمان (Duration) 30
روزReferral
ساخته شود.SubscriptionLog
ساخته شود.Subscription
ساخته شود.User
ساخته شود. تمامی این ۵۰ کاربر باید در اشتراک کاربری basic
که قبلا توسط سیدر مربوطه ساخته شده است، مشترک شوند.در این سوال شما باید دو پالیسی FeaturePolicy
و SubscriptionPolicy
را برای مدیریت دسترسی به ویژگیهای خاص و اشتراکهای کاربری خاص به صورت زیر پیادهسازی کنید:
FeaturePolicy
پالیسی FeaturePolicy
به صورت زیر در مسیر app/Policies/FeaturePolicy.php
پیادهسازی شده است که با استفاده از تابعی با نام accessFeature
و دریافت دو مقدار $user
و $feature
به صورت پویا مشخص میکند که آیا کاربر $user
در اشتراک کاربری فعلیاش میتواند میتواند به قابلیت $feature
دسترسی داشته باشد یا نه.
SubscriptionPolicy
پالیسی SubscriptionPolicy
به صورت زیر در مسیر app/Policies/SubscriptionPolicy.php
پیادهسازی شده است که برای مدیریت دسترسیهای کاربران با اشتراکهای کاربری پیشفرض basic
، premium
و vip
به بخشهای مختلف پنلکاربری مورد استفاده قرار میگیرد. این پالیسی با استفاده از هر کدام از سه تابع accessVIP
، accessPremium
و accessBasic
بررسی میکند که آیا اشتراک کاربری $subscription
که متعلق به کاربر $user
میباشد به ترتیب از نوع vip
، premium
و basic
میباشد یا نه.
SubscriptionObserver
🔗در این پروژه شما باید یک Observer را برای مدل Subscription
به صورتی پیادهسازی کنید که هنگام بروزرسانی (Update) شناسه اشتراک کاربری (plan_id
) برای یک کاربر، یک SubscriptionLog
جدید ایجاد کند که مقدار user_id
آن برابر با شناسه کاربری است که اشتراک کاربری آن تغییر کرده است، مقدار old_subscription_level
آن برابر با مقدار عنوان (Slug) اشتراک کاربری قبلی کاربر و مقدار new_subscription_level
آن برابر با مقدار عنوان (Slug) اشتراک جدید کاربر میباشد.
در این پروژه شما باید دو جاب ExpireSubscriptionsJob
و DowngradePendingInvoicesJob
را برای انجام بخشی از پردازشهای مربوط به پنل کاربری به صورت زیر پیادهسازی کنید:
DowngradePendingInvoicesJob
جاب DowngradePendingInvoicesJob
را به صورت زیر در مسیر app/Jobs/DowngradePendingInvoicesJob.php
پیادهسازی کنید تا اشتراک کاربری تمام کاربرانی را حداقل یک صورتحساب (Invoice) با وضعیت (Status) باز (pending
) دارند که بیشتر مساوی یک هفته از صادر شدنشان گذشته است، به اشتراک کاربری basic
تغییر دهد که زمان انقضایی هم نداشته باشد.
ExpireSubscriptionsJob
جاب ExpireSubscriptionsJob
را به صورت زیر در مسیر app/Jobs/ExpireSubscriptionsJob.php
پیادهسازی کنید تا اشتراک کاربری تمام کاربرانی را که تاریخ انقضای اشتراک کاربریشان (expires_at
) به پایان رسیده است (تاریخ انقضای اشتراک کاربریشان کوچکتر مساوی زمان حال است)، به اشتراک کاربری basic
تغییر دهد که زمان انقضایی هم نداشته باشد.
در نهایت پس از تعریف وظایف (Jobs) های مشخص شده، شما باید این دو وظیفه (Job) را طوری در لاراول اسکجول (Schedule) کنید تا به صورت خودکار و هر دقیقه یک بار پردازش شوند.
در نهایت شما قرار است تا میدلور (Middleware) CheckFeatureAccess
را برای پردازش دسترسی در درخواست (Request) کاربر به صورت زیر پیادهسازی کنید:
این میدلور با استفاده از پالیسیهای تعریف شده در مراحل قبل بررسی میکند که آیا کاربر فعلی میتواند به مسیر مشخص شده که از این میدلور استفاده میکند و مشخص کننده مسیر یکی از قابلیتهای موجود در برنامه است دسترسی دارد یا نه. در صورتی که کاربر در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که در برنامه به کل وجود ندارد باید خطای 404
به همراه پیام "این صفحه یافت نشد." و در صورتی که در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که اشتراک کاربریاش اجازه دسترسی به آن را ندارد، باید خطای 403
به همراه پیام "شما دسترسی به این بخش را ندارید." نمایش داده شود. در غیر این صورت ادامه پردازش درخواست کاربر انجام میشود.
vendor
را زیپ کرده و ارسال کنید.