#Search Engine Example
- PHP 8.2+
- Laravel 12.x
- MySQL 8.0 - Veritabanı
- Redis 7.2 - Cache ve Queue
- Guzzle HTTP - API istekleri
- Docker & Docker Compose - Containerization
- Nginx - Web server
- Supervisor - Process management
- Pest PHP - Testing framework
- GitHub Actions - CI/CD
- Laravel Telescope - Application debugging
- Laravel Horizon - Queue monitoring
- Docker ve Docker Compose
1. Projeyi klonlayın:
git clone https://github.com/wMBLw/search-engine-example.git
cd search-engine-example/docker2. Environment dosyasını oluşturun:
cp ../.env.example ../.envNot: .env.example dosyası hazır ayarlarla gelir, değişiklik yapmanıza gerek yoktur.
3. Docker container'ları başlatın:
docker compose --env-file ../.env up -d --build4. Tamamlandı
Uygulama otomatik olarak:
- Bağımlılıkları yükler (composer, npm)
- Veritabanını oluşturur ve migrate eder
- Seed verilerini yükler
API Base URL: http://localhost/api
Test Kullanıcısı:
- Email: test@example.com
- Şifre: password
Servisler:
- Web Application: http://localhost
- Telescope (Debug): http://localhost/telescope
- Horizon (Queue): http://localhost/horizon
- MySQL: localhost:3306
- Redis: localhost:6379
Proje içinde hazır Postman collection ve environment dosyaları bulunmaktadır:
- postman/SearchEngineExample.postman_collection.json
- postman/SearchEngineExample.postman_environment.json
1. Login
POST /api/login
Content-Type: application/json
{
"email": "test@example.com",
"password": "password"
}Response (200):
{
"data": {
"user": {
"id": 1,
"name": "Test User",
"email": "test@example.com"
},
"access_token": "1|xxxxxxxxxxxxx",
"refresh_token": "2|xxxxxxxxxxxxx",
"access_token_expires_at": "2025-10-03T13:00:00.000000Z",
"refresh_token_expires_at": "2025-11-02T12:00:00.000000Z"
}
}2. Refresh Token
POST /api/refresh-token
Authorization: Bearer {refresh_token}3. Get Logged In User
GET /api/user
Authorization: Bearer {access_token}4. Logout
GET /api/user/logout
Authorization: Bearer {access_token}5. Search Contents
GET /api/search?keyword=laravel&type=article&sort_by=score&per_page=10&page=1
Authorization: Bearer {access_token}Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| keyword | string | No | - | Aranacak kelime (min: 2, max: 255) |
| type | string | No | - | İçerik tipi: video, article |
| sort_by | string | No | score | Sıralama: score, title, views, likes, published_at |
| sort_direction | string | No | desc | Sıralama yönü: asc, desc |
| per_page | integer | No | 20 | Sayfa başına kayıt (min: 1, max: 100) |
| page | integer | No | 1 | Sayfa numarası |
Response (200):
{
"data": [
{
"id": 1,
"external_id": "ext-123",
"title": "Laravel 12 Yenilikleri",
"type": "article",
"views": 15000,
"likes": 1200,
"tags": ["laravel", "php", "framework"],
"published_at": "2025-10-01T10:00:00.000000Z",
"score": 125.75,
"provider": {
"id": 1,
"name": "Provider 1"
}
}
],
"meta": {
"current_page": 1,
"per_page": 10,
"total": 100
}
}6. Get Statistics
GET /api/search/statistics
Authorization: Bearer {access_token}Response (200):
{
"data": {
"total_contents": 1250,
"total_videos": 750,
"total_articles": 500,
"total_providers": 2,
"active_providers": 2
}
}Kullanım Alanı: Provider Adapter'ları oluşturmak için
Amaç: Farklı veri formatlarını (JSON, XML) destekleyen provider adapter'larını dinamik olarak oluşturmak.
┌────────────────────────────────────────┐
│ ProviderAdapterFactory │
│ (Abstract Factory) │
└────────────────────────────────────────┘
│
│ creates
▼
┌────────────────────────────────────────┐
│ AbstractProviderAdapter │
│ (Abstract Product) │
└────────────────────────────────────────┘
△
│ implements
┌────────┴────────┐
│ │
┌───────────────┐ ┌──────────────┐
│ JsonProvider │ │ XmlProvider │
│ Adapter │ │ Adapter │
└───────────────┘ └──────────────┘
Kullanım Alanı: Dış API'lerden gelen farklı formatları normalize etmek
Amaç: JSON ve XML formatındaki farklı veri yapılarını ortak bir formata dönüştürmek.
External APIs Adapters Application
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ JSON API │────────>│ JsonAdapter │─────>│ │
└──────────┘ └──────────────┘ │ Normalized │
┌──────────┐ ┌──────────────┐ │ Content │
│ XML API │────────>│ XmlAdapter │─────>│ DTO │
└──────────┘ └──────────────┘ └──────────────┘
Kullanım Alanı: İçerik puanlama (scoring) algoritmaları
Amaç: Video ve Article içerikleri için farklı puanlama algoritmaları uygulamak.
┌────────────────────────────────┐
│ ScoringStrategyFactory │
└────────────────────────────────┘
│
│ creates
▼
┌────────────────────────────────┐
│ ScoringStrategyInterface │
└────────────────────────────────┘
△
│ implements
┌───────┴────────┐
│ │
┌───────────────┐ ┌────────────────┐
│ VideoScoring │ │ ArticleScoring │
│ Strategy │ │ Strategy │
└───────────────┘ └────────────────┘
Kullanım Alanı: Arama filtreleri
Amaç: Arama sorgusuna birden fazla filtreyi zincirleme şekilde uygulamak.
Search Query
│
▼
┌──────────────────┐
│ KeywordFilter │ → keyword varsa filtrele
└──────────────────┘
│
▼
┌──────────────────┐
│ ContentTypeFilter│ → type varsa filtrele
└──────────────────┘
│
▼
┌──────────────────┐
│ SortingFilter │ → sıralama uygula
└──────────────────┘
│
▼
Final Result
Kullanım Alanı: Provider senkronizasyonu hata yönetimi
Amaç: Sürekli başarısız olan provider'ları geçici olarak devre dışı bırakmak.
┌─────────────────────────────────────────────────┐
│ Circuit Breaker States │
└─────────────────────────────────────────────────┘
┌──────────┐
│ CLOSED │ (Normal çalışma)
└──────────┘
│
│ Başarısız istekler artıyor
│ (consecutive_failures >= 3)
▼
┌──────────┐
│ OPEN │ (Devre dışı - 30 dakika)
└──────────┘
│
│ Timeout süresi doldu
▼
┌──────────┐
│ CLOSED │ (Tekrar aktif)
└──────────┘
Veritabanı Yapısı:
providers
├── consecutive_failures (int) -- Ardışık hata sayısı
└── disabled_until (timestamp) -- Devre dışı kalma süresiKullanım Alanı: Provider senkronizasyonu
Amaç: Aynı provider'ın aynı anda birden fazla process tarafından senkronize edilmesini önlemek.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Process 1 │ │ Process 2 │ │ Process 3 │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
│ Lock Al │ Lock Almaya Çalış │ Lock Almaya Çalış
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────┐
│ Redis Lock │
│ provider_sync_1: uuid-xxx-xxx │
└────────────────────────────────────────────────────────────────┘
│ │ │
│ ✅ Lock Alındı │ ❌ Bekle │ ❌ Bekle
▼ │ │
İşlem Yap │ │
│ │ │
│ Lock Serbest Bırak │ │
▼ ▼ ▼
✅ Tamamlandı ✅ Şimdi Alabilir ✅ Şimdi Alabilir
Kullanım Alanı: Content modeli değişikliklerinde cache yönetimi
Kullanım Alanı: Veri erişim katmanı
Amaç: Business logic ile veri erişim mantığını ayırmak.
Kullanım Alanı: Kullanıcı login loglama
Akış:
Login → Event Dispatch → Listener → Queue Job → Database
GitHub Actions master branch'e PR açıldığında otomatik test çalıştırır:
# .github/workflows/run-tests.yml
name: Run Pest Tests
on:
pull_request:
branches: [ master ]
jobs:
laravel-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: ./vendor/bin/pestsearch-engine-example/
│
├── app/
│ ├── Console/Commands/ # Artisan komutları
│ ├── Enums/ # Enum'lar
│ ├── Events/ # Event'ler
│ ├── Exceptions/ # Custom exception'lar
│ ├── Filters/ # Chain of Responsibility
│ ├── Http/
│ │ ├── Controllers/
│ │ ├── Requests/
│ │ └── Resources/
│ ├── Jobs/ # Queue jobs
│ ├── Listeners/ # Event listeners
│ ├── Models/
│ ├── Observers/ # Observer pattern
│ ├── Repositories/ # Repository pattern
│ └── Services/
│ ├── Auth/
│ ├── Content/ # Sync, Lock
│ ├── Providers/ # Adapter, Factory
│ └── Search/ # Strategy pattern
│
├── config/
├── database/
│ ├── factories/
│ ├── migrations/
│ └── seeders/
│
├── docker/
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── supervisord.conf
│
├── routes/
│ ├── api.php
│ └── web.php
│
├── tests/
│ ├── Feature/
│ └── Unit/
│
└── .github/workflows/ # CI/CD
└── run-tests.yml
Artisan Command
│
▼
┌──────────────────────┐
│ContentSyncService │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ Distributed Lock │
│ Acquire Lock │──── ❌ Skip if locked
└──────────────────────┘
│
│ ✅ Lock Alındı
▼
┌──────────────────────┐
│ Provider Adapter │
│ Factory.make() │
└──────────────────────┘
│
├──→ JSON Adapter
└──→ XML Adapter
│
▼
┌──────────────────────┐
│ External API │
│ (JSON/XML Data) │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ NormalizedContentDTO │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ Content Model │
│ (Database) │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ ContentObserver │
│ clearStatisticsCache │
└──────────────────────┘
User Request
│
▼
┌──────────────────────┐
│ SearchController │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ SearchService │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ SearchRepository │
└──────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Pipeline (Chain of Responsibility) │
│ │
│ Query → KeywordFilter │
│ → ContentTypeFilter │
│ → SortingFilter │
└──────────────────────────────────────┘
│
▼
┌──────────────────────┐
│ Database │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ ScoreCalculator │
│ (Strategy Pattern) │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ SearchResultDTO │
└──────────────────────┘
- Abstract Factory Pattern
- Adapter Pattern
- Strategy Pattern
- Chain of Responsibility Pattern
- Circuit Breaker Pattern
- Observer Pattern
- Repository Pattern
- Factory Pattern
- SOLID Prensipleri
- Clean Code
- KISS
- DRY
- Dependency Injection
- Layered Architecture
- Event-Driven Architecture
- DTO
- Service Layer Pattern
- Distributed Lock
- Race Condition Prevention
- Circuit Breaker
- Sanctum Authentication (JWT)
- Eloquent ORM & Relationships
- Query Scopes (Global & Local)
- Model Observers
- Events & Listeners
- Queue Jobs (Redis)
- Laravel Horizon
- Laravel Telescope
- Service Providers
- Middleware
- Custom Artisan Commands
- Pest PHP Testing Framework
- Unit Tests
- Feature Tests
# Seed data
php artisan db:seed
# Provider senkronizasyonu
php artisan providers:sync
# Container'ları başlat
docker compose --env-file ../.env up -d --build
# Container'ları durdur
docker compose down