Merhabalar
Laravel de Kullanabileceğim Abonelik Sistemi Arıyorum
Gelişmiş Bir Abonelik Paketi Bilen Varmı Acaba Baya bi Araştırdım Fakat İstediğim Gibi Bir Paket Bulamadım
İstedigim Paket Tam Olarak
https://github.com/Laragear/SubscriptionsDemo
Linkteki Gibi
Birden Fazla Model İçin Birden Fazla Abonelik Türleri vs Gibi Çeşitli Abonelik Türlerim Olacak
Buda Beni Baya Karmaşık Bir yola Soktu Bu Yüzden Bir Paket Arayısına Girdim.
Herhangi Bir Ödeme Sistemi Entegrasyonu Olmak Zorunda Değil
Ödeme Sistemini Kendim Entegre Etmeyi Planlıyorum
Linkteki Paket İsteklerimi Karsılayacak Aslında Sponsor Olup Pakete Erişmeyi Planlıyorum Fakat
Pakettin Ne Derece Saglıklı Oldugu ve Pakette Anlatılan Özellikleri Barındırıp Barındırmadıgı Konusunda Sorun Olurmu Acaba
Varsa Benzer Bildiğiniz Paket Önerileri Alabilirmiyim
Simdiden Herkese Teşekkürler.
Laravel Abonelik Paketi
mgsmus
kontrol saglamam gereken alanları ve değişkenler için bir türlü mantıgı oturtamadım malesef
biraz mantıgı acayım sizlere belki mantıken de yardımınız olur
sistemimde bulunanlar
users, domains, bots ve categories, posts tabloları
kullanıcılar domainlere ve botlara abone olacaklar bir kullanıcını aboneligi 1 domain içeriyorsa 1 domainle kısıtlı kalacak
birden fazla domain eklemeyi içeren bir abonelige sahipse aboneliginin içerdiği kadar domain ekleyebilecek örnegin 5-10 domain gibi
benzer kosullar bots tablosu için de geçerli bots tablosuna üyeler ekleme yapmayacak sadece burda bulunan aboneligini kapsayan botlara ait postları görüntüleyebilecek
örnegin 3 adet bot aboneligi var o 3 botun içeriklerini görüntüleyebilecek
yada tüm botlara erişimi var tüm botlara ait postları görüntüleyebilecek
aynı mantık yine kategoriler içinde geçerli örnegin üye sadece 2 kategori den içerik almak istiyor diger kategorileri istemiyor
bot farketmeksizin o iki kategori içeriklerini alabilmeli
yada 5 bota ait 3 kategori gibi değişik varyasyonlar mevcut olacak
- Düzenlendi
- En İyi YanıtSerhatt tarafından
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
- Düzenlendi
@mgsmus
abi yeniden selamlar
plans
features
plan_feature
bu üç tablo arasındaki ilişkide bir problem mi var acaba
Planlarım
Özelliklerim
Plan Özellik İlişki Tablom
Adımlarım
Plan Ekle (plan adı,plan süresi) kaydet
Özellik Ekle
kaydederken kodum su sekilde
$feature = Feature::create($request->only('name'));
$plan = Plan::find($request->plan_id);
$plan->features()->attach([
[
'feature_id' => $feature->id,
'price' => $request->price ?? 0,
'limits' => $request->limits,
]
]);
burada plana sorgu atıp plan üzerinden özelliği kaydetmem gerekiyor
düzenlerken plan_feature tablosundaki verileri alamıyorum
Feature üzerinden de bir ilişki mi kurulması lazım plan_feature deki verileri almak için
ve aynı sekilde Feature den Plan ı almak için
Ben yanlıs mantıkta ilerliyormusum şuan için bu bölüm tamam
planları ve özellikleri bagımsız ekliyorum
plan düzenleme bölümünde plana ait özellikleri listeleyip
eklemek istedigim özellikleri multi select içinden seçip planı kaydediyorum
$plan->features()->sync($request->features);
seçili olanları direk plana dahil ediyor yada cıkarıyor
plan düzenlemenin altında bir tablo koydum tabloda da
fiyat ve limite güncelleme attırıyorum
mgsmus
abi selamlar
yeni konus acmak istemedim direk aynı konu üzerinde devam edeyim dedim
planables tablosu ve ilişkileri problemlimi abi yukarıdaki anlatımında yoksa benim bakıs acımmı yanlıs
ilk olarak features tablomuza özelliklerimizi herseyden bagımsız olarak ekliyoruz
ardından plans tablomuza planlarımızı ekliyoruz
$plan = Plan::create($request->only('name', 'interval'));
$plan->features()->attach($request->features);
plan adı süresi ve multiselect ile seçtigim özellikler plana dahil oluyor
plan düzenle bölümümde plana ait özelliklerin fiyatlarını ve limitlerini düzenliyorum
bu adımlarda problem yok
ardından bir kullanıcıya abonelik ekliyorum
abonelik ekleme de
user_id,plan_id,trial_ends_at bots ve categories alanlarımı gönderiyorum formdan
controller tarafında kodum su sekilde
$now = now()->toImmutable();
$plan = Plan::find($request->plan_id);
$subscription = new Subscription();
$subscription->user_id = $request->user_id;
$subscription->plan_id = $request->plan_id;
$subscription->starts_at = $now;
$subscription->ends_at = $now->addMonth($plan->interval);
$subscription->trial_ends_at = $request->trial_ends_at;
$subscription->save();
if ($plan->features->contains('id', 5)) {
$subscription->bots()->attach(Bot::all()->pluck('id'));
}else{
$subscription->bots()->attach($request->bots);
}
if ($plan->features->contains('id', 6)) {
$subscription->categories()->attach(Category::all()->pluck('id'));
}else{
$subscription->categories()->attach($request->categories);
}
burda yapmak istedigim formdan gelen planın
özelliklerinde tüm botlar varsa tüm botları abonelige eklemek
özelliklerinde tüm kategoriler varsa tüm kategorileri abonelige eklemek
formdan veriyi gönderdigimde
feature_id ve plan_id sütunları boş geliyor diye hata alıyorum
bu tablo da sadece aboneligin kullanacagı botlar kategoriler ve domainler olmayacakmı
feature_id ve plan_id nin bu tablodaki amacını anlayamadım
bu tablo subscribable isminde
subscription_id,subscribable_id,subscribable_type
sütunlarından olusması gerekmiyormu
burda benim kacırdıgım bir mantık vs mi var acaba
abonelige baglı domainleri kullanıcı kendisi ekleyecek kaç domain ekleme limiti varsa ona göre
üstteki sql yapımda sürelere göre paket belirlemesi yapmıstım bu kullanımımında yanlıs oldugunu anladım
böyle kullanırsam aboneligin özelliklerini listeledigimde
aynı özellik birden fazla dönmüş oluyor o yapıyı düzenledim suan
sizin belirttiginiz gibi
Basic
Premium
Enterprise
planları ekledim
Özelliklerde Domain, Kategori, Bot Standart Kalıyor
Basic te 1 Domain
premiumda 5 Domain
Enterprise da Domain özelliğinin limit alanı null geçiyorum
başlangıç ve bitiş süreleri abonelik tablomda zaten var
satın alırken kac aylık seçtiyse müşteri seçtigi ay*özellikler ücreti toplamı diyecegim
sadece planable tablo yapısı sorunlu gibi duruyor suanda
Serhatt Şöyle bir kod yazmışsınız:
$subscription->bots()->attach($request->bots);
// Bu arada daima $request->input('bots') şeklinde kullanın,
// $request->bots şeklinde kullanmayın
Bu kodun planables tablosuna nasıl kayıt eklediğini görsel olarak anlatmaya çalışayım. $request->input('bots') değeri [3,5,9] olmak üzere;
planables
+---------+------------+-----------------+----------------+-------------------+
| plan_id | feature_id | subscription_id | planable_type | planable_id |
+---------+------------+-----------------+----------------+-------------------+
| null | null | 1 | App\Models\Bot | 3 |
| null | null | 1 | App\Models\Bot | 5 |
| null | null | 1 | App\Models\Bot | 9 |
+---------+------------+-----------------+----------------+-------------------+
| - | - | $subscription | ->bots() | ->attach([3,5,9]) |
+---------+------------+-----------------+----------------+-------------------+
En alttaki satırda hangi kod segmenti hangi alanı dolduruyor onu gösteriyor.
- Ana model olarak $subscription kullanınca subscription_id doluyor.
- bots isimli MorphedByMany ilişkisini kullanınca planable_type alanı doluyor.
- attach([1,2,3]) yaparak da her bir planable_type için planable_id satırı oluşuyor.
Burada yaptığınız hata ise, plan_id ve feature_id vermiyorsunuz. Yazdığınız koda ve verdiğim tabloya vs bakarsanız içinde hiç plan ve feature ile ilgili bir şeyler olmadığını zaten görüyorsunuz. Peki nasıl olması gerekiyordu? Dokümanda da belirttiği gibi:
https://laravel.com/docs/9.x/eloquent-relationships#updating-many-to-many-relationships
ilişki alanları haricindeki alanları her bir planable_id için de ekstra alan olarak vermeniz gerekiyor. Yani açık veri şeklinde ifade etmek gerekirse şunu elde etmelisiniz:
$subscription // subscription_id alanını doldur
->bots() // planable_type alanını doldur
->attach([
3 => [ // planable_id alanını doldur
'plan_id' => 1, // plan_id alanını doldur
'feature_id' => 1 // feature_id alanını doldur
],
5 => [
'plan_id' => 1,
'feature_id' => 1
],
9 => [
'plan_id' => 1,
'feature_id' => 1
]
]);
benim için en temel seviyede plans ve subscriptions yeterli işime yararmı bilemedim ama belki plan_feature tablosuda da olabilir .
Kullanıcılar Free , Basic , Premium planlarından birini secer yıllık ödeme yada aylık ödeme vs tüm kullanıcılar default olarak free dir ancak bazı rotalar ve adresleri ve bazı işlemleri aboneliğe göre yapabiliyo olması gerek örneğin bir rotaya girememesi yada bir formu bellli sayıda oluşturabilmesi gibi işlemler için middlware mı yazmalıyım yada controllerda bu planını mı kontrol edicem