PHP Framework

Laravel ile Enterprise PHP Geliştirme

Türkiye'nin önde gelen Laravel geliştiricilerinden biri olarak, küçük startup'lardan büyük kurumsal firmalara kadar geniş bir yelpazede Laravel tabanlı çözümler üretiyoruz. Eloquent ORM, Queue sistemi, API kaynakları ve modern PHP mimarisiyle ölçeklenebilir, güvenli ve bakımı kolay uygulamalar inşa ediyoruz.

Sürüm Laravel 11.x
PHP 8.2+
Lisans MIT
Paket Yöneticisi packagist.org

Üretim Kalitesinde Laravel Kodu

Gerçek projelerde kullandığımız mimari kalıplar, tasarım desenleri ve en iyi pratikler. Hello World değil, production-ready kod.

app/Models/Order.php
PHP
<?php

declare(strict_types=1);

namespace App\Models;

use App\Enums\OrderStatus;
use App\Observers\OrderObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;

#[ObservedBy([OrderObserver::class])]
class Order extends Model
{
    use HasFactory, SoftDeletes, LogsActivity;

    protected $fillable = [
        'user_id',
        'status',
        'subtotal',
        'discount_amount',
        'tax_amount',
        'total_amount',
        'currency',
        'shipping_address',
        'billing_address',
        'notes',
        'metadata',
        'paid_at',
        'shipped_at',
    ];

    protected function casts(): array
    {
        return [
            'status'           => OrderStatus::class,
            'subtotal'         => 'decimal:2',
            'discount_amount'  => 'decimal:2',
            'tax_amount'       => 'decimal:2',
            'total_amount'     => 'decimal:2',
            'shipping_address' => 'array',
            'billing_address'  => 'array',
            'metadata'         => 'array',
            'paid_at'          => 'datetime',
            'shipped_at'       => 'datetime',
        ];
    }

    // ─── Activity Log Yapılandırması ───────────────────────
    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['status', 'total_amount', 'paid_at', 'shipped_at'])
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs()
            ->setDescriptionForEvent(fn (string $eventName) => "Sipariş {$eventName}");
    }

    // ─── İlişkiler ─────────────────────────────────────────
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class)->withTrashed();
    }

    public function products(): BelongsToMany
    {
        return $this->belongsToMany(Product::class, 'order_items')
            ->withPivot(['quantity', 'unit_price', 'discount_rate', 'line_total'])
            ->withTimestamps()
            ->using(OrderItem::class);
    }

    public function payments(): HasMany
    {
        return $this->hasMany(Payment::class)->latestOfMany();
    }

    // ─── Local Scope'lar ───────────────────────────────────
    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', OrderStatus::Pending)
            ->whereNull('paid_at');
    }

    public function scopeForUser(Builder $query, int $userId): Builder
    {
        return $query->where('user_id', $userId);
    }

    public function scopeWithinDateRange(
        Builder $query,
        string $from,
        string $to
    ): Builder {
        return $query->whereBetween('created_at', [$from, $to]);
    }

    // ─── Accessor'lar ──────────────────────────────────────
    protected function formattedTotal(): Attribute
    {
        return Attribute::make(
            get: fn () => number_format((float) $this->total_amount, 2, ',', '.') . ' ' . $this->currency,
        );
    }

    public function isEditable(): bool
    {
        return in_array($this->status, [
            OrderStatus::Pending,
            OrderStatus::Processing,
        ]);
    }
}
app/Http/Controllers/Api/V1/OrderController.php
PHP
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\Api\StoreOrderRequest;
use App\Http\Requests\Api\UpdateOrderRequest;
use App\Http\Resources\OrderResource;
use App\Jobs\ProcessOrderJob;
use App\Services\OrderService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

class OrderController extends Controller
{
    public function __construct(
        private readonly OrderService $orderService,
    ) {
        $this->middleware(['auth:sanctum', 'verified']);
        $this->middleware('throttle:orders')->only(['store', 'update']);
    }

    /**
     * Kimliği doğrulanmış kullanıcının siparişlerini filtreli + sayfalı döndürür.
     */
    public function index(Request $request): AnonymousResourceCollection
    {
        $orders = QueryBuilder::for(
            $this->orderService->getUserOrdersQuery($request->user()->id)
        )
            ->allowedFilters([
                AllowedFilter::exact('status'),
                AllowedFilter::scope('within_date_range', 'withinDateRange'),
                AllowedFilter::partial('notes'),
            ])
            ->allowedSorts(['created_at', 'total_amount', 'status'])
            ->allowedIncludes(['products', 'payments', 'user'])
            ->defaultSort('-created_at')
            ->paginate($request->integer('per_page', 15))
            ->withQueryString();

        return OrderResource::collection($orders);
    }

    /**
     * Yeni sipariş oluşturur ve arka plan kuyruğuna gönderir.
     */
    public function store(StoreOrderRequest $request): JsonResponse
    {
        $order = $this->orderService->createOrder(
            userId: $request->user()->id,
            data: $request->validated(),
        );

        // Asenkron işlem: ödeme, stok güncelleme ve bildirimler
        ProcessOrderJob::dispatch($order)
            ->onQueue('orders')
            ->delay(now()->addSeconds(5));

        return OrderResource::make($order)
            ->response()
            ->setStatusCode(201);
    }

    /**
     * Belirtilen siparişi günceller (sadece düzenlenebilir durumda ise).
     */
    public function update(UpdateOrderRequest $request, int $orderId): OrderResource
    {
        $order = $this->orderService->findForUser(
            orderId: $orderId,
            userId:  $request->user()->id,
        );

        abort_unless($order->isEditable(), 422, 'Bu sipariş artık düzenlenemiyor.');

        $updated = $this->orderService->updateOrder($order, $request->validated());

        return OrderResource::make($updated);
    }

    /**
     * Siparişi iptal eder ve stok iadesi başlatır.
     */
    public function cancel(Request $request, int $orderId): JsonResponse
    {
        $order = $this->orderService->findForUser(
            orderId: $orderId,
            userId:  $request->user()->id,
        );

        $this->orderService->cancelOrder($order, $request->input('reason'));

        return response()->json([
            'message' => 'Sipariş başarıyla iptal edildi.',
            'data'    => OrderResource::make($order->fresh()),
        ]);
    }
}
app/Jobs/ProcessOrderJob.php
PHP
<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Models\Order;
use App\Services\InventoryService;
use App\Services\NotificationService;
use App\Services\PaymentService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable;

class ProcessOrderJob implements ShouldQueue, ShouldBeUnique
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /** Maksimum deneme sayısı */
    public int $tries = 3;

    /** Başarısız denemeler arası bekleme (saniye) */
    public array $backoff = [60, 180, 600];

    /** Job zaman aşımı süresi (saniye) */
    public int $timeout = 120;

    /** Unique lock süresi (saniye) */
    public int $uniqueFor = 3600;

    public function __construct(
        private readonly Order $order,
    ) {}

    /** Unique lock key'i — aynı sipariş için paralel işlem önlenir */
    public function uniqueId(): string
    {
        return "order:{$this->order->id}";
    }

    /**
     * İş mantığı: Ödeme → Stok → Bildirim (tek transaction'da)
     */
    public function handle(
        PaymentService      $paymentService,
        InventoryService    $inventoryService,
        NotificationService $notificationService,
    ): void {
        Log::info('Sipariş işleme başladı', [
            'order_id' => $this->order->id,
            'attempt'  => $this->attempts(),
        ]);

        DB::transaction(function () use ($paymentService, $inventoryService, $notificationService) {

            // 1. Ödeme tahsilatı
            $payment = $paymentService->charge(
                order:  $this->order,
                method: $this->order->metadata['payment_method'] ?? 'card',
            );

            // 2. Stok düşümü (atomik — race condition'a karşı korumalı)
            $inventoryService->decrementBulk(
                items: $this->order->products()
                    ->withPivot(['quantity'])
                    ->get()
                    ->map(fn ($p) => [
                        'product_id' => $p->id,
                        'quantity'   => $p->pivot->quantity,
                    ])->all(),
            );

            // 3. Sipariş durumu ve ödeme zamanı güncelleme
            $this->order->update([
                'status'  => \App\Enums\OrderStatus::Processing,
                'paid_at' => now(),
            ]);

        }); // transaction commit

        // Transaction dışı: bildirimler (idem-potent olduğu için)
        $notificationService->sendOrderConfirmation($this->order);

        Log::info('Sipariş işleme tamamlandı', ['order_id' => $this->order->id]);
    }

    /**
     * Tüm denemeler başarısız olduğunda çağrılır.
     */
    public function failed(Throwable $exception): void
    {
        Log::error('Sipariş işleme başarısız', [
            'order_id'  => $this->order->id,
            'exception' => $exception->getMessage(),
            'trace'     => $exception->getTraceAsString(),
        ]);

        // Sipariş durumunu hata olarak işaretle
        $this->order->update(['status' => \App\Enums\OrderStatus::Failed]);

        // Müşteri ve ekibe acil bildirim gönder
        \App\Notifications\OrderProcessingFailed::dispatch($this->order, $exception->getMessage());
    }
}
app/Providers/RepositoryServiceProvider.php
PHP
<?php

declare(strict_types=1);

namespace App\Providers;

use App\Contracts\PaymentGatewayInterface;
use App\Contracts\Repositories\OrderRepositoryInterface;
use App\Contracts\Repositories\ProductRepositoryInterface;
use App\Contracts\Repositories\UserRepositoryInterface;
use App\Repositories\Eloquent\EloquentOrderRepository;
use App\Repositories\Eloquent\EloquentProductRepository;
use App\Repositories\Eloquent\EloquentUserRepository;
use App\Services\Payment\FakePaymentGateway;
use App\Services\Payment\IyzicoPaymentGateway;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    /**
     * Otomatik bağlantılar — interface → implementation
     * Laravel bu eşlemeleri IoC container'a otomatik kaydeder.
     */
    public array $bindings = [
        OrderRepositoryInterface::class   => EloquentOrderRepository::class,
        ProductRepositoryInterface::class => EloquentProductRepository::class,
        UserRepositoryInterface::class    => EloquentUserRepository::class,
    ];

    /**
     * Manuel/koşullu servis kayıtları
     */
    public function register(): void
    {
        // Test ortamında sahte (fake) ödeme gateway'i kullan
        // Production/staging'de gerçek Iyzico gateway'i kullan
        $this->app->singleton(
            abstract: PaymentGatewayInterface::class,
            concrete: function (Application $app): PaymentGatewayInterface {
                if ($app->environment('testing')) {
                    return new FakePaymentGateway();
                }

                return new IyzicoPaymentGateway(
                    apiKey:    config('services.iyzico.api_key'),
                    secretKey: config('services.iyzico.secret_key'),
                    baseUri:   config('services.iyzico.base_uri'),
                    sandbox:   $app->environment('local', 'staging'),
                );
            },
        );

        // Cache decorator — repository üzerine önbellekleme katmanı
        $this->app->extend(
            abstract: ProductRepositoryInterface::class,
            closure: function (ProductRepositoryInterface $repository, Application $app) {
                return new \App\Repositories\Cache\CachedProductRepository(
                    repository: $repository,
                    cache:      $app->make('cache.store'),
                    ttl:        now()->addMinutes(30),
                );
            },
        );
    }

    public function boot(): void
    {
        // Rate limiter tanımlamaları
        \Illuminate\Support\Facades\RateLimiter::for('orders', function ($request) {
            return \Illuminate\Cache\RateLimiting\Limit::perMinute(10)
                ->by($request->user()?->id ?? $request->ip())
                ->response(fn () => response()->json([
                    'message' => 'Çok fazla istek gönderdiniz. Lütfen bekleyin.',
                ], 429));
        });
    }
}

Kullandığımız Laravel Paketleri

Her projede güvenilirliğini kanıtlamış, topluluk tarafından aktif olarak desteklenen paketler.

laravel/sanctum Auth

SPA ve mobil uygulamalar için hafif API token kimlik doğrulama sistemi. Cookie tabanlı session desteğiyle full-stack projeler için ideal çözüm.

spatie/laravel-permission ACL

Role ve permission yönetimi için endüstri standardı paket. Blade direktifleri, middleware ve Eloquent entegrasyonuyla tam donanımlı yetkilendirme.

spatie/laravel-activitylog Audit

Model üzerindeki tüm değişiklikleri otomatik olarak loglar. Kurumsal uygulamalarda denetim izi (audit trail) için vazgeçilmez bir araç.

laravel/horizon Queue

Redis tabanlı queue işlemlerini gerçek zamanlı izleyen güzel bir dashboard. İş başarısızlıklarını, gecikmeleri ve iş yükü dağılımını tek ekranda görün.

spatie/laravel-query-builder API

JSON:API standardına uygun filter, sort ve include parametrelerini Eloquent sorgularına otomatik çeviren güçlü paket. RESTful API'lerde üretkenliği ikiye katlar.

barryvdh/laravel-debugbar Dev

Geliştirme ortamında sorgu sayısını, bellek kullanımını, timeline ve exception detaylarını tarayıcı üzerinde anlık raporlayan debug aracı.

intervention/image Media

GD ve Imagick sürücü desteğiyle görsel yeniden boyutlandırma, kırpma, watermark ve format dönüştürme işlemlerini zarif bir API ile sunar.

maatwebsite/excel Export

PHPSpreadsheet tabanlı Excel/CSV okuma-yazma kütüphanesi. Chunk import, queue export ve özel formatlar ile büyük veri setlerini kolayca yönetin.

Laravel ile Neler Yapıyoruz?

Farklı sektörlerden büyük ve küçük ölçekli projelerde Laravel'in gücünü pratiğe dökme deneyimlerimiz.

🛒

E-ticaret Backend

Ürün kataloğu, sepet yönetimi, ödeme entegrasyonu (Iyzico, Stripe), kargo takibi ve müşteri paneli içeren tam özellikli e-ticaret altyapısı.

☁️

SaaS Platformu

Abonelik yönetimi, kullanım bazlı faturalama, feature flag sistemi ve kullanıcı onboarding akışları içeren modern SaaS mimarisi.

🏢

Kurumsal CRM / ERP

Müşteri yönetimi, satış pipeline, teklif/fatura oluşturma, envanter takibi ve raporlama modülleriyle kurumsal iş süreçlerini dijitalleştirme.

🔌

REST API Servisi

Mobil ve SPA uygulamalar için Sanctum veya Passport tabanlı OAuth2 korumalı, versiyonlu, kapsamlı dokümantasyona sahip RESTful API'ler.

📊

Admin Paneli & Dashboard

Filament veya özel arayüzlerle geliştirilmiş, gerçek zamanlı analitik, raporlama ve içerik yönetimi araçları sunan yönetim ekranları.

🏗️

Multi-tenant Uygulamalar

Spatie Multitenancy veya özel mimariyle her müşteriye izole veritabanı/şema sunan, farklı domain'lerde çalışan kiracı bazlı uygulamalar.

Laravel Projenizi Birlikte İnşa Edelim

Fikrinizi veya mevcut projenizi ele alalım; mimari tasarımdan deployment'a kadar her adımda yanınızdayız.