Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Laravel SaaS’im için Spatie Kullanmadan Tam Bir Çoklu Kiracı Sistemi Nasıl Geliştirdim
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Laravel SaaS’im için Spatie Kullanmadan Tam Bir Çoklu Kiracı Sistemi Nasıl Geliştirdim

Yazılım

Laravel SaaS’im için Spatie Kullanmadan Tam Bir Çoklu Kiracı Sistemi Nasıl Geliştirdim

teknomers
Son güncelleme: 16 Mart 2026 14:32
teknomers
Paylaş
Paylaş

Tüm SaaS uygulamaları her istekte “hangi şirket içinde kim ne yapabilir?” sorusuna yanıt vermelidir.

Basit gibi görünüyor. Ama değil.

Kohana.io üzerinde çalışıyorum – küçük işletmeler için bir SaaS CRM/ERP. Çoklu kiracılık modülünü doğru hale getirmek, diğer modüllerden daha uzun sürdü. Bu, kodun karmaşık olduğu anlamına gelmiyor, ama kararlar karmaşık: tek veritabanı mı yoksa ayrı veritabanları mı? Yapılandırma odaklı mı yoksa veritabanı odaklı izinler mi? İzin çelişkilerini nasıl ele alırsınız? Bir kullanıcı 403 aldığında ne olur?

Artık bu modülü LaraFoundry’ye çıkarıyorum – her kişinin bu kararları sıfırdan yapmasına gerek kalmaması için açık kaynak bir Laravel SaaS çerçevesi.

İşte tam olarak nasıl çalıştığı:

<hr/>

<h2>
    <a name="architecture-overview" href="#architecture-overview"></a>
    Mimari Genel Görünüm
</h2>

<div class="table-wrapper-paragraph">
    <table>
        <thead>
            <tr>
                <th>Komponent</th>
                <th>Amaç</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>'BelongsToCompany' trait</td>
                <td>Aktif şirket için otomatik sorgu filtreleme</td>
            </tr>
            <tr>
                <td>'config/roles-and-permissions.php'</td>
                <td>Tüm izinler tek bir yerde tanımlanır</td>
            </tr>
            <tr>
                <td>Gate sınıfları (8 dosya)</td>
                <td>Modül bazında karmaşık yetkilendirme mantığı</td>
            </tr>
            <tr>
                <td>'HasRolesAndPermissions' trait</td>
                <td>Kullanıcı modelinde 5 seviyeli izin hiyerarşisi</td>
            </tr>
            <tr>
                <td>'SetActiveCompanyMiddleware'</td>
                <td>Kiralayan bağlamını otomatik olarak çözer</td>
            </tr>
            <tr>
                <td>'CheckAccessMiddleware'</td>
                <td>Ban + ödeme durumu kontrolleri</td>
            </tr>
            <tr>
                <td>'LayoutDataService'</td>
                <td>İzin farkındalığı olan menü + FAR yönlendirmesi</td>
            </tr>
        </tbody>
    </table>
</div>

<p><strong>Teknoloji yığını:</strong> Laravel 12, Inertia.js v2, Vue 3, Pest PHP</p>

<hr/>

<h2>
    <a name="1-data-isolation-the-belongstocompany-trait" href="#1-data-isolation-the-belongstocompany-trait"></a>
    1. Veri İzolasyonu - BelongsToCompany Traiti
</h2>

<p>Çokça kiracılığın en korkutucu hatası veri sızıntısıdır. Şirket A'nın siparişlerini Şirket B'ye göstermek.</p>
<p>Bir unutulmuş 'where('company_id', ...)' yeter ve GDPR kabusu başlar.</p>
<p>LaraFoundry bunu Eloquent model seviyesinde çözüyor:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="kd">trait</span> <span class="nc">BelongsToCompany</span>

{
protected static function bootBelongsToCompany(): void
{
static::addGlobalScope(‘company’, function (Builder $builder) {
if (auth()->check()) {
$companyId = auth()->user()->getCurrentCompanyId();
if ($companyId) {
$builder->where(
$builder->getModel()->getTable() . ‘.company_id’,
$companyId
);
}
}
});
}

<span class="k">public</span> <span class="k">function</span> <span class="n">scopeForCompany</span><span class="p">(</span><span class="kt">Builder</span> <span class="nv">$query</span><span class="p">,</span> <span class="kt">int</span> <span class="nv">$companyId</span><span class="p">):</span> <span class="kt">Builder</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="nv">$query</span><span class="o">-&gt;</span><span class="nf">withoutGlobalScope</span><span class="p">(</span><span class="s1">'company'</span><span class="p">)</span>
        <span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getTable</span><span class="p">()</span> <span class="mf">.</span> <span class="s1>'.company_id'</span><span class="p">,</span> <span class="nv">$companyId</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">function</span> <span class="n">scopeForAdmin</span><span class="p">(</span><span class="kt">Builder</span> <span class="nv">$query</span><span class="p">):</span> <span class="kt">Builder</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="nv">$query</span><span class="o">-&gt;</span><span class="nf">withoutGlobalScope</span><span class="p">(</span><span class="s1">'company'</span><span class="p">);</span>
<span class="p">}</span>

}

<p><strong>Kullanım:</strong><br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="c1">// Kontrolcü - filtreleme gerekli değil</span>

$orders = Order::query()->latest()->paginate(20);
// Otomatik olarak: SELECT * FROM orders WHERE company_id = 5

Contents
  • 7. İzin Farkındalığı Olan Navigasyon
  • 8. İlk İzin Verilen Rota (FAR) Deseni
  • 9. Test – Çalıştığını Kanıtla
    • İzin hiyerarşisi testi:
    • Şirketler arası izolasyon:
    • Menü görünürlüğü:
    • Sahip İzinli Yollar:

// Yönetici paneli – her şeyi gör
$allOrders = Order::forAdmin()->latest()->paginate(50);

// Belirli bir şirket için sorgu
$report = Order::forCompany(7)->where(‘status’, <span class=”s1>’completed’)->get();

<p>Tablo ön eki ('$builder-&gt;getModel()-&gt;getTable() . '.company_id'') join’lerde belirsiz sütun hatalarını önler. Küçük bir ayrıntı, saatlerce hata ayıklama süresini kurtarır.</p>

<hr/>

<h2>
    <a name="2-configdriven-permission-registration" href="#2-configdriven-permission-registration"></a>
    2. Yapılandırma Tabanlı İzin Kaydı
</h2>

<p>Tüm izinler 'config/roles-and-permissions.php' dosyasında tanımlanır:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="s1">'permissions'</span> <span class="o">=&gt;</span> <span class="p">[</span>
<span class="s1>'orders'</span> <span class="o">=&gt;</span> <span class="p">[</span>
    <span class="s1>'label'</span> <span class="o">=&gt;</span> <span class="s1>'Orders'</span><span class="p">,</span>
    <span class="s1>'permissions'</span> <span class="o">=&gt;</span> <span class="p">[</span>
        <span class="s1>'orders.view'</span> <span class="o">=&gt;</span> <span class="s1>'View orders'</span><span class="p">,</span>
        <span class="s1>'orders.create'</span> <span class="o">=&gt;</span> <span class="s1>'Create orders'</span><span class="p">,</span>
        <span class="s1>'orders.update'</span> <span class="o">=&gt;</span> <span class="s1>'Update orders'</span><span class="p">,</span>
        <span class="s1>'orders.delete'</span> <span class="o">=&gt;</span> <span class="s1>'Delete orders'</span><span class="p">,</span>
        <span class="s1>'orders.approve'</span> <span class="o">=&gt;</span> <span class="s1>'Approve orders'</span><span class="p">,</span>
        <span class="s1>'orders.status_change'</span> <span class="o">=&gt;</span> <span class="s1>'Change order status'</span><span class="p">,</span>
        <span class="s1>'orders.export'</span> <span class="o">=&gt;</span> <span class="s1>'Export orders'</span><span class="p">,</span>
    <span class="p">],</span>
<span class="p">],</span>
<span class="s1>'warehouse'</span> <span class="o">=&gt;</span> <span class="p">[</span>
    <span class="s1>'label'</span> <span class="o">=&gt;</span> <span class="s1>'Warehouse'</span><span class="p">,</span>
    <span class="s1>'permissions'</span> <span class="o">=&gt;</span> <span class="p">[</span>
        <span class="s1>'warehouse.view'</span> <span class="o">=&gt;</span> <span class="s1>'View warehouse'</span><span class="p">,</span>
        <span class="s1>'warehouse.create'</span> <span class="o">=&gt;</span> <span class="s1>'Add to warehouse'</span><span class="p">,</span>
        <span class="s1>'warehouse.update'</span> <span class="o">=&gt;</span> <span class="s1>'Update warehouse'</span><span class="p">,</span>
        <span class="s1>'warehouse.delete'</span> <span class="o">=&gt;</span> <span class="s1>'Delete from warehouse'</span><span class="p">,</span>
        <span class="s1>'warehouse.inventory'</span> <span class="o">=&gt;</span> <span class="s1>'Inventory'</span><span class="p">,</span>
        <span class="s1>'warehouse.transfer'</span> <span class="o">=&gt;</span> <span class="s1>'Transfer goods'</span><span class="p">,</span>
        <span class="s1>'warehouse.reserve'</span> <span class="o">=&gt;</span> <span class="s1>'Reserve goods'</span><span class="p">,</span>
        <span class="s1>'warehouse.assembly'</span> <span class="o">=&gt;</span> <span class="s1>'Order assembly'</span><span class="p">,</span>
    <span class="p">],</span>
<span class="p">],</span>
<span class="c1">// 20+ modül, 100+ izin toplam</span>

],

<p>'AuthServiceProvider' bu yapılandırmayı okur ve her izin için bir Gate kaydeder:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="k">protected</span> <span class="k">function</span> <span class="n">registerPermissionGates</span><span class="p">():</span> <span class="kt">void</span>

{
$permissions = $this->getAllPermissionsFromConfig();

<span class="k">foreach</span> <span class="p">(</span><span class="nv">$permissions</span> <span class="k">as</span> <span class="nv">$permissionSlug</span><span class="p">)</span> <span class="p">{</span>
    <span class="nc">Gate</span><span class="o">::</span><span class="nb">define</span><span class="p">(</span><span class="nv">$permissionSlug</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">User</span> <span class="nv">$user</span><span class="p">,</span> <span class="kt">?Company</span> <span class="nv">$company</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$permissionSlug</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$company</span> <span class="o">=</span> <span class="nv">$company</span> <span class="o">??</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">getActiveCompany</span><span class="p">();</span>
        <span class="k">return</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">hasPermissionTo</span><span class="p">(</span><span class="nv>$permissionSlug</span><span class="p">,</span> <span class="nv">$company</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">}</span>

}

<p>Tek bir döngü. Tüm izinler kaydedildi. Yeni bir izin eklemek ister misiniz? Yapılandırmaya bir satır ekleyin, 'php artisan permissions:sync' çalıştırın.</p>

<hr/>

<h2>
    <a name="3-dedicated-gate-classes-for-complex-logic" href="#3-dedicated-gate-classes-for-complex-logic"></a>
    3. Karmaşık Mantık için Ayrı Gate Sınıfları
</h2>

<p>Yapılandırma, izinlerin %90'ını (basit CRUD) işler. Ancak bazı yetkilendirme mantığı iş kurallarına sahiptir. Örneğin bir çalışanı kaldırma:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="c1">// app/Gates/EmployeeGates.php</span>

class EmployeeGates
{
public static function register(): void
{
Gate::define(<span class=”s1>’employees.remove’, function (User $user, User $employee) {
$company = $user->getActiveCompany();

        <span class="k">if</span> <span class="p">(</span><span class="nv">$user</span><span class="o">-&gt;</span><span class="n">id</span> <span class="o">===</span> <span class="nv">$employee</span><span class="o">-&gt;</span><span class="n">id</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// kendini kaldıramaz</span>
        <span class="k">if</span> <span class="p">(</span><span class="nv">$employee</span><span class="o">-&gt;</span><span class="nf">isOwnerOf</span><span class="p">(</span><span class="nv">$company</span><span class="p">))</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// sahip olanı kaldıramaz</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$employee</span><span class="o">-&gt;</span><span class="n">companies</span><span class="o">-&gt;</span><span class="nf">contains</span><span class="p">(</span><span class="nv">$company</span><span class="p">))</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// yanlış şirket</span>

        <span class="k">if</span> <span class="p">(</span><span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">isOwnerOf</span><span class="p">(</span><span class="nv">$company</span><span class="p">))</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// sahibi herkes kaldırabilir</span>

        <span class="k">return</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">hasPermissionTo</span><span class="p">(</span><span class="s1>'company.employees.remove'</span><span class="p">,</span> <span class="nv>$company</span><span class="p">);</span>
    <span class="p">});</span>

    <span class="c1">// employees.assignRole, employees.grantPermissions,</span>
    <span class="c1">// employees.manageRolesAndPermissions, employees.requestRemoval,</span>
    <span class="c1">// employees.confirmRemoval...</span>
<span class="p">}</span>

}

<p>Her modül kendi Gate sınıfına sahip. Tüm kaydedilmeler 'AuthServiceProvider' tarafından:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="k">protected</span> <span class="k">function</span> <span class="n">registerCustomGates</span><span class="p">():</span> <span class="kt">void</span>

{
CompanyGates::register();
EmployeeGates::register();
RoleGates::register();
ContragentGates::register();
WarehouseGates::register();
ProductionGates::register();
}

<p><strong>Sonuç:</strong> AuthServiceProvider 60 satırın altında kalır. Her Gate sınıfı bağımsız bir şekilde test edilebilir.</p>

<hr/>

<h2>
    <a name="4-the-5level-permission-hierarchy" href="#4-the-5level-permission-hierarchy"></a>
    4. 5 Seviyeli İzin Hiyerarşisi
</h2>

<p>Sistemin merkezi. Beş seviye, üstten alta kontrol edilir:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight plaintext"><code>Level 1: Süper Yönetici -&gt; her şeyi atlar

Level 2: Şirket Sahibi -> kendi şirketine tam erişim
Level 3: İptal Edilmiş -> açıkça engellenmiş (rolleri geçersiz kılar!)
Level 4: Bireysel -> açıkça verilmiş (rolleri geçersiz kılar)
Level 5: Rol tabanlı -> rollerden miras alınan izinler

<p>Uygulama:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="k">public</span> <span class="k">function</span> <span class="n">hasPermissionTo</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$permissionSlug</span><span class="p">,</span> <span class="kt">Company</span><span class="o">|</span><span class="n">int</span><span class="o">|</span><span class="kc">null</span> <span class="nv">$company</span> <span class="o">=</span> <span class="kc">null</span><span class="p">):</span> <span class="kt">bool</span>

{
// Seviye 1: Süper yönetici
if ($this->isSuperAdmin()) return true;

<span class="c1">// Seviye 2: Şirket sahibi</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$company</span> <span class="o">&amp;&amp;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">isOwnerOf</span><span class="p">(</span><span class="nv">$company</span><span class="p">))</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>

<span class="nv">$companyId</span> <span class="o">=</span> <span class="nv">$company</span> <span class="k">instanceof</span> <span class="nc">Company</span> <span class="o">?</span> <span class="nv">$company</span><span class="o">-&gt;</span><span class="n">id</span> <span class="o">:</span> <span class="nv">$company</span><span class="p">;</span>

<span class="c1">// Seviye 3: Açıkça iptal edilmişse kontrol et</span>
<span class="nv">$isRevoked</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">permissions</span><span class="p">()</span>
    <span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="s1>'permissions.slug'</span><span class="p">,</span> <span class="nv">$permissionSlug</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'company_id'</span><span class="p">,</span> <span class="nv">$companyId</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'is_revoked'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">exists</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$isRevoked</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>

<span class="c1">// Seviye 4: Açıkça verilmişse kontrol et</span>
<span class="nv">$isGranted</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">permissions</span><span class="p">()</span>
    <span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="s1>'permissions.slug'</span><span class="p">,</span> <span class="nv">$permissionSlug</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'company_id'</span><span class="p">,</span> <span class="nv">$companyId</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'is_revoked'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">exists</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$isGranted</span><span class="p">)</span> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>

<span class="c1">// Seviye 5: Rol izinlerini kontrol et (şirket bazında)</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">roles</span><span class="p">()</span>
    <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'company_id'</span><span class="p">,</span> <span class="nv">$companyId</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">whereHas</span><span class="p">(</span><span class="s1>'permissions'</span><span class="p">,</span> <span class="k">fn</span><span class="p">(</span><span class="nv">$q</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$q</span><span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="s1>'slug'</span><span class="p">,</span> <span class="nv>$permissionSlug</span><span class="p">))</span>
    <span class="o">-&gt;</span><span class="nf">exists</span><span class="p">();</span>

}

<p><strong>Bu sıranın önemi:</strong></p>
<p>'Manager' rolü ile 'orders.delete' iznine sahip bir durum düşünün. Ama belirli bir yönetici siparişleri silmemeli. Yeni bir rol oluşturmak yerine, şirket sahibi yalnızca o izni iptal eder. Tamamlandı.</p>
<p>Aynı şekilde - 'Worker' rolü 'production.assign'e sahip değil, ama bir kıdemli çalışanın bu izne ihtiyacı var. Bunu bireysel olarak verin.</p>
<p>'is_revoked' bayrağı, 'user_permissions' pivot tablosundaki her şeyi mümkün kılar:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight plaintext"><code>user_permissions: user_id, permission_id, company_id, is_revoked, granted_by_id

<p>Tek bir boolean. Bu kadar çok esneklik.</p>

<hr/>

<h2>
    <a name="5-role-templates-amp-custom-roles" href="#5-role-templates-amp-custom-roles"></a>
    5. Rol Şablonları ve Özel Roller
</h2>

<p>Yeni bir şirket oluşturulduğunda, 5 rol şablonu otomatik olarak klonlanır:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="s1>'role_templates'</span> <span class="o">=&gt;</span> <span class="p">[</span>
<span class="s1>'manager'</span> <span class="o">=&gt;</span> <span class="p">[</span>
    <span class="s1>'name'</span> <span class="o">=&gt;</span> <span class="s1>'Manager'</span><span class="p">,</span>
    <span class="s1>'description'</span> <span class="o">=&gt;</span> <span class="s1>'Siparişleri yönet, karşı taraflarla işlemleri, raporları gör'</span><span class="p">,</span>
    <span class="s1>'permissions'</span> <span class="o">=&gt;</span> <span class="p">[</span>
        <span class="s1>'orders.view'</span><span class="p">,</span>
        <span class="s1>'orders.create'</span><span class="p">,</span>
        <span class="s1>'orders.update'</span><span class="p">,</span>
        <span class="s1>'orders.delete'</span><span class="p">,</span>
        <span class="s1>'contragents.viewAny'</span><span class="p">,</span>
        <span class="s1>'contragents.view'</span><span class="p">,</span>
        <span class="s1>'contragents.create'</span><span class="p">,</span>
        <span class="s1>'production.view'</span><span class="p">,</span>
        <span class="s1>'production.create'</span><span class="p">,</span>
        <span class="s1>'dashboard.view'</span><span class="p">,</span>
        <span class="c1">// ...20+ izin</span>
    <span class="p">],</span>
<span class="p">],</span>
<span class="s1>'accountant'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="cm">/* ... */</span> <span class="p">],</span>
<span class="s1>'storekeeper'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="cm">/* ... */</span> <span class="p">],</span>
<span class="s1>'logistician'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="cm">/* ... */</span> <span class="p">],</span>
<span class="s1>'worker'</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="cm">/* ... */</span> <span class="p">],</span>

],

<p>Şirket sahibi şunları yapabilir:</p>
<ul>
    <li>Şablon rollerini düzenleme (izin ekleme/çıkarma)</li>
    <li>Tamamen özel roller oluşturma</li>
    <li>Bir çalışana birden fazla rol atama</li>
    <li>Herhangi bir rol iznini bireysel kullanıcı bazında geçersiz kılma</li>
    <li>Özel rolleri silme (atama yoksa)</li>
</ul>

<p>Tüm bunlar UI üzerinden. Geliştiriciye ihtiyaç yok.</p>

<hr/>

<h2>
    <a name="6-middleware-stack-tenant-context" href="#6-middleware-stack-tenant-context"></a>
    6. Middleware Yığını - Kiracı Bağlamı
</h2>

<p>Üç middleware bileşeni, çoklu kiracılık yaşam döngüsünü yönetir:</p>

<h3>
    <a name="setactivecompanymiddleware" href="#setactivecompanymiddleware"></a>
    SetActiveCompanyMiddleware
</h3>

<p>Her web isteğinde çalışır. Aktif şirket bağlamını ayarlar:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="k">public</span> <span class="k">function</span> <span class="n">handle</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">,</span> <span class="kt">Closure</span> <span class="nv">$next</span><span class="p">):</span> <span class="kt">Response</span>

{
if (!auth()->check() || !auth()->user()->hasVerifiedEmail()) {
return $next($request);
}

<span class="nv">$user</span> <span class="o">=</span> <span class="nf">auth</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">user</span><span class="p">();</span>

<span class="c1">// Zaten aktif şirket var mı? Sahipliği doğrula</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">session</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">has</span><span class="p">(</span><span class="s1>'active_company_id'</span><span class="p">))</span> <span class="p">{</span>
    <span class="nv">$companyBelongsToUser</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">companies</span><span class="p">()</span>
        <span class="o">-&gt;</span><span class="nf">wherePivot</span><span class="p">(</span><span class="s1>'is_deleted'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
        <span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="s1>'companies.id'</span><span class="p">,</span> <span class="nf">session</span><span class="p">(</span><span class="s1>'active_company_id'</span><span class="p">))</span>
        <span class="o">-&gt;</span><span class="nf">exists</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="nv">$companyBelongsToUser</span><span class="p">)</span> <span class="k">return</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$request</span><span class="p">);</span>
    <span class="nf">session</span><span class="p">()-><span class="nf">forget</span><span class="p">(</span><span class="s1>'active_company_id'</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Öncelik: sahip olunan şirket &gt; çalışan şirketi</span>
<span class="nv">$ownedCompany</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="n">companies</span><span class="o">-&gt;</span><span class="nf">first</span><span class="p">(</span>
    <span class="k">fn</span><span class="p">(</span><span class="nv">$company</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">isOwnerOf</span><span class="p">(</span><span class="nv">$company</span><span class="p">)</span>
<span class="p">);</span>
<span class="nv">$user</span><span class="o">-&gt;</span><span class="nf">setActiveCompany</span><span class="p">(</span><span class="nv">$ownedCompany</span> <span class="o">??</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="n">companies</span><span class="o">-&gt;</span><span class="nf">first</span><span class="p">());</span>

<span class="k">return</span> <span class="nv">$next</span><span class="p">(</span><span class="nv">$request</span><span class="p">);</span>

}

<p>Eğer bir kullanıcı bir şirketten kaldırılırsa, middleware bir sonraki istekte otomatik olarak geçiş yapar. Geçersiz bir durum yok.</p>

<h3>
    <a name="checkaccessmiddleware" href="#checkaccessmiddleware"></a>
    CheckAccessMiddleware
</h3>

<p>Ban durumu (kullanıcı + sahip) ve ödeme durumunu (deneme + abonelik) kontrol eder. Yasaklı kullanıcılar bile destek biletlerine erişebilir.</p>

<h3>
    <a name="checkcompanyaccess" href="#checkcompanyaccess"></a>
    CheckCompanyAccess
</h3>

<p>Sahip olanlara özel yollar: şirket ayarları, çalışan yönetimi, rol yapılandırması.</p>

<p><strong>Middleware sırası bootstrap/app.php dosyasında:</strong><br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight plaintext"><code>1. HandleInertiaRequests
  1. SetActiveCompanyMiddleware

    Sıra önemlidir. Hangi şirket olduğunu bilmeden şirket izinlerini kontrol edemezsiniz.



    7. İzin Farkındalığı Olan Navigasyon

    Her menü öğesinin bir ‘policyName’ değeri vardır:

    [
     => __(),
     => ,
     => ,
    ],
    [
     => __(),
     => ,
     => ,
    ],
    

    Her öğe ‘checkUserAndCompanyPolicy()’ üzerinden geçer:

    private function checkUserAndCompanyPolicy($policyName)
    {
     if ($this->isAdmin()) return true;
     if (empty($policyName)) return false;
     if ($policyName === ) return auth()->check();
    

    $user = auth()->user(); $company = $user->getActiveCompany(); if (!$company) return false;

    if ($user->isOwnerOf($company)) return true;

    return $user->hasPermissionTo(<span class="nv>$policyName, <span class="nv>$company); }

    Bir “Storekeeper” depoyu görür. Bir “Manager” siparişleri, üretimi, karşı tarafları görür. Bir sahibi her şeyi görür. Eğer erişemezseniz – onu görmezsiniz.



    8. İlk İzin Verilen Rota (FAR) Deseni

    Bütün 403 sayfalarını SaaS’tan kaldıran desen.

    Bir 403 hata sayfası göstermek yerine, kullanıcıyı ulaşabileceği ilk sayfaya yönlendirin:

    // bootstrap/app.php - istisna işleyici
    $exceptions->renderable(function (AccessDeniedHttpException $e, Request $request) {
     $layoutService = app(LayoutDataService::class);
    

    return redirect() ->route($layoutService->getFirstAllowedRouteName()) ->with(<span class="s1>'message-disappear-error', __(<span class="s1>'Bu sayfaya erişim izniniz yok')); });

    ‘getFirstAllowedRouteName()’ metodu:

    1. Kullanıcının bu şirket için kaydedilmiş varsayılan sayfasını kontrol eder
    2. O sayfa hala erişilebilir ise – oraya gider
    3. Eğer değilse – temizle, uyarı göster, yedekle
    4. Menü öğelerini dolaş, ilk erişilebilir olanı bul
    5. Yedekleme bildirimlerine geç
    public function getFirstAllowedRouteName(): string
    if (self::isAdmin()) return ;
    

    if (!$this->authUserData->hasUserCompanyOrRole()) { return <span class="s1>'notifications.index'; }

    // Kaydedilmiş varsayılan rotayı kontrol et $saved = auth()->user()->getDefaultRouteForActiveCompany(); if (<span $saved && $this->isBottomLevelRouteAccessible($saved)) { return $saved; }

    // Menü öğelerini dolaş foreach ($this->getNavUpLevelItemsArray() as $navItem) { if (!empty($navItem[<span class="s1>'unvisibly'])) continue; if (<span $this->checkUserAndCompanyPolicy(<span $navItem[<span class="s1>'policyName'])) { return $this->getFirstAllowedBottomLevelRoute(<span $navItem[<span class="s1>'routeName']) ?? <span $navItem[<span class="s1>'routeName']; } }

    return <span class="s1>'notifications.index'; }

    4 yerinde kullanılır: giriş sonrası, 403 sonrası, şirket geçişinde, yan menü “Ana” linkinde.



    9. Test – Çalıştığını Kanıtla

    Çoklu kiracılık hataları sessiz veri sızıntılarıdır. Otomatik testler zorunludur.


    İzin hiyerarşisi testi:

    it(, function () {
     $employee->assignRole(, );
    

    <span class="c1>// Yönetici rolü 'orders.view' iznine sahiptir $employee->revokePermissionFrom(<span class="nv>$ordersView, <span class="nv>$company); <span class="c1>// Yönetici 'warehouse.delete' iznine sahip değildir $employee->givePermissionTo(<span class="nv>$warehouseDelete, <span class="nv>$company);

    expect($employee->hasPermissionTo(<span class="s1>'orders.view', <span class="nv>$company))->toBeFalse() ->and(<span class="nv>$employee->hasPermissionTo(<span class="s1>'warehouse.delete', <span class="nv>$company))->toBeTrue(); });


    Şirketler arası izolasyon:

    it(, function () {
     $roleFromCompany2 = Role::create([
         => ->id,
     ]);
    

    $this->actingAs$ownerOfCompany1); expect(Gate::denies(<span class="s1>'roles.update', <span class="nv>$roleFromCompany2))->toBeTrue(); });


    Menü görünürlüğü:

    it(, function () {
     $employee->givePermissionTo(, );
    

    $menu = $service->getNav'upLevel'); $labels = collect<span class="p>($menu)->pluck'linkName')->toArray<span class="p>();

    expect($labels) ->toContain'Siparişler') ->not->toContain'Üretim') ->not->toContain'Depo') ->not->toContain'Şirketim'); });


    Sahip İzinli Yollar:

    it(, function () {
     $response = $this->withCookie$cookieName, )
         ->get(route());
    

    $response->assertRedirect(); $response->assertSessionHas(<span class="s1>'message-disappear-error'); });

    Toplam test kapsamı: izin hiyerarşisi, şirketler arası izolasyon, Gate yetkilendirmesi, menü görünürlüğü

Kaynak: Orijinal Makale

United Airlines biletleri nasıl alınır? 877-297-0699
Verimli Denetim: PostgreSQL JSONB’yi Laravel’de Ustalıkla Kullanma
Üçüncü Taraf API’lerini Çökertmeyi Durdurun: Laravel İşlerini Yavaşlatma 🚦
Browsershot Alternatifi: PHP Ekran Görüntüsü Oluşturma için Yerine Geçen Bir Çözüm
Hata Takibi Yaptım Çünkü Sentry Beni Yetersiz Hissettirdi
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale COBOL: Programlamanın Tehlikeli Asbest’i Neden Hala Kullanılıyor?
Sonraki Makale Zephyr, RTX 4070 Ti Super’ı iptal etti, SFF’ye geçiyor

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Yeni ABD AI veri merkezleri su sıkıntısı çeken bölgelerde inşa ediliyor
Donanım
Yeni Noob Incremental Kodlarıyla Oyun Deneyiminizi Geliştirin
Oyun
NotebookLM ile Sohbetten Kaynak Deposu Oluşturmanın Yolu Açılıyor
Genel
Apple iOS 27’yi Duyurdu: Yenilikler ve Özellikler Neler?
Liste
Gogs’ta Kritik Sıfır Gün Açığı: Uzaktan Kod İcrası Tehdidi!
Siber Güvenlik
Amazon, AI ile kişiye özel ürün tasarımına izin veriyor
Yapay Zeka
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?