Laravel AI entegrasyonları ile ilgili hemen hemen her makalede sessiz bir varsayım mevcuttur: kimlik doğrulama vardır. Yollar korunur. Jetonlar verilir. API kilitli.
<p>Bu varsayım, gerçek bir şey inşa etmeye oturduğunuzda kırılır.</p>
<p>Laravel Sanctum, framework'ün hafif API jeton kimlik doğrulamasına yanıtıdır. Laravel ile birlikte gelir, Eloquent ile temiz bir şekilde entegre edilir ve tam bir OAuth sunucusu getirmeden iki en yaygın kimlik doğrulama modelini (SPA çerez tabanlı oturumlar ve mobil/dış API jetonlarının verilmesi) yönetir. Bu kılavuz her iki modeli de kapsar ancak kişisel erişim jetonu modeline çok yönelir. Çünkü, kendi ön yüzünüzün, mobil uygulamanızın veya üçüncü taraf bir istemcinin tüketeceği bir API inşa ederken ihtiyacınız olan budur.</p>
<p>Sonunda, üretim için hazır bir kimlik doğrulama katmanınız olacak: yetki kapsamına sahip jeton verimi, korunan yollar, iptal endpoint’leri, Redis ile hız sınırlama ve gerçek yük altında dayanıklı bir çoklu kiracı jeton modeli. Ayrıca, Laravel 11/12 <code>bootstrap/app.php</code> yapılandırma tarzını da ele alacağız; eski <code>Kernel.php</code> referanslarını içermeyeceğiz.</p>
<p>Hadi başlayalım.</p>
<h2>
<a name="what-sanctum-actually-does" href="#what-sanctum-actually-does"></a>
Sanctum Gerçekte Ne Yapar
</h2>
<p>İlk satırdan bir kod yazmadan önce, Sanctum'un ekosistemde nerede yer aldığını anlamalısınız. Sanctum OAuth değildir. Yenileyici jetonlar vermez. Üçüncü parti yetkilendirme akışlarını desteklemez. Bu şeylere ihtiyacınız varsa <a href="https://laravel.com/docs/passport" target="_blank" rel="noopener noreferrer">Laravel Passport</a>'a başvurun.</p>
<p>Sanctum'un oldukça iyi yaptığı şey, <code>users</code> tablonuza bağlanan, karmaşık <code>personal_access_tokens</code> tablosu aracılığıyla hashlenmiş kişisel erişim jetonları vermektir. Her jeton, uygulamanızın çalışma zamanında kontrol ettiği bir dizi <em>yetenek</em> taşıyabilir. Jetonun kendisi rastgele bir dizedir; yalnızca SHA-256 hash'i veritabanında saklanır. Bu, makul bir varsayılandır.</p>
<p>SPA'lar aynı alan adında olduğunda, Sanctum, Laravel’in mevcut oturum kimlik doğrulamasını çerez aracılığıyla kullanır. Bu, <code>EnsureFrontendRequestsAreStateful</code> middleware'inin işini yapmasıdır. Bu konuyu kısaca ele alacağız, ancak bu kılavuzun çoğunluğu API istemcileri için jeton tabanlı kimlik doğrulamaya odaklanmaktadır.</p>
<h2>
<a name="installation-and-setup-laravel-11-12" href="#installation-and-setup-laravel-11-12"></a>
Kurulum ve Ayar (Laravel 11/12)
</h2>
<p>Sanctum Laravel 11 ve 12 ile birlikte gelir. Yeni bir projeye başlıyorsanız, zaten <code>composer.json</code> dosyanızda mevcut. Orada olduğunu doğrulayın:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code>composer show laravel/sanctum
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Eski bir kurulumdan Laravel 11’e güncellenmişseniz, bunu kurmanız gerekebilir:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code>composer require laravel/sanctum
php artisan vendor:publish <span class="nt">--provider</span><span class="o">=</span><span class="s2">"Laravel</span><span class="se">\S</span><span class="s2">anctum</span><span class="se">\S</span><span class="s2">anctumServiceProvider"</span>
php artisan migrate
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Bu yayınlama adımı, <code>config/sanctum.php</code> dosyasını ve <code>personal_access_tokens</code> tablosu için bir migrasyonu bırakır. Öncelikle migrasyonu çalıştırın.</p>
<h3>
<a name="bootstrapappphp-the-laravel-1112-way" href="#bootstrapappphp-the-laravel-1112-way"></a>
bootstrap/app.php - Laravel 11/12 Yolu
</h3>
<p>Laravel 11'de, middleware kayıt işlemi <code>Kernel.php</code>'den çıkarılarak <code>bootstrap/app.php</code>'ye taşındı. Bu, SPA kimlik doğrulaması için Sanctum'un durumsal middleware'ini kaydedeceğiniz yerdir:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// bootstrap/app.php</span>
<span class="kn">use</span> <span class="nc">Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful</span><span class="p">;</span>
<span class="o">-></span><span class="nf">withMiddleware</span><span class="p">(</span><span class="k">function</span> <span class="p">(</span><span class="kt">Middleware</span> <span class="nv">$middleware</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$middleware</span><span class="o">-></span><span class="nf">statefulApi</span><span class="p">();</span>
<span class="p">})</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p><code>$middleware->statefulApi()</code> çağrısı, Sanctum'un SPA middleware'ini kaydetmenin temiz Laravel 11 yoludur. Middleware sınıfını elle listelemekten kaçının; <code>statefulApi()</code> doğru sıralamayı halledecektir.</p>
<p>Jeton tabanlı API'ler (SPA yoksa) için bunu tamamen atlayabilirsiniz. Jetonu koruma altındaki yollarınız <code>auth:sanctum</code> koruyucusunu kullanır. Bu, her şeyi <code>Authorization: Bearer</code> başlığı aracılığıyla yönetir.</p>
<h3>
<a name="configuring-the-guard" href="#configuring-the-guard"></a>
Koruyucuyu Yapılandırma
</h3>
<p><code>config/auth.php</code> dosyasını açın ve <code>api</code> koruyucunuzun Sanctum'a yönlendirildiğinden emin olun:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="s1">'guards'</span> <span class="o">=></span> <span class="p">[</span>
<span class="s1">'web'</span> <span class="o">=></span> <span class="p">[</span>
<span class="s1">'driver'</span> <span class="o">=></span> <span class="s1">'session'</span><span class="p">,</span>
<span class="s1">'provider'</span> <span class="o">=></span> <span class="s1">'users'</span><span class="p">,</span>
<span class="p">],</span>
<span class="s1">'api'</span> <span class="o">=></span> <span class="p">[</span>
<span class="s1">'driver'</span> <span class="o">=></span> <span class="s1">'sanctum'</span><span class="p">,</span>
<span class="s1">'provider'</span> <span class="o">=></span> <span class="s1">'users'</span><span class="p">,</span>
<span class="p">],</span>
<span class="p">],</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Bu, <code>auth:api</code> ve <code>auth:sanctum</code>'un aynı şeyi çözmesine yol açar. Birini seçin ve yollarınızda tutarlılık sağlayın.</p>
<h2>
<a name="preparing-the-user-model" href="#preparing-the-user-model"></a>
Kullanıcı Modelini Hazırlama
</h2>
<p><code>User</code> modelinize <code>HasApiTokens</code> trait'ini ekleyin:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="kn">use</span> <span class="nc">Laravel\Sanctum\HasApiTokens</span><span class="p">;</span>
<span class="kd">class</span> <span class="nc">User</span> <span class="kd">extends</span> <span class="nc">Authenticatable</span>
<span class="p">{</span>
<span class="kn">use</span> <span class="nc">HasApiTokens</span><span class="p">,</span> <span class="nc">HasFactory</span><span class="p">,</span> <span class="nc">Notifiable</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Bu trait, Eloquent'in <code>User</code> modeline <code>createToken()</code>, <code>tokens()</code> ve <code>currentAccessToken()</code> metodlarını kazandırır. Bunların tümü, bu yapının üzerine kuruludur.</p>
<h2>
<a name="issuing-personal-access-tokens" href="#issuing-personal-access-tokens"></a>
Kişisel Erişim Jetonları Yayınlama
</h2>
<p>Jeton yayımı, özel bir endpoint aracılığıyla gerçekleşir. Temiz tutun: bir controller, bir sorumluluk.<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// app/Http/Controllers/Auth/TokenController.php</span>
<span class="kn">namespace</span> <span class="nn">App\Http\Controllers\Auth</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">App\Http\Controllers\Controller</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Illuminate\Http\Request</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Illuminate\Support\Facades\Hash</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Illuminate\Validation\ValidationException</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">App\Models\User</span><span class="p">;</span>
<span class="kd">class</span> <span class="nc">TokenController</span> <span class="kd">extends</span> <span class="nc">Controller</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">issue</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">):</span> <span class="nc">\Illuminate\Http\JsonResponse</span>
<span class="p">{</span>
<span class="nv">$request</span><span class="o">-></span><span class="nf">validate</span><span class="p">([</span>
<span class="s1">'email'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'required'</span><span class="p">,</span> <span class="s1">'email'</span><span class="p">],</span>
<span class="s1">'password'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'required'</span><span class="p">],</span>
<span class="s1">'device_name'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'required'</span><span class="p">,</span> <span class="s1">'string'</span><span class="p">,</span> <span class="s1">'max:255'</span><span class="p">],</span>
<span class="p">]);</span>
<span class="nv">$user</span> <span class="o">=</span> <span class="nc">User</span><span class="o">::</span><span class="nf">where</span><span class="p">(</span><span class="s1">'email'</span><span class="p">,</span> <span class="nv">$request</span><span class="o">-></span><span class="n">email</span><span class="p">)</span><span class="o">-></span><span class="nf">first</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nv">$user</span> <span class="o">||</span> <span class="o">!</span> <span class="nc">Hash</span><span class="o">::</span><span class="nf">check</span><span class="p">(</span><span class="nv">$request</span><span class="o">-></span><span class="n">password</span><span class="p">,</span> <span class="nv">$user</span><span class="o">-></span><span class="n">password</span><span class="p">))</span> <span class="p">{</span>
<span class="k">throw</span> <span class="nc">ValidationException</span><span class="o">::</span><span class="nf">withMessages</span><span class="p">([</span>
<span class="s1">'email'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'The provided credentials are incorrect.'</span><span class="p">],</span>
<span class="p">]);</span>
<span class="p">}</span>
<span class="nv">$token</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-></span><span class="nf">createToken</span><span class="p">(</span>
<span class="nv">$request</span><span class="o">-></span><span class="n">device_name</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'api:read'</span><span class="p">,</span> <span class="s1">'api:write'</span><span class="p">]</span>
<span class="p">);</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1">'token'</span> <span class="o">=></span> <span class="nv">$token</span><span class="o">-></span><span class="n">plainTextToken</span><span class="p">,</span>
<span class="p">]);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">revoke</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">):</span> <span class="nc">\Illuminate\Http\JsonResponse</span>
<span class="p">{</span>
<span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span><span class="o">-></span><span class="nf">currentAccessToken</span><span class="p">()</span><span class="o">-></span><span class="nb">delete</span><span class="p">();</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1">'message'</span> <span class="o">=></span> <span class="s1">'Token revoked.'</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p><code>device_name</code> alanı, Sanctum'un tasarımının bir parçasıdır; kullanıcıların her jetonu hangi cihaz veya istemcinin verdiğini görmesini sağlar. <code>ValidationException</code> yaklaşımı, 401 yerine temiz bir 422 ile alan düzeyinde hatalar döndürür; bu, çoğu ön uç istemcisinin beklediği bir durumdur.</p>
<p>Route kaydı:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// routes/api.php</span>
<span class="kn">use</span> <span class="nc">App\Http\Controllers\Auth\TokenController</span><span class="p">;</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">post</span><span class="p">(</span><span class="s1">'/auth/token'</span><span class="p">,</span> <span class="p">[</span><span class="nc">TokenController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1">'issue'</span><span class="p">]);</span>
<span class="nc">Route</span><span class="o">::</span><span class="nb">delete</span><span class="p">(</span><span class="s1">'/auth/token'</span><span class="p">,</span> <span class="p">[</span><span class="nc">TokenController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1">'revoke'</span><span class="p">])</span><span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1">'auth:sanctum'</span><span class="p">);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<blockquote>
<p><strong>Mimarın Notu:</strong> Jetonları bir route closure'ının içinden asla yayınlamayın. Günlükleme, hız sınırlama veya yetki kapsamı eklemeniz gerektiği anda refaktör yapıyorsunuz demektir. Her zaman ilk günden itibaren özel bir controller kullanın, Service Container daha sonra bağımlılıkları enjekte etmeniz gerektiğinde teşekkür edecektir.</p>
</blockquote>
<h2>
<a name="token-abilities-scoped-permissions" href="#token-abilities-scoped-permissions"></a>
Jeton Yetenekleri: Kapsamlı İzinler
</h2>
<p>Jeton yetenekleri, Sanctum'un OAuth kapsamlarına hafif bir alternatifidir. Yayınlama sırasında bunları dize olarak tanımlarsınız ve sonrasında rota veya controller düzeyinde kontrol edersiniz.<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// Okuma izinli jeton</span>
<span class="nv">$token</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-></span><span class="nf">createToken</span><span class="p">(</span><span class="s1">'mobile-client'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'api:read'</span><span class="p">]);</span>
<span class="c1">// Tam erişim jetonu</span>
<span class="nv">$token</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-></span><span class="nf">createToken</span><span class="p">(</span><span class="s1">'admin-panel'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'api:read'</span><span class="p">,</span> <span class="s1">'api:write'</span><span class="p">,</span> <span class="s1">'api:delete'</span><span class="p">]);</span>
<span class="c1">// AI özellik jetonu</span>
<span class="nv">$token</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-></span><span class="nf">createToken</span><span class="p">(</span><span class="s1">'ai-assistant'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'ai:query'</span><span class="p">,</span> <span class="s1">'api:read'</span><span class="p">]);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Yetenekleri, <code>tokenCan()</code> kullanarak controller'larınızda kontrol edin:<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">store</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">):</span> <span class="kt">JsonResponse</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span><span class="o">-></span><span class="nf">tokenCan</span><span class="p">(</span><span class="s1>'api:write'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1>'error'</span> <span class="o">=></span> <span class="s1>'Insufficient token abilities.'</span><span class="p">],</span> <span class="mi">403</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// yazma işlemi ile devam et</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Ya da doğrudan rota gruplarına <code>abilities</code> middleware'ını uygulayın:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="nc">Route</span><span class="o">::</span><span class="nf">middleware</span><span class="p">([</span><span class="s1>'auth:sanctum'</span><span class="p">,</span> <span class="s1>'abilities:api:read,api:write'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">group</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">post</span><span class="p">(</span><span class="s1>'/documents'</span><span class="p">,</span> <span class="p">[</span><span class="nc">DocumentController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'store'</span><span class="p">]);</span>
<span class="p">});</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">middleware</span><span class="p">([</span><span class="s1>'auth:sanctum'</span><span class="p">,</span> <span class="s1>'ability:api:read'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">group</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">get</span><span class="p">(</span><span class="s1>'/documents'</span><span class="p">,</span> <span class="p">[</span><span class="nc">DocumentController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'index'</span><span class="p">]);</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Dikkat edin ki; <code>abilities</code> (çoğul) jetonun listedeki <em>hepsini</em> içermesini gerektirirken, <code>ability</code> (tekil) <em>en az bir tanesini</em> gerektirir. Bu ayrım, üretim aşamasında insanları yanıltabilir.</p>
<h2>
<a name="protecting-routes" href="#protecting-routes"></a>
Rotaları Koruma
</h2>
<p>JSON API için standart korunan rota grubu:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// routes/api.php</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'auth:sanctum'</span><span class="p">)</span><span class="o">-></span><span class="nf">group</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">apiResource</span><span class="p">(</span><span class="s1>'documents'</span><span class="p">,</span> <span class="nc">DocumentController</span><span class="o">::</span><span class="n">class</span><span class="p">);</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">prefix</span><span class="p">(</span><span class="s1>'ai'</span><span class="p">)</span><span class="o">-></span><span class="nf">group</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">post</span><span class="p">(</span><span class="s1>'/query'</span><span class="p">,</span> <span class="p">[</span><span class="nc">AiQueryController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'query'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'ability:ai:query'</span><span class="p">);</span>
<span class="nc">Route</span><span class="o">::</span><span class="nf">get</span><span class="p">(</span><span class="s1>'/history'</span><span class="p">,</span> <span class="p">[</span><span class="nc">AiQueryController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'history'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'ability:api:read'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p><code>auth:sanctum</code> koruyucusu, Bearer jetonundan kimliği tanımlayarak, <code>$request->user()</code> öğesini istemcinin yaşam döngüsü boyunca kullanılabilir hale getirir.</p>
<h2>
<a name="token-revocation" href="#token-revocation"></a>
Jeton İptali
</h2>
<p>En az üç iptal endpoint'ine ihtiyacınız var:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// Geçerli jeton (bu cihazdan çıkış yap)</span>
<span class="nc">Route</span><span class="o">::</span><span class="nb">delete</span><span class="p">(</span><span class="s1>'/auth/token'</span><span class="p">,</span> <span class="p">[</span><span class="nc">TokenController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'revoke'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'auth:sanctum'</span><span class="p">);</span>
<span class="c1">// Belirli bir jetonu ID ile iptal et</span>
<span class="nc">Route</span><span class="o">::</span><span class="nb">delete</span><span class="p">(</span><span class="s1>'/auth/tokens/{tokenId}'</span><span class="p">,</span> <span class="p">[</span><span class="nc">TokenController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'revokeById'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'auth:sanctum'</span><span class="p">);</span>
<span class="c1">// Tüm jetonları iptal et (her yerden çıkış yap)</span>
<span class="nc">Route</span><span class="o">::</span><span class="nb">delete</span><span class="p">(</span><span class="s1>'/auth/tokens'</span><span class="p">,</span> <span class="p">[</span><span class="nc">TokenController</span><span class="o">::</span><span class="n">class</span><span class="p">,</span> <span class="s1>'revokeAll'</span><span class="p">])</span>
<span class="o">-></span><span class="nf">middleware</span><span class="p">(</span><span class="s1>'auth:sanctum'</span><span class="p">);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Controller metodları:<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">revokeById</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">,</span> <span class="kt">int</span> <span class="nv">$tokenId</span><span class="p">):</span> <span class="kt">JsonResponse</span>
<span class="p">{</span>
<span class="nv">$deleted</span> <span class="o">=</span> <span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span>
<span class="o">-></span><span class="nf">tokens</span><span class="p">()</span>
<span class="o">-></span><span class="nf">where</span><span class="p">(</span><span class="s1>'id'</span><span class="p">,</span> <span class="nv">$tokenId</span><span class="p">)</span>
<span class="o">-></span><span class="nb">delete</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nv">$deleted</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1>'error'</span> <span class="o">=></span> <span class="s1>'Token not found.'</span><span class="p">],</span> <span class="mi">404</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1>'message'</span> <span class="o">=></span> <span class="s1>'Token revoked.'</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">revokeAll</span><span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">):</span> <span class="kt">JsonResponse</span>
<span class="p">{</span>
<span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span><span class="o">-></span><span class="nf">tokens</span><span class="p">()</span><span class="o">-></span><span class="nb">delete</span><span class="p">();</span>
<span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-></span><span class="nf">json</span><span class="p">([</span><span class="s1>'message'</span> <span class="o">=></span> <span class="s1>'All tokens revoked.'</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Jetonların ilişkisini nerede olduğuna göre filtrelemek önemlidir. Aksi takdirde, kullanıcı başka bir kullanıcının jetonunu tahmin ederek silebilir — bu, IDOR güvenlik açığıdır. İlişki kapsamı, silme işlemi gerçekleşmeden önce yalnızca o kullanıcının jetonlarının işlenmesini sağlar.</p>
<blockquote>
<p><strong>Üretim Sıkıntısı:</strong> Jeton iptali, yalnızca veritabanı sorgunuzun hızı kadar hızlıdır. Yoğun eşzamanlı trafik altında, iptal edilen tüm işlemlerin bir indekse sahip olmaması belirgin gecikmelere neden olabilir. <code>personal_access_tokens.tokenable_id</code> üzerinde bir indeks ekleyin, Laravel'in migrasyonu bunu tüm versiyonlarda varsayılan olarak dahil etmez.<br/></p>
</blockquote>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// Yeni bir migrasyonda</span>
<span class="nc">Schema</span><span class="o">::</span><span class="nf">table</span><span class="p">(</span><span class="s1>'personal_access_tokens'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="nf">index</span><span class="p">([</span><span class="s1>'tokenable_type'</span><span class="p">,</span> <span class="s1>'tokenable_id'</span><span class="p">]);</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<h2>
<a name="token-expiry" href="#token-expiry"></a>
Jeton Süresi
</h2>
<p>Varsayılan olarak, Sanctum jetonları süresizdir. <code>config/sanctum.php</code> dosyasında bir sürenin ayarlanması gerekmektedir:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="s1>'expiration'</span> <span class="o">=></span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">30</span><span class="p">,</span> <span class="c1">// 30 gün, dakikalar cinsinden</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Bu işlemi zamanlı prune komutu ile eşleştirin:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="c1">// routes/console.php (Laravel 11)</span>
<span class="nc">Schedule</span><span class="o">::</span><span class="nf">command</span><span class="p">(</span><span class="s1>'sanctum:prune-expired --hours=24'</span><span class="p">)</span><span class="o">-></span><span class="nf">daily</span><span class="p">();</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Kontrol edilmediği takdirde, <code>personal_access_tokens</code> tablosu sonsuza kadar büyür ve her kimlik doğrulama isteğinde yavaşlamaya yol açar.</p>
<h2>
<a name="rate-limiting-with-redis" href="#rate-limiting-with-redis"></a>
Redis ile Hız Sınırlama
</h2>
<p><code>auth:sanctum</code> middleware'ı hız sınırlamasını içermez. Bunu eklemek tamamen sizin sorumluluğunuzdadır. Bu, harici bir AI sağlayıcısını arayan bir API için zorunludur - aksi takdirde, tek bir sorunlu istemci, bütçenizi dakikalar içinde tüketebilir.</p>
<p>Hız sınırlayıcıları <code>bootstrap/app.php</code> içinde tanımlayın:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="kn">use</span> <span class="nc">Illuminate\Cache\RateLimiting\Limit</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Illuminate\Support\Facades\RateLimiter</span><span class="p">;</span>
<span class="nc">RateLimiter</span><span class="o">::</span><span class="k">for</span><span class="p">(</span><span class="s1>'api'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span>
<span class="o">?</span> <span class="nc">Limit</span><span class="o">::</span><span class="nf">perMinute</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span><span class="o">-></span><span class="nf">by</span><span class="p">(</span><span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span><span class="o">-></span><span class="n">id</span><span class="p">)</span>
<span class="o">:</span> <span class="nc">Limit</span><span class="o">::</span><span class="nf">perMinute</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">-></span><span class="nf">by</span><span class="p">(</span><span class="nv">$request</span><span class="o">-></span><span class="nf">ip</span><span class="p">());</span>
<span class="p">});</span>
<span class="nc">RateLimiter</span><span class="o">::</span><span class="k">for</span><span class="p">(</span><span class="s1>'ai'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span>
<span class="o">?</span> <span class="nc">Limit</span><span class="o">::</span><span class="nf">perMinute</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">-></span><span class="nf">by</span><span class="p">(</span><span class="nv">$request</span><span class="o">-></span><span class="nf">user</span><span class="p">()</span><span class="o">-></span><span class="n">id</span><span class="p">)</span>
<span class="o">:</span> <span class="nc">Limit</span><span class="o">::</span><span class="nf">perMinute</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">-></span><span class="nf">by</span><span class="p">(</span><span class="nv">$request</span><span class="o">-></span><span class="nf">ip</span><span class="p">());</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
</svg>
</div>
</div>
</div>
<p>Route gruplarına uygulayın:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span classKaynak: Orijinal Makale


