Serhatt
plans
+----+-------+
| id | name |
+----+-------+
| 1 | Basic |
| 2 | Pro |
+----+-------+
subscriptions
+----+---------+---------+---------------------+---------------------+---------------+
| id | user_id | plan_id | starts_at | ends_at | trial_ends_at |
+----+---------+---------+---------------------+---------------------+---------------+
| 1 | 1 | 1 | 2022-11-20 23:40:56 | 2022-12-20 23:40:56 | |
+----+---------+---------+---------------------+---------------------+---------------+
features
+----+----------+-----------------------+
| id | slug | name |
+----+----------+-----------------------+
| 1 | domain | Domain Subscription |
| 2 | bot | Bot Subscription |
| 3 | category | Category Subscription |
+----+----------+-----------------------+
plan_feature
+----+---------+------------+----------+-------+--------+
| id | plan_id | feature_id | pricing | price | limits |
+----+---------+------------+----------+-------+--------+
| 1 | 1 | 1 | per_subs | 4.95 | 3 |
+----+---------+------------+----------+-------+--------+
planables
+---------+------------+-----------------+-------------+-------------------+
| plan_id | feature_id | subscription_id | planable_id | planable_type |
+---------+------------+-----------------+-------------+-------------------+
| 1 | 1 | 1 | 1 | App\Models\Domain |
+---------+------------+-----------------+-------------+-------------------+
---
User
public function subscriptions(): HasMany
{
return $this->hasMany(Subscription::class);
}
public function currentSubscription(): HasOne
{
return $this->hasOne(Subscription::class)
->latestOfMany();
}
Subscription
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function plan(): BelongsTo
{
return $this->belongsTo(Plan::class);
}
public function domains(): MorphedByMany
{
return $this->morphedByMany(Domain::class, 'planable');
}
public function bots(): MorphedByMany
{
return $this->morphedByMany(Bot::class, 'planable');
}
public function categories(): MorphedByMany
{
return $this->morphedByMany(Category::class, 'planable');
}
public function isTrial(): bool
{
return !is_null($this->trial_ends_at);
}
public function hasExpired(): bool
{
if($this->isTrial()) {
return $this->trial_ends_at
->isPast();
}
return $this->ends_at
->isPast();
}
public function expiresIn(string $interval = 'days'): int
{
if($this->hasExpired()) {
return 0;
}
$endsAt = $this->isTrial()
? $this->trial_ends_at
: $this->ends_at;
return match($interval) {
'days' => $endsAt->diffInDays(),
'hours' => $endsAt->diffInHours(),
};
}
Plan
public function subscriptions(): HasMany
{
return $this->hasMany(Subscription::class);
}
public function features(): BelongsToMany
{
return $this->belongsToMany(Feature::class, 'plan_feature', 'plan_id', 'feature_id')
->withPivot('pricing', 'price', 'limits');
}
public function domains(): MorphedByMany
{
return $this->morphedByMany(Domain::class, 'planable');
}
public function bots(): MorphedByMany
{
return $this->morphedByMany(Bot::class, 'planable');
}
public function categories(): MorphedByMany
{
return $this->morphedByMany(Category::class, 'planable');
}
Domain, Bot, Category
public function plans(): MorphToMany
{
return $this->morphToMany(Plan::class, 'planable');
}
---
$now = now()->toImmutable();
$user = User::findOrFail(1);
$plan = Plan::findOrFail(1); // Basic
// Kullanıcı Basic plana şimdiden başlayıp 1 ay boyunca abone oluyor
$subscription = $user->subscriptions()
->create([
'plan_id' => $plan->id,
'starts_at' => $now,
'ends_at' => $now->addMonth()
]);
// Mevcut abonelik
$currentSubscription = $user->currentSubscription;
// Mevcut aboneliğin süresi doldu mu?
$currentSubscription->hasExpired();
// Mevcut abonelik deneme mi?
$currentSubscription->isTrial();
// Mevcut abonelik kaç gün/saat içinde bitiyor?
$currentSubscription->expiresIn(); // ->expiresIn('hours')
// Mevcut abone olunan plan
$plan = $currentSubscription->plan;
// Plan özellikleri
$features = $plan->features;
foreach($features as $feature) {
$feature->pricing; // Fiyatlandırma şekli. Kullanım sayısına göre vs
$feature->price; // Fiyatı
$feature->limits; // Kullanım limiti
$feature->feature->name; // "Domain Aboneliği"
$feature->feature->slug; // "domain"
}
// Kullanıcının abone olduğu planda domain özelliği var mı?
$features->contains('domain');
// Plan dahilinde kullanıcının abone olduğu domainler, botlar ve kategorier
$domains = $currentSubscription->where('plan_id', $plan->id)
->domains()
->get();
$bots = $currentSubscription->where('plan_id', $plan->id)
->bots()
->get();
$categories = $currentSubscription->where('plan_id', $plan->id)
->categories()
->get();
// Kullanıcı mevcut aboneliğin planında example.com domainine abone olmuş mu?
$domains->contains('name', 'example.com');
// Kullanıcı mevcut aboneliğin planında hangi kategori id'lerini görebilir
$categories->pluck('id');
şeklinde gidiyor... Daha yazılacak çok kod var ama benden bu kadar, gerisi sizin.
https://laravel.com/docs/9.x/eloquent-relationships
https://laravel.com/docs/9.x/collections#method-contains