پس از تلاشهای فراوان معین بالاخره به سرزمین رویاهایش ایرانسرور (IranServer) رسید! از آنجایی که ایرانسرور قصد دارد به زودی سرویسهای ابری جدیدی را به کاربرانش ارائه دهد، معین قرار است همراه با سایر ایرانسروریون به توسعه پنل کاربری جدیدی با عنوان "ایرانگیت" مشغول شود که بر پایه لاراول ۱۲ است که به تازگی معرفی شده است!
از آنجایی که ایرانگیت به زودی قرار است به کاربران معرفی شود، در این سوال شما نیز قرار است تا به کمک معین و ایرانسروریون بروید تا با استفاده از لاراول ۱۲ و استارتر کیت Jetstream بخشهایی از پنل کاربری ایرانگیت را پیادهسازی کنید.
جزئیات پروژه
پروژهی اولیه را از این لینک دانلود کنید.
ساختار فایلها
irangate
├── app
│ ├── Http
│ │ ├── Middleware
│ │ │ └── CheckFeatureAccess.php
│ ├── Jobs
│ │ ├── DowngradePendingInvoicesJob.php
│ │ └── ExpireSubscriptionsJob.php
│ ├── Models
│ │ ├── Coupon.php
│ │ ├── Feature.php
│ │ ├── Invoice.php
│ │ ├── Log.php
│ │ ├── Plan.php
│ │ ├── Referral.php
│ │ ├── Subscription.php
│ │ ├── SubscriptionLog.php
│ │ └── User.php
│ ├── Observers
│ │ └── SubscriptionObserver.php
│ ├── Policies
│ │ ├── FeaturePolicy.php
│ │ └── SubscriptionPolicy.php
│ ├── Providers
│ │ ├── AppServiceProvider.php
│ │ └── AuthServiceProvider.php
├── bootstrap
│ ├── app.php
│ └── providers.php
├── database
│ ├── factories
│ │ ├── CouponFactory.php
│ │ ├── FeatureFactory.php
│ │ ├── InvoiceFactory.php
│ │ ├── LogFactory.php
│ │ ├── PlanFactory.php
│ │ ├── ReferralFactory.php
│ │ ├── SubscriptionFactory.php
│ │ ├── SubscriptionLogFactory.php
│ │ └── UserFactory.php
│ ├── migrations
│ │ ├── 2025_02_26_164235_create_plans_table.php
│ │ ├── 2025_02_26_164344_create_features_table.php
│ │ ├── 2025_02_26_164408_create_subscriptions_table.php
│ │ ├── 2025_02_26_164432_create_subscription_logs_table.php
│ │ ├── 2025_02_26_164501_create_referrals_table.php
│ │ ├── 2025_02_26_164538_create_coupons_table.php
│ │ ├── 2025_02_26_164610_create_invoices_table.php
│ │ ├── 2025_02_26_164639_create_logs_table.php
│ │ └── 2025_02_26_173343_create_plan_feature_table.php
│ ├── seeders
│ │ ├── CouponSeeder.php
│ │ ├── DatabaseSeeder.php
│ │ ├── FeatureSeeder.php
│ │ ├── InvoiceSeeder.php
│ │ ├── LogSeeder.php
│ │ ├── PlanSeeder.php
│ │ ├── ReferralSeeder.php
│ │ ├── SubscriptionLogSeeder.php
│ │ ├── SubscriptionSeeder.php
│ │ └── UserSeeder.php
├── routes
│ └── console.php
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── server.php
└── webpack.mix.js
راهاندازی پروژه
برای اجرای پروژه، باید php
، composer
و npm
را از قبل نصب کرده باشید.
- ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
- دستور
composer install
را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید. - دستور
npm install
را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید. (توجه کنید که این پروژه از Tailwindcss استفاده میکند) - دستور
npm run dev
را در مسیر پوشه اصلی پروژه اجرا کنید. در صورتی که این دستور را اجرا نکنید، نمیتوانید ویوهای ساخته شده با Tailwindcss را مشاهده کنید. - برای اجرای مایگریشنها از دستور
php artisan migrate
استفاده کنید. - برای اجرای تستهای نمونه، میتوانید از دستور
php artisan test
استفاده کنید.
شما در این سوال قرار است تا در پیادهسازی یک سیستم پنل کاربری بر پایه Jetstream که امکانات شخصیسازی شدهای دارد به معین و ایرانسروریون کمک کنید. توجه داشته باشید که برخی مواردی که در این سوال از شما خواسته شده تا پیادهسازی کنید ممکن است تا در این سوال به صورت عملیاتی مورد استفاده قرار نگیرند، اما در داوری سوال مورد ارزیابی قرار خواهند گرفت. توضیح مواردی که باید پیادهسازی شوند به شکل زیر میباشد:
معرفی کلی پروژه ایرانگیت (IranGate) و موارد پیادهسازی شده
در این بخش به معرفی پروژه ایرانگیت (IranGate) خواهیم پرداخت. بخشهایی از ایرانگیت در این پروژه اولیه این سوال از قبل پیادهسازی شدهاند و نیازی به پیادهسازی دوباره ندارند. پایه ایرانگیت همانطور که پیشتر گفته شد بر اساس استارتر کیت Jetstream میباشد و امکانات پیشفرضی که این استارتر کیت در اختیار توسعهدهندگان قرار میدهد را شامل میشود. موارد پیادهسازی شده در پروژه به شکل زیر هستند:
معرفی امکانات پیشفرض بر پایه Jetstream
این پروژه بر اساس امکانات پیشفرض پیادهسازی شده در Jetstream از ویوها، کامپوننتها، روتها و ... این استارتر کیت ارثبری میکند. صفحه پیشفرض پروژه، صفحه پیشفرض لاراول ۱۲ میباشد که به شما برای شروع حل این سوال خوشآمد میگوید:
بخشی از روتهای (Routes) پیادهسازی شده در پروژه نیز به مانند روتهای پیشفرض Jetstream به شکل زیر تعریف شدهاند:
<?php
use App\Http\Controllers\FeatureController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\PlanController;
use App\Http\Controllers\SubscriptionLogController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
});
شما میتوانید همانند تصاویر زیر از امکانات پیشفرض پیادهسازی شده در پروژه برای ورود، عضویت، فراموشی رمز عبور و پنل کاربری پیشفرض استفاده کنید:
معرفی کنترلر FeatureController
FeatureController
این کنترلر برای مدیریت دسترسی به ویژگیهای مختلف استفاده میشود. میدلور CheckFeatureAccess
که قرار است توسط شما پیادهسازی شود، بررسی میکند که کاربر دسترسی به یک ویژگی خاص با عنوان featureSlug
را دارد یا نه و پیامی مبنی بر مجوز دسترسی به آن ویژگی برمیگرداند. کنترلر و روت (Route) مربوط به آن به شکل زیر از قبل پیادهسازی شدهاند:
<?php
namespace App\Http\Controllers;
class FeatureController extends Controller
{
public function access($featureSlug)
{
return response('Access granted to feature: ' . $featureSlug, 200);
}
}
<?php
use App\Http\Controllers\FeatureController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\PlanController;
use App\Http\Controllers\SubscriptionLogController;
use Illuminate\Support\Facades\Route;
Route::get('/feature/{featureSlug}', [FeatureController::class, 'access'])->middleware('feature.access')->name('feature');
معرفی کنترلر InvoiceController
و ویوی invoices.index
InvoiceController
و ویوی invoices.index
این کنترلر برای نمایش صورتحسابهای کاربران است. متد index
تمام فاکتورها را از طریق ارتباط با مدل Invoice
و برای کاربر وارد شده (auth()->user()
) بدست میآورد و در ویوی invoices.index
به نمایش میگذارد. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
<?php
namespace App\Http\Controllers;
class InvoiceController extends Controller
{
public function index()
{
$invoices = auth()->user()->invoices()->latest()->get();
return view('invoices.index', compact('invoices'));
}
}
معرفی کنترلر PlanController
و ویوی profile.plans
PlanController
و ویوی profile.plans
این کنترلر برای مدیریت اشتراکهای کاربری و تغییرات آنها استفاده میشود. متد index
تمام اشتراکهای کاربری موجود را نمایش میدهد و متد switch
به کاربر این امکان را میدهد که اشتراک کاربری خود را تغییر دهد. اگر اشتراک جدید هزینه داشته باشد، یک صورتحساب برای پرداخت ایجاد میشود که شما باید در بخشی از پیادهسازیهای خود آنها را مدیریت کنید. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
<?php
namespace App\Http\Controllers;
use App\Models\Plan;
use Illuminate\Support\Facades\Auth;
class PlanController extends Controller
{
public function index()
{
$plans = Plan::all();
return view('profile.plans', compact('plans'));
}
public function switch(Plan $plan)
{
$user = Auth::user();
$subscription = $user->subscription;
if (!$subscription) {
$subscription = $user->subscription()->create([
'plan_id' => $plan->id,
'expires_at' => now()->addDays($plan->duration),
]);
} else {
$subscription->plan_id = $plan->id;
$subscription->expires_at = now()->addDays($plan->duration);
}
if ($plan->price > 0) {
$invoice = $user->invoices()->create([
'plan_id' => $plan->id,
'amount' => $plan->price,
'status' => 'pending',
]);
$subscription->expires_at = now()->addDays($plan->duration);
$subscription->save();
return redirect()->route('plans.index')->with('success', 'اشتراک کاربری شما با موفقیت بروزرسانی شد. لطفا نسبت به پرداخت صورت حساب آن اقدام کنید.');
} else {
$subscription->save();
return redirect()->route('plans.index')->with('success', 'اشتراک کاربری شما با موفقیت بروزرسانی شد.');
}
}
}
با تغییر اشتراک کاربری، پیام موفقیت در تغییر اشتراک کاربری به نمایش در میآید و شما میتوانید در منوی کاربری بالای صفحه امکانات مختلفی که برای آن اشتراک کاربری تعریف شده است را ببینید و به آنها دسترسی پیدا کنید.
معرفی کنترلر SubscriptionLogController
و ویوی subscription-logs.index
SubscriptionLogController
و ویوی subscription-logs.index
این کنترلر برای نمایش سوابق اشتراکهای کاربری است. متد index
تمام سوابق اشتراکی مربوط به کاربر فعلی را از طریق ارتباط با مدل SubscriptionLog
بدست میآورد و در ویوی subscription-logs.index
به نمایش میگذارد. کنترلر مربوط به آن به شکل زیر از قبل پیادهسازی شده است:
<?php
namespace App\Http\Controllers;
class SubscriptionLogController extends Controller
{
public function index()
{
$subscriptionLogs = auth()->user()->subscriptionLogs()->latest()->get();
return view('subscription-logs.index', compact('subscriptionLogs'));
}
}
پیادهسازی جداول و نوشتن مایگریشنها (Migrations)
در این بخش، مایگریشنهای مربوط به ایجاد جداول users
، plans
، subscriptions
، invoices
، logs
، coupons
، features
و سایر جداول مرتبط باید پیادهسازی شوند. این جداول برای مدیریت کاربران، اشتراکها، پرداختها، ویژگیهای پلنها و گزارشهای سیستم طراحی شدهاند. ساختار مربوط به هر کدام از جداول در بخش زیر مشخص است:
ساختار جدول users
و پیادهسازی مایگریشن create_users_table
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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن create_features_table
ساختار جدول features
:
نام ستون | نوع داده | توضیحات |
---|---|---|
id |
bigint |
کلید اصلی |
name |
string |
نام ویژگی |
slug |
string |
شناسه یکتای ویژگی |
created_at |
timestamp |
زمان ایجاد |
updated_at |
timestamp |
زمان آخرین بهروزرسانی |
ساختار جدول plan_feature
و پیادهسازی مایگریشن create_plan_feature_table
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
و پیادهسازی مایگریشن 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
و پیادهسازی مایگریشن 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
: مدیریت تغییرات اشتراک کاربران و تاریخچه ارتقا یا کاهش پلن.
پیادهسازی روابط بین مدلها
- هر کاربر (User) میتواند دارای یک اشتراک کاربری (Subscription) باشد.
- هر اشتراک کاربری (Subscription) به یک اشتراک (Plan) متصل است.
- هر اشتراک (Plan) میتواند چندین ویژگی (Feature) داشته باشد.
- هر ویژگی (Feature) میتواند به چندین اشتراک (Plan) مرتبط باشد.
- هر کاربر میتواند چندین صورتحساب (Invoice) برای خرید پلنهای مختلف داشته باشد.
- هر صورتحساب (Invoice) به دقیقا یک اشتراک (Plan) و دقیقا یک کاربر (User) مرتبط است.
- هر کاربر (User) میتواند از کدهای تخفیف (Coupon) استفاده کند.
- هر کاربر (User) میتواند از طریق سیستم ارجاع، کاربران جدید را معرفی کرده و پاداش دریافت کند.
- هر کاربر (User) میتواند چندین لاگ (Log) از فعالیتهای خود داشته باشد. همچنین هر لاگ مربوط به دقیقا یک کاربر (User) است.
- هر کاربر (User) میتواند چندین تاریخچه تغییرات اشتراک (SubscriptionLogs) داشته باشد.
پیادهسازی فکتوریها (Factories)
در این بخش، فکتوریهای تعریف شده برای تولید دادههای تصادفی جهت تست برنامه معرفی شدهاند. این فکتوریها به منظور تولید دادههای آزمایشی برای جداول مرتبط در پایگاه داده استفاده میشوند:
ساختار فکتوری CouponFactory
CouponFactory
فکتوری CouponFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
code |
کد تخفیف (منحصربهفرد) | یک UUID تصادفی |
discount |
مقدار تخفیف | عدد اعشاری بین ۵ تا ۵۰ |
max_uses |
حداکثر دفعات استفاده | عدد صحیح بین ۱ تا ۱۰۰ |
expires_at |
تاریخ انقضا | تاریخی بین حالا تا ۱ سال آینده |
ساختار فکتوری FeatureFactory
FeatureFactory
فکتوریFeatureFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام ویژگی | یک کلمه تصادفی |
slug |
شناسه یکتا (slug) | مقدار یکتای تصادفی |
ساختار فکتوری InvoiceFactory
InvoiceFactory
فکتوری InvoiceFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
plan_id |
کلید خارجی به جدول plans |
مقدار یک plan_id معتبر |
payment_id |
شناسه پرداخت | یک UUID تصادفی |
amount |
مبلغ صورتحساب | عدد اعشاری بین ۱۰ تا ۱۰۰ |
status |
وضعیت پرداخت | paid یا failed |
ساختار فکتوری LogFactory
LogFactory
فکتوری LogFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
action |
نوع فعالیت | یک کلمه تصادفی |
details |
جزئیات فعالیت | یک آرایه JSON |
user_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
ساختار فکتوری PlanFactory
PlanFactory
فکتوری PlanFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام اشتراک | یک کلمه تصادفی |
slug |
شناسه یکتا (slug) | مقدار یکتای تصادفی |
price |
قیمت | عدد اعشاری بین ۱۰ تا ۱۰۰ |
duration |
مدت زمان (روز) | عدد صحیح بین ۳۰ تا ۳۶۵ |
is_active |
وضعیت فعال/غیرفعال بودن اشتراک | مقدار true یا false |
ساختار فکتوری ReferralFactory
ReferralFactory
فکتوری ReferralFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
referrer_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
referred_id |
کلید خارجی به جدول users |
مقدار یک user_id معتبر |
code |
کد ارجاع (منحصربهفرد) | یک UUID تصادفی |
reward |
مقدار جایزه ارجاع | عدد اعشاری بین ۰ تا ۱۰۰ |
is_used |
وضعیت استفاده از کد | مقدار true یا false |
ساختار فکتوری UserFactory
UserFactory
فکتوری UserFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
name |
نام کاربر | یک نام تصادفی |
email |
ایمیل کاربر (یونیک) | ایمیل تصادفی معتبر |
email_verified_at |
زمان تأیید ایمیل | now() یا null |
password |
رمز عبور | password هششده |
remember_token |
توکن یادآوری | رشته ۱۰ کاراکتری تصادفی |
ساختار فکتوری SubscriptionFactory
SubscriptionFactory
فکتوری SubscriptionFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
شناسه کاربر مرتبط | مقدار تصادفی از UserFactory |
plan_id |
شناسه پلن مرتبط | مقدار تصادفی از PlanFactory |
expires_at |
تاریخ انقضای اشتراک | تاریخ تصادفی بین now و +1 year |
created_at |
زمان ایجاد رکورد | now() |
updated_at |
زمان آخرین بهروزرسانی رکورد | now() |
ساختار فکتوری SubscriptionLogFactory
SubscriptionLogFactory
فکتوری SubscriptionLogFactory
نام ستون | توضیحات | مقادیر ممکن |
---|---|---|
user_id |
شناسه کاربر مرتبط | مقدار تصادفی از UserFactory |
old_subscription_level |
سطح قبلی اشتراک کاربر | یک کلمه تصادفی |
new_subscription_level |
سطح جدید اشتراک کاربر | یک کلمه تصادفی |
created_at |
زمان ایجاد رکورد | now() |
updated_at |
زمان آخرین بهروزرسانی رکورد | now() |
پیادهسازی سیدر (Seeder)
درون پروژه اولیه تعدادی سیدر (Seeder) وجود دارد که باید به شکل زیر پیادهسازی شوند، در نهایت قرار است تا این سیدرها با ترتیب درستی در سیدر DatabaseSeeder
مورد استفاده قرار بگیرند. سیدرها باید به شکل زیر ایجاد شوند:
- دقیقا ۵۰ کد تخفیف (Coupon) با استفاده از فکتوری
Coupon
ساخته شود. - دقیقا ۲۰ قابلیت (Feature) با استفاده از فکتوری
Feature
ساخته شود. همچنین یک اشتراک کاربری با عنوان (Slug)vip
که توسط سیدر مربوطه ساخته شده باید به تمام قابلیتهای ساخته شده توسط این سیدر دسترسی داشته باشد. - دقیقا ۱۰۰ صورت حساب (Invoice) با استفاده از فکتوری
Invoice
ساخته شود. - دقیقا ۲۰۰ لاگ (Log) با استفاده از فکتوری
Log
ساخته شود. - دقیقا ۱۰ اشتراک (Plan) با استفاده از فکتوری
Plan
ساخته شود. همچنین باید سه اشتراک کاربری دیگر توسط این سیدر به صورت زیر ساخته شود:- اشتراک پایه با نام (Name)
Basic
، عنوان (Slug)basic
، قیمت (Price)0
و مدتزمان (Duration)30
روز - اشتراک پریمیوم با نام (Name)
Premium
، عنوان (Slug)premium
، قیمت (Price)100
و مدتزمان (Duration)30
روز - اشتراک ویژه با نام (Name)
Vip
، عنوان (Slug)vip
، قیمت (Price)200
و مدتزمان (Duration)30
روز
- اشتراک پایه با نام (Name)
- دقیقا ۵۰ ارجاع (Referral) با استفاده از فکتوری
Referral
ساخته شود. - دقیقا ۱۵۰ گزارش اشتراک (SubscriptionLog) با استفاده از فکتوری
SubscriptionLog
ساخته شود. - دقیقا ۱۰۰ اشتراک کاربری (Subscription) با استفاده از فکتوری
Subscription
ساخته شود. - دقیقا ۵۰ کاربر (User) با استفاده از فکتوری
User
ساخته شود. تمامی این ۵۰ کاربر باید در اشتراک کاربریbasic
که قبلا توسط سیدر مربوطه ساخته شده است، مشترک شوند.
پیادهسازی پالیسیها (Policies)
در این سوال شما باید دو پالیسی FeaturePolicy
و SubscriptionPolicy
را برای مدیریت دسترسی به ویژگیهای خاص و اشتراکهای کاربری خاص به صورت زیر پیادهسازی کنید:
ساختار پالیسی FeaturePolicy
FeaturePolicy
پالیسی FeaturePolicy
به صورت زیر در مسیر app/Policies/FeaturePolicy.php
پیادهسازی شده است که با استفاده از تابعی با نام accessFeature
و دریافت دو مقدار $user
و $feature
به صورت پویا مشخص میکند که آیا کاربر $user
در اشتراک کاربری فعلیاش میتواند میتواند به قابلیت $feature
دسترسی داشته باشد یا نه.
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Feature;
class FeaturePolicy
{
public function accessFeature(User $user, Feature $feature)
{
// TODO
}
}
- از این پالیسی برای مدیریت دسترسی بر اساس قابلیتها (Features) استفاده خواهد شد.
ساختار پالیسی SubscriptionPolicy
SubscriptionPolicy
پالیسی SubscriptionPolicy
به صورت زیر در مسیر app/Policies/SubscriptionPolicy.php
پیادهسازی شده است که برای مدیریت دسترسیهای کاربران با اشتراکهای کاربری پیشفرض basic
، premium
و vip
به بخشهای مختلف پنلکاربری مورد استفاده قرار میگیرد. این پالیسی با استفاده از هر کدام از سه تابع accessVIP
، accessPremium
و accessBasic
بررسی میکند که آیا اشتراک کاربری $subscription
که متعلق به کاربر $user
میباشد به ترتیب از نوع vip
، premium
و basic
میباشد یا نه.
<?php
namespace App\Policies;
use App\Models\Subscription;
use App\Models\User;
use App\Models\Plan;
class SubscriptionPolicy
{
public function accessVip(User $user, Subscription $subscription)
{
// TODO
}
public function accessPremium(User $user, Subscription $subscription)
{
// TODO
}
public function accessBasic(User $user, Subscription $subscription)
{
// TODO
}
}
- از این پالیسی برای مدیریت دسترسی بر اساس اشتراکهای کاربری (Subscriptions) استفاده خواهد شد.
پیادهسازی Observer SubscriptionObserver
در این پروژه شما باید یک Observer را برای مدل Subscription
به صورتی پیادهسازی کنید که هنگام بروزرسانی (Update) شناسه اشتراک کاربری (plan_id
) برای یک کاربر، یک SubscriptionLog
جدید ایجاد کند که مقدار user_id
آن برابر با شناسه کاربری است که اشتراک کاربری آن تغییر کرده است، مقدار old_subscription_level
آن برابر با مقدار عنوان (Slug) اشتراک کاربری قبلی کاربر و مقدار new_subscription_level
آن برابر با مقدار عنوان (Slug) اشتراک جدید کاربر میباشد.
پیادهسازی وظایف (Jobs) و اسکجول (Schedule) کردن
در این پروژه شما باید دو جاب ExpireSubscriptionsJob
و DowngradePendingInvoicesJob
را برای انجام بخشی از پردازشهای مربوط به پنل کاربری به صورت زیر پیادهسازی کنید:
ساختار جاب DowngradePendingInvoicesJob
DowngradePendingInvoicesJob
جاب DowngradePendingInvoicesJob
را به صورت زیر در مسیر app/Jobs/DowngradePendingInvoicesJob.php
پیادهسازی کنید تا اشتراک کاربری تمام کاربرانی را حداقل یک صورتحساب (Invoice) با وضعیت (Status) باز (pending
) دارند که بیشتر مساوی یک هفته از صادر شدنشان گذشته است، به اشتراک کاربری basic
تغییر دهد که زمان انقضایی هم نداشته باشد.
<?php
namespace App\Jobs;
use App\Models\User;
use App\Models\Plan;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class DowngradePendingInvoicesJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
// TODO
}
}
- از این پالیسی برای مدیریت دسترسی بر اساس قابلیتها (Features) استفاده خواهد شد.
ساختار جاب ExpireSubscriptionsJob
ExpireSubscriptionsJob
جاب ExpireSubscriptionsJob
را به صورت زیر در مسیر app/Jobs/ExpireSubscriptionsJob.php
پیادهسازی کنید تا اشتراک کاربری تمام کاربرانی را که تاریخ انقضای اشتراک کاربریشان (expires_at
) به پایان رسیده است (تاریخ انقضای اشتراک کاربریشان کوچکتر مساوی زمان حال است)، به اشتراک کاربری basic
تغییر دهد که زمان انقضایی هم نداشته باشد.
<?php
namespace App\Jobs;
use App\Models\Subscription;
use App\Models\Plan;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ExpireSubscriptionsJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
// TODO
}
}
- از این پالیسی برای مدیریت دسترسی بر اساس اشتراکهای کاربری (Subscriptions) استفاده خواهد شد.
در نهایت پس از تعریف وظایف (Jobs) های مشخص شده، شما باید این دو وظیفه (Job) را طوری در لاراول اسکجول (Schedule) کنید تا به صورت خودکار و هر دقیقه یک بار پردازش شوند.
پیادهسازی میدلور (Middleware)
در نهایت شما قرار است تا میدلور (Middleware) CheckFeatureAccess
را برای پردازش دسترسی در درخواست (Request) کاربر به صورت زیر پیادهسازی کنید:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckFeatureAccess
{
public function handle(Request $request, Closure $next): Response
{
// TODO
}
}
این میدلور با استفاده از پالیسیهای تعریف شده در مراحل قبل بررسی میکند که آیا کاربر فعلی میتواند به مسیر مشخص شده که از این میدلور استفاده میکند و مشخص کننده مسیر یکی از قابلیتهای موجود در برنامه است دسترسی دارد یا نه. در صورتی که کاربر در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که در برنامه به کل وجود ندارد باید خطای 404
به همراه پیام "این صفحه یافت نشد." و در صورتی که در تلاش بود تا به مسیر مربوط به قابلیتی دسترسی پیدا کند که اشتراک کاربریاش اجازه دسترسی به آن را ندارد، باید خطای 403
به همراه پیام "شما دسترسی به این بخش را ندارید." نمایش داده شود. در غیر این صورت ادامه پردازش درخواست کاربر انجام میشود.
آنچه باید آپلود کنید
- توجه: پس از پیادهسازی موارد خواسته شده، کل فایلهای پروژه بهجز پوشهی
vendor
را زیپ کرده و ارسال کنید. - توجه: شما مجاز به افزودن فایل جدیدی در این ساختار نیستید و تنها باید تغییرات را در فایلهای موجود اعمال کنید.
- توجه: که نام فایل Zip اهمیتی ندارد.
ارسال پاسخ برای این سؤال