2026'da Symfony ile API Platform: Mimari, State Providers ve Mülakat Soruları

API Platform 4.2 ile Symfony'yi öğrenin: State Providers, Processors, Object Mapper, JSON Streamer ve performans optimizasyonları. Deneyimli geliştiriciler için sık sorulan mülakat soruları.

REST API iş akışı ile API Platform Symfony mimari diyagramı

API Platform 4.2, Symfony uygulamalarının REST ve GraphQL API'lerini sunma şeklini dönüştürmektedir. Bu sürüm, temiz kaynak ayrımı için Symfony Object Mapper'ı, önemli performans artışları için JSON Streamer'ı ve yeniden tasarlanmış filtre sistemini tanıtmaktadır. Teknik mülakatlarına hazırlanan geliştiriciler için bu mimari kalıpları anlamak, kıdemli adayları junior'lardan ayırt etmektedir.

API Platform 4.2 Gereksinimleri

API Platform 4.2, Symfony 7.4 veya 8.0 gerektirmektedir. Symfony 6.4 ve 7.0-7.3 desteği kaldırılmıştır. JSON Streamer, koleksiyon endpoint'lerinde saniyede %32'ye kadar daha fazla istek sağlamaktadır.

Symfony ile API Platform 4.2 Kurulumu

API Platform, otomatik yapılandırma ile Symfony Flex üzerinden kurulmaktadır. Varsayılan kurulum çoğu kullanım durumunu karşılarken, karmaşık domain gereksinimleri için tamamen özelleştirilebilir olmaya devam etmektedir.

bash
# API Platform kurulumu
composer require api-platform/symfony

# API dokümantasyonu /api/ adresinde mevcuttur
# Sunucuyu başlattıktan sonra http://localhost:8000/api/ adresini açın
symfony serve

Flex tarifi, serializasyon gruplarını, Doctrine entegrasyonunu ve OpenAPI dokümantasyon oluşturmayı yapılandırmaktadır. API kaynakları, entity sınıflarına tek bir attribute ekleyerek CRUD işlemlerini sunmaktadır.

src/Entity/Book.phpphp
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity]
#[ApiResource(
    operations: [
        new GetCollection(),
        new Get(),
        new Post(security: "is_granted('ROLE_ADMIN')"),
        new Put(security: "is_granted('ROLE_ADMIN')"),
        new Delete(security: "is_granted('ROLE_ADMIN')")
    ],
    normalizationContext: ['groups' => ['book:read']],
    denormalizationContext: ['groups' => ['book:write']]
)]
class Book
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    #[Assert\NotBlank]
    #[Groups(['book:read', 'book:write'])]
    private string $title;

    #[ORM\Column(type: 'text')]
    #[Groups(['book:read', 'book:write'])]
    private string $description;

    #[ORM\Column]
    #[Groups(['book:read'])]
    private \DateTimeImmutable $createdAt;

    // Getter ve setter'lar...
}

Bu yapılandırma, otomatik doğrulama, serializasyon ve OpenAPI dokümantasyonu ile beş endpoint oluşturmaktadır.

State Providers: Herhangi Bir Kaynaktan Veri Çekme

State Providers, API Platform'un GET işlemleri için verileri nasıl aldığını kontrol etmektedir. Varsayılan Doctrine provider'ı entity getirme işlemlerini yönetirken, özel provider'lar harici API'ler, Elasticsearch veya önbelleğe alınmış verilerle entegrasyonu mümkün kılmaktadır.

src/State/BookStateProvider.phpphp
namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\BookRepository;
use Psr\Cache\CacheItemPoolInterface;

final class BookStateProvider implements ProviderInterface
{
    public function __construct(
        private BookRepository $repository,
        private CacheItemPoolInterface $cache
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        // Tekil öğe getirme
        if (isset($uriVariables['id'])) {
            $cacheKey = sprintf('book_%d', $uriVariables['id']);
            $item = $this->cache->getItem($cacheKey);
            
            if ($item->isHit()) {
                return $item->get();
            }
            
            $book = $this->repository->find($uriVariables['id']);
            $item->set($book)->expiresAfter(3600);
            $this->cache->save($item);
            
            return $book;
        }

        // Özel filtreleme ile koleksiyon getirme
        return $this->repository->findActiveBooks();
    }
}

Provider'ı belirli işlemler için kaydetme:

php
#[ApiResource(
    operations: [
        new GetCollection(provider: BookStateProvider::class),
        new Get(provider: BookStateProvider::class),
        // Diğer işlemler varsayılan Doctrine provider'ını kullanır
        new Post(),
        new Put(),
    ]
)]
class Book { /* ... */ }

State Processors: İş Mantığı ile Mutasyonları Yönetme

State Processors, POST, PUT, PATCH ve DELETE işlemlerini yönetmektedir. Deserialize edilmiş verileri alır ve kalıcılık öncesinde iş mantığını uygularlar.

src/State/BookStateProcessor.phpphp
namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Book;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

final class BookStateProcessor implements ProcessorInterface
{
    public function __construct(
        private EntityManagerInterface $em,
        private MailerInterface $mailer,
        private ProcessorInterface $persistProcessor
    ) {}

    public function process(
        mixed $data,
        Operation $operation,
        array $uriVariables = [],
        array $context = []
    ): mixed {
        // Kalıcılık öncesi iş mantığı
        if ($data instanceof Book && $operation instanceof Post) {
            $data->setCreatedAt(new \DateTimeImmutable());
            $data->setSlug($this->generateSlug($data->getTitle()));
        }

        // Doctrine processor'a delege etme
        $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);

        // Kalıcılık sonrası bildirim
        if ($operation instanceof Post) {
            $this->notifyNewBook($data);
        }

        return $result;
    }

    private function generateSlug(string $title): string
    {
        return strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $title));
    }

    private function notifyNewBook(Book $book): void
    {
        $email = (new Email())
            ->to('catalog@example.com')
            ->subject('Yeni kitap eklendi: ' . $book->getTitle())
            ->text('Kataloga yeni bir kitap eklendi.');
        $this->mailer->send($email);
    }
}
Processor Dekorasyonu

Varsayılan Doctrine processor'ını #[AsDecorator] ile dekore etmek, özel mantık eklerken kalıcılık davranışını korumaktadır. Bu desen, ORM işlemlerinin tekrarlanmasını önlemektedir.

Object Mapper: API Kaynaklarını Entity'lerden Ayırma

API Platform 4.2, API temsillerini domain entity'lerinden ayırmak için Symfony Object Mapper bileşenini entegre etmektedir. Bu ayrım, farklı okuma/yazma modellerini mümkün kılmakta ve dahili entity yapılarını korumaktadır.

src/ApiResource/BookResource.phpphp
namespace App\ApiResource;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use App\Entity\Book;
use Symfony\Component\ObjectMapper\Attribute\Map;

#[ApiResource(
    shortName: 'Book',
    operations: [
        new GetCollection(),
        new Get()
    ]
)]
#[Map(target: Book::class)]
class BookResource
{
    public ?int $id = null;
    
    public string $title;
    
    public string $description;
    
    // Entity'de olmayan hesaplanmış alan
    public int $wordCount;
    
    // API tüketicileri için formatlanmış tarih
    public string $publishedDate;
}

Mapper provider'ı entity'leri otomatik olarak kaynaklara dönüştürmektedir:

src/State/BookResourceProvider.phpphp
namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookResource;
use App\Repository\BookRepository;
use Symfony\Component\ObjectMapper\ObjectMapperInterface;

final class BookResourceProvider implements ProviderInterface
{
    public function __construct(
        private BookRepository $repository,
        private ObjectMapperInterface $mapper
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        if (isset($uriVariables['id'])) {
            $book = $this->repository->find($uriVariables['id']);
            return $book ? $this->toResource($book) : null;
        }

        return array_map(
            fn(Book $book) => $this->toResource($book),
            $this->repository->findAll()
        );
    }

    private function toResource(Book $book): BookResource
    {
        $resource = $this->mapper->map($book, BookResource::class);
        $resource->wordCount = str_word_count($book->getDescription());
        $resource->publishedDate = $book->getCreatedAt()->format('j F Y');
        return $resource;
    }
}

Symfony mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

JSON Streamer: %32 Performans Artışı

JSON Streamer bileşeni, büyük koleksiyonları tüm veri setlerini belleğe yüklemeden serileştirmektedir. Sylius API üzerindeki benchmark'lar saniyede %32,4 istek artışı göstermiştir.

Kaynak veya işlem düzeyinde streaming'i etkinleştirme:

php
#[ApiResource(
    operations: [
        new GetCollection(
            jsonStream: true,  // JSON streaming'i etkinleştir
            paginationItemsPerPage: 100
        ),
        new Get()
    ]
)]
class Book { /* ... */ }

Streaming özellikle şunlar için faydalıdır:

  • 50'den fazla öğe içeren koleksiyon endpoint'leri
  • İç içe geçmiş ilişkileri olan kaynaklar
  • Sınırlı bant genişliğine sahip mobil istemcilere hizmet veren API'ler

OpenAPI spesifikasyonu da optimize edilmiştir. JSON Schema karşılıklılaştırması dosya boyutunu %30 azaltarak dokümantasyon yükleme sürelerini iyileştirmektedir.

Mülakat Soruları: API Platform Mimarisi

Symfony pozisyonları için teknik mülakatlar sıklıkla API Platform kalıplarını kapsamaktadır. Bu sorular, framework mimarisinin temel CRUD işlemlerinin ötesinde anlaşılmasını değerlendirmektedir.

"State Providers ve Processors arasındaki farkı açıklayın"

Beklenen yanıt: State Providers, okuma işlemleri (GET) için veri çekmektedir. Entity'ler, DTO'lar veya diziler döndürürler. State Processors, yazma işlemlerini (POST, PUT, PATCH, DELETE) yönetmektedir. Deserialize edilmiş girdiyi alır ve kalıcılık öncesinde iş mantığını çalıştırırlar. Bu ayrım CQRS prensiplerini takip etmektedir: Provider'lar aracılığıyla sorgular, Processor'lar aracılığıyla komutlar.

"Bir entity'yi doğrudan sunmak yerine özel API Resource ne zaman kullanılır?"

Beklenen yanıt: Özel kaynaklar şu durumlarda uygulanmaktadır:

  • API temsili veritabanı şemasından farklı olduğunda
  • Hesaplanmış alanlar birden fazla entity'den toplama gerektirdiğinde
  • Yazma ve okuma modelleri farklı yapılar gerektirdiğinde
  • Dahili entity alanlarının API tüketicilerinden gizli kalması gerektiğinde
  • Sürüm uyumluluğu entity'ler gelişirken kararlı kontratlar gerektirdiğinde

"API Platform doğrulamayı nasıl yönetir?"

Beklenen yanıt: API Platform, entity özelliklerinde Symfony Validator constraint'lerini kullanmaktadır. Doğrulama, State Processor çalışmadan önce deserializasyon sırasında otomatik olarak çalışmaktadır. Doğrulama grupları, işlem başına hangi constraint'lerin uygulanacağını kontrol etmektedir. Özel doğrulayıcılar standart Symfony mekanizmaları aracılığıyla entegre olmaktadır.

php
#[ApiResource(
    operations: [
        new Post(validationContext: ['groups' => ['create']]),
        new Put(validationContext: ['groups' => ['update']])
    ]
)]
class Book
{
    #[Assert\NotBlank(groups: ['create', 'update'])]
    private string $title;

    #[Assert\Isbn(groups: ['create'])]
    private string $isbn;  // Yalnızca oluşturmada gerekli
}
Yaygın Mülakat Hatası

Adaylar genellikle doğrulamayı, doğrulama gruplarından veya özel constraint'lerden bahsetmeden "otomatik" olarak tanımlamaktadır. Mülakatçılar, işlem başına doğrulamanın nasıl özelleştirileceğinin anlaşılmasını aramaktadır.

"API Platform hangi güvenlik mekanizmalarını sağlar?"

Beklenen yanıt: API Platform, Symfony Security ile şu şekillerde entegre olmaktadır:

  • Rol tabanlı erişim için işlemlerde security attribute'u
  • Veri bağlamadan sonra nesne düzeyinde kontroller için securityPostDenormalize
  • Karmaşık yetkilendirme mantığı için Voter'lar
  • Symfony Rate Limiter entegrasyonu aracılığıyla hız sınırlama
php
#[ApiResource(
    operations: [
        new Get(
            security: "is_granted('ROLE_USER')"
        ),
        new Put(
            security: "is_granted('ROLE_ADMIN') or object.getOwner() == user",
            securityPostDenormalize: "is_granted('BOOK_EDIT', object)"
        )
    ]
)]

Filtreler ve Sayfalama: Gelişmiş Sorgu Desenleri

API Platform filtreleri, istemcilerin URL parametreleri ile koleksiyonları sorgulamasını sağlamaktadır. 4.2'deki filtre sistemi, daha iyi genişletilebilirlik için yeniden tasarlanmıştır.

php
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;

#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
    'title' => 'partial',      // LIKE %value%
    'author.name' => 'exact'   // İç içe özellik
])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
#[ApiFilter(OrderFilter::class, properties: ['title', 'createdAt'])]
class Book { /* ... */ }

Oluşturulan endpoint'ler:

text
GET /api/books?title=symfony           # Başlığa göre arama
GET /api/books?createdAt[after]=2026-01-01  # Tarih aralığı
GET /api/books?order[createdAt]=desc   # Sıralama

API Platform Kaynaklarını Test Etme

API Platform, fonksiyonel testleri basitleştiren bir test istemcisi sağlamaktadır. ApiTestCase sınıfı, API yanıtlarına özgü assertion'lar sunmaktadır.

tests/Api/BookTest.phpphp
namespace App\Tests\Api;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;

class BookTest extends ApiTestCase
{
    public function testGetCollection(): void
    {
        $response = static::createClient()->request('GET', '/api/books');

        $this->assertResponseIsSuccessful();
        $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
        $this->assertJsonContains([
            '@context' => '/api/contexts/Book',
            '@type' => 'Collection'
        ]);
    }

    public function testCreateBook(): void
    {
        $response = static::createClient()->request('POST', '/api/books', [
            'json' => [
                'title' => 'Symfony En İyi Uygulamaları',
                'description' => 'Modern Symfony geliştirme rehberi'
            ],
            'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
        ]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertJsonContains(['title' => 'Symfony En İyi Uygulamaları']);
    }

    public function testCreateBookValidationFails(): void
    {
        $response = static::createClient()->request('POST', '/api/books', [
            'json' => ['description' => 'Başlık eksik'],
            'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
        ]);

        $this->assertResponseStatusCodeSame(422);
        $this->assertJsonContains([
            'violations' => [
                ['propertyPath' => 'title', 'message' => 'This value should not be blank.']
            ]
        ]);
    }
}

Symfony ile API Platform İçin Temel Çıkarımlar

  • State Providers GET işlemlerini, State Processors mutasyonları yönetmektedir. Bu ayrım, özel veri kaynakları ve iş mantığı ile temiz mimariyi mümkün kılmaktadır
  • Object Mapper bileşeni API kaynaklarını Doctrine entity'lerinden ayırmakta, farklı okuma/yazma modellerini mümkün kılmakta ve dahili yapıları korumaktadır
  • JSON Streamer, tam bellek tahsisi olmadan serileştirme yoluyla koleksiyon endpoint'lerinde %32 performans artışı sağlamaktadır
  • Güvenlik, standart Symfony mekanizmaları aracılığıyla entegre olmaktadır: security ifadeleri, Voter'lar ve Rate Limiter bileşeni
  • Doğrulama grupları, işlem başına constraint uygulamasını özelleştirmektedir
  • Filtreler sorgu parametrelerini otomatik olarak sunmakta, arama, tarih ve sıralama filtreleri çoğu kullanım durumunu kapsamaktadır
  • Mülakat soruları mimari kararlara odaklanmaktadır: özel provider'ların ne zaman kullanılacağı, okuma/yazma modellerinin nasıl ayrılacağı ve güvenlik uygulama desenleri

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

Symfony kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

8 Eylül 2026 tarihinde güncellendi

Etiketler

#api-platform
#symfony
#rest-api
#state-providers
#interview

Paylaş

İlgili makaleler