Gereksinimler ve Ön Hazırlık
Projeye başlamadan önce sisteminizde Laravel 11 veya üzeri bir sürümün kurulu olduğundan emin olmalısınız. Ayrıca veritabanı yönetimi için MySQL veya PostgreSQL kullanmanız önerilir. Geliştirme ortamınızda PHP 8.2+ sürümünün aktif olması, modern tip tanımlamaları ve performans iyileştirmelerinden yararlanmanızı sağlar.
- Laravel 11.x framework
- PHP 8.2 veya üzeri
- Composer paket yöneticisi
- Temel veritabanı bilgisi (Eloquent ORM)
Veritabanı Tasarımı ve Model İlişkileri
Anket sistemi için üç ana tabloya ihtiyacımız var: polls (anketler), poll_options (anket seçenekleri) ve poll_votes (kullanıcı oyları). Bu yapı, verilerin normalize edilmesini ve ölçeklenebilir olmasını sağlar.
// Migration: create_polls_table
Schema::create('polls', function (Blueprint $table) {
$table->id();
$table->string('question');
$table->boolean('is_active')->default(true);
$table->timestamps();
});
// Migration: create_poll_options_table
Schema::create('poll_options', function (Blueprint $table) {
$table->id();
$table->foreignId('poll_id')->constrained()->onDelete('cascade');
$table->string('option_text');
$table->timestamps();
});
Yukarıdaki kod bloğu, anketin temelini oluşturur. poll_id üzerinden kurulan foreignId ilişkisi, anket silindiğinde seçeneklerin de otomatik olarak temizlenmesini (cascade) sağlar.
Oylama Mantığı ve Güvenlik Önlemleri
Kullanıcıların bir ankete birden fazla kez oy vermesini engellemek, sistemin güvenilirliği için şarttır. Bunun için poll_votes tablosunda user_id veya ip_address üzerinden kısıtlama yapmalıyız. Ayrıca SQL injection riskine karşı Laravel'in Eloquent ORM yapısını kullanmak en güvenli yöntemdir.
// Migration: create_poll_votes_table
Schema::create('poll_votes', function (Blueprint $table) {
$table->id();
$table->foreignId('poll_option_id')->constrained();
$table->string('ip_address')->nullable();
$table->foreignId('user_id')->nullable()->constrained();
$table->timestamps();
});
Kritik Güvenlik Uyarısı: Kullanıcıların aynı ankete defalarca oy vermesini engellemek için sadece IP adresi yeterli değildir; çünkü paylaşımlı ağlarda (ofis, okul, kafe) birçok kişi aynı IP'yi kullanabilir. Mümkünse mutlaka oturum açmış kullanıcıları (user_id) baz alarak doğrulama yapın.
Controller Katmanında Oylama İşlemi
Oylama işlemini gerçekleştiren metodumuz, gelen isteğin doğruluğunu kontrol etmeli ve veritabanına kaydı güvenli bir şekilde işlemelidir. validate metodu ile gelen verinin doğruluğunu garanti altına alıyoruz.
public function store(Request $request, $pollId)
{
$request->validate([
'option_id' => 'required|exists:poll_options,id',
]);
// Kullanıcının daha önce oy verip vermediğini kontrol et
$hasVoted = PollVote::where('user_id', auth()->id())
->whereHas('pollOption', function($q) use ($pollId) {
$q->where('poll_id', $pollId);
})->exists();
if ($hasVoted) {
return back()->with('error', 'Bu ankete zaten oy verdiniz.');
}
PollVote::create([
'poll_option_id' => $request->option_id,
'user_id' => auth()->id(),
]);
return back()->with('success', 'Oyunuz başarıyla kaydedildi.');
}
Blade Şablonunda Anket Gösterimi
Anketi kullanıcıya sunarken, seçenekleri bir form içerisinde listelemeliyiz. Kullanıcı bir seçeneği işaretleyip gönderdiğinde, arka planda store metodumuz tetiklenecektir. Tasarımda Bootstrap veya Tailwind CSS kullanarak kullanıcı deneyimini iyileştirebilirsiniz.
@csrf
{{ $poll->question }}
@foreach($poll->options as $option)
{{ $option->option_text }}
@endforeach
Oyla
Anket Sonuçlarını Dinamik Olarak Hesaplama
Anket sonuçlarını göstermek için her seçeneğin aldığı oy sayısını hesaplamamız gerekir. Bunu withCount yöntemiyle verimli bir şekilde yapabiliriz. Bu yaklaşım, N+1 sorgu problemini engelleyerek uygulamanızın performansını artırır.
$poll = Poll::withCount('votes')->with('options.votes')->findOrFail($pollId);
// Seçenek bazlı oy sayısı hesaplama
$results = $poll->options->map(function ($option) use ($poll) {
return [
'text' => $option->option_text,
'count' => $option->votes_count,
'percentage' => $poll->votes_count > 0
? round(($option->votes_count / $poll->votes_count) * 100, 2)
: 0
];
});
| Yöntem | Avantajı | Dezavantajı |
|---|---|---|
| Session Tabanlı | Hızlı ve kurulumu kolay | Çerez temizlenince tekrar oy verilebilir |
| IP Adresi Tabanlı | Üyelik gerektirmez | VPN ve paylaşımlı ağlarda hatalı sonuç verir |
| User ID Tabanlı | En güvenli ve tutarlı | Kullanıcının giriş yapması gerekir |
Sıkça Sorulan Sorular
Anket sonuçlarını sayfa yenilenmeden nasıl güncelleyebilirim?
Laravel Livewire kullanarak, sayfa yenileme ihtiyacı duymadan oylama işlemini gerçekleştirebilir ve sonuçları anlık olarak DOM üzerinde güncelleyebilirsiniz.
Aynı ankete birden fazla seçenek işaretlenmesini nasıl engellerim?
Form içerisinde radio input türünü kullanarak kullanıcının sadece tek bir seçenek seçmesini zorunlu kılabilirsiniz.
Performans için veritabanı indeksleri gerekli mi?
Evet, özellikle poll_id ve user_id gibi sık sorgulanan sütunlara indeks eklemek, binlerce oy olan sistemlerde sorgu hızını ciddi oranda artırır.
Anket süresi nasıl eklenir?
polls tablosuna expires_at adında bir timestamp sütunu ekleyerek, bir middleware ile anketin süresi dolduğunda oylamayı kapatabilirsiniz.
Mobil uyumluluk için ne yapmalıyım?
CSS Grid veya Flexbox kullanarak anket seçeneklerini mobil cihazlarda dikey, masaüstünde ise tercihe göre yan yana dizebilirsiniz.
Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamına almadan önce tüm giriş verilerini (request) doğrulamalı ve yetkilendirme (authorization) katmanlarını eksiksiz yapılandırdığınızdan emin olmalısınız.
Anket Sisteminde Performans Optimizasyonu ve Veritabanı İndeksleme
Anket sistemleri, özellikle anlık trafik alan yayınlarda veritabanı üzerinde yoğun okuma ve yazma işlemlerine neden olur. Kullanıcı sayısı arttıkça, votes tablosundaki sorgular yavaşlamaya başlayabilir. Bu durumu engellemek için veritabanı düzeyinde doğru indeksleme stratejileri uygulamak kritiktir.
Migration dosyanızda poll_id ve user_id sütunları üzerinde bileşik indeks (composite index) tanımlayarak, aynı kullanıcının aynı ankete mükerrer oy verip vermediğini kontrol eden sorguları hızlandırabilirsiniz.
Schema::table('votes', function (Blueprint $table) {
$table->index(['poll_id', 'user_id']);
$table->index('poll_id');
});
Ayrıca, anket sonuçlarını her seferinde count() fonksiyonu ile hesaplatmak yerine, poll_options tablosunda votes_count adında bir sütun tutarak "counter cache" yöntemini kullanabilirsiniz. Bu, okuma performansını ciddi oranda artıracaktır.
Anket Sistemleri İçin Otomatik Test Stratejileri
Geliştirdiğiniz oylama sisteminin kararlılığını korumak için Laravel'in sunduğu Pest veya PHPUnit test araçlarını kullanmalısınız. Özellikle oylama mantığının dış etkenlerden etkilenmediğini doğrulamak için "Feature Test" yazmak en sağlıklı yaklaşımdır.
Aşağıdaki örnek, bir kullanıcının ankete oy verme sürecini simüle eden temel bir test senaryosudur:
public function test_user_can_vote_for_an_option()
{
$user = User::factory()->create();
$poll = Poll::factory()->create();
$option = $poll->options()->create(['text' => 'Laravel']);
$this->actingAs($user)
->post('/vote', [
'poll_id' => $poll->id,
'option_id' => $option->id
])
->assertStatus(200)
->assertJson(['status' => 'success']);
$this->assertDatabaseHas('votes', [
'user_id' => $user->id,
'option_id' => $option->id
]);
}
Bu test ile sisteminize yeni özellikler eklerken mevcut oylama mantığının bozulmadığından emin olabilirsiniz. Özellikle assertDatabaseHas metodu, veritabanı bütünlüğünü doğrulamak için en güvenilir yöntemdir.
İleri Seviye: Yayın İçi Anketlerde Redis Kullanımı
Yüksek trafikli bir platformda, her oy için veritabanına gitmek yerine oyları geçici olarak Redis üzerinde tutabilir ve belirli aralıklarla veritabanına "flush" edebilirsiniz. Bu yöntem, veritabanı üzerindeki "write-heavy" yükünü minimize eder.
Redis kullanarak anket sonuçlarını önbelleğe alma mantığı şu şekilde kurgulanabilir:
use Illuminate\Support\Facades\Redis;
public function castVote(Request $request)
{
$voteKey = "poll_votes:{$request->poll_id}";
// Oyu Redis'e ekle (Atomic increment)
Redis::hincrby($voteKey, $request->option_id, 1);
return response()->json(['message' => 'Oyunuz kaydedildi!']);
}
Bu yaklaşım, anketin anlık sonuçlarını göstermek için veritabanı sorgusu yapmadan doğrudan bellekten veri çekmenizi sağlar. Ancak, verilerin kalıcılığı için bir "cron job" veya "queue worker" aracılığıyla Redis'teki verilerin düzenli olarak ana veritabanına aktarılması gerektiğini unutmamalısınız.
Sonuç
Laravel ile dinamik bir yayın içi anket ve oylama sistemi oluşturmak, hem veritabanı ilişkilerini anlamak hem de kullanıcı etkileşimini artırmak için mükemmel bir projedir. Bu rehberde öğrendiğiniz temel yapıları kullanarak, sistemi daha da geliştirebilir; örneğin Livewire entegrasyonu ile gerçek zamanlı (real-time) bir deneyim sunabilirsiniz. Bir sonraki adım olarak, ankete katılan kullanıcıların demografik verilerini analiz eden bir dashboard paneli geliştirmeyi deneyebilirsiniz.

Yorumlar (0)
Yorum Yaz