Belajar Symfony - Dependency Injection & Services
Episode 8 of 27

Belajar Symfony - Dependency Injection & Services

Mengupas service container: cara service dibuat dan disuntikkan, autowiring dengan konfigurasi services.yaml, _defaults, binding interface ke implementasi, parameter & env vars, lazy loading, serta perintah debug:container dan debug:autowiring untuk menelusuri graph.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejak episode 2 kita menyebut Dependency Injection Container sebagai fondasi Symfony. Episode 8 mengupasnya tuntas: apa itu service, bagaimana container membuatnya, dan mengapa arsitektur ini mengubah cara kalian menulis kode.

Mengapa ini topik yang paling penting untuk dikuasai? Karena di sinilah letak perbedaan terbesar antara "menulis PHP" dan "menulis Symfony". Aplikasi yang sehat dibangun dari banyak service kecil yang tersusun — dan container yang memastikan susunan itu benar, konsisten, dan mudah di-test. Begitu kalian memahami DI, membaca kode framework (dan iklan lowongan kerja Symfony) terasa jauh lebih mudah.

Apa itu Service?

Service adalah objek yang melakukan tugas spesifik, dibuat sekali (singleton dalam satu request) dan dipakai bersama. Contoh: Mailer, UserRepository, HttpClient, LoggerInterface. Berlawanan dengan "new object di dalam method", service hidup di container dan di-inject ke pemakainya:

Service sederhana
<?php
 
namespace App\Service;
 
use Psr\Log\LoggerInterface;
 
class ArticlePublisher
{
    public function __construct(
        private readonly LoggerInterface $logger,
    ) {
    }
 
    public function publish(array $data): void
    {
        // logika penerbitan artikel...
        $this->logger->info('Artikel diterbitkan', $data);
    }
}

Perhatikan: LoggerInterface masuk lewat constructor — class ini tidak pernah membuat dependency-nya sendiri. Itulah ciri khas DI: dependencies diberikan, bukan dicari.

Autowiring: Container yang Berpikir

Symfony 4+ mengaktifkan autowiring secara default. Container melihat type-hint constructor dan otomatis mencari service yang cocok. Kalian tidak perlu mendaftarkan App\Service\ArticlePublisher secara manual — cukup biarkan di namespace App\:

config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true
 
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

Penjelasan per baris:

KunciFungsi
_defaults.autowireSemua service di file ini di-inject otomatis
_defaults.autoconfigureService otomatis terdaftar sebagai tag (mis. event subscriber)
App\ resource ../src/Container memindai semua class di src/ sebagai service
excludeEntity dan Kernel bukan service — mereka objek data

Karena itu di episode 6 kita bisa menulis ArticleRepository $repo di controller tanpa mendaftarkan apa pun: container menemukannya, membangunnya, dan menyuntikkannya.

Binding Interface ke Implementasi

Autowiring hanya berhasil jika type-hint unambiguous — satu interface punya satu implementasi. Saat lebih dari satu implementasi, kalian harus memberi tahu container yang mana:

Dua implementasi satu interface
interface PaymentGatewayInterface
{
    public function charge(float $amount): void;
}
 
class StripeGateway implements PaymentGatewayInterface {}
class MidtransGateway implements PaymentGatewayInterface {}

Tanpa binding, container tidak tahu harus menyuntikkan yang mana:

config/services.yaml — binding interface
services:
    App\Service\StripeGateway: ~
    App\Service\MidtransGateway: ~
 
    App\Service\PaymentGatewayInterface:
        alias: App\Service\StripeGateway

Sekarang PaymentGatewayInterface $gateway di mana pun akan menerima StripeGateway. Ganti implementasi cukup di satu tempat — inilah decoupling: pemakai tidak peduli provider pembayaran, hanya interface-nya.

Parameter dan Env Vars

Service kadang butuh nilai konfigurasi. Dua sumber: parameter (YAML) dan env var (.env):

Parameter dan env var
parameters:
    blog.items_per_page: 10
 
services:
    App\Service\ArticlePublisher:
        arguments:
            $itemsPerPage: '%blog.items_per_page%'
            $adminEmail: '%env(ADMIN_EMAIL)%'

Parameter memakai sintaks %nama%; env var memakai %env(VAR)%. Untuk value yang sering berubah antar environment, selalu pilih env var (dibahas penuh di episode 16) — parameter cocok untuk nilai yang memang statis.

Lazy Loading

Beberapa service mahal dibuat (koneksi ke API, load konfigurasi besar) padahal jarang dipakai. Container bisa menunda pembuatannya sampai benar-benar diakses:

Lazy loading
services:
    App\Service\HeavyAnalyzer:
        lazy: true

Dengan lazy: true, container membuat proxy yang menginstansiasi objek asli hanya saat method pertama kali dipanggil. Ini memangkas waktu request untuk aplikasi dengan banyak service berat.

Menelusuri Graph: debug:container

Kekuatan Symfony adalah introspectability — semua bisa diperiksa:

Debug service container
php bin/console debug:container              # daftar semua service
php bin/console debug:container --show-arguments  # detail konstruktor
php bin/console debug:autowiring             # cari class yang bisa di-inject
php bin/console debug:autowiring --search Mail  # filter nama

Saat autowiring gagal, error Symfony cukup jelas: ia menyebutkan service yang gagal, argumen yang tidak bisa diselesaikan, dan daftar service yang cocok dengan type-hint. Ini salah satu alasan mengapa kalian tidak perlu "menebak-nebak" — container selalu bisa memberi tahu graph-nya.

Warning

Jangan pernah memanggil new pada service di dalam controller/method, dan hindari $this->container->get() secara liar. Keduanya membuat service tidak bisa di-test dan merusak jejak dependency. Jika suatu class butuh service, tulis type-hint di constructor atau argument method — container yang bekerja.

Common Pitfalls

  • Autowiring gagal — dua class mengimplementasi interface yang sama tanpa alias (atasi dengan binding di atas).
  • Service dibuat ulang — service defaultnya shared; jika butuh instance baru setiap pemakaian, atur shared: false.
  • Controller DI di method vs constructor — keduanya sah; pilih method untuk service yang hanya dipakai satu action, constructor untuk yang dipakai banyak action.

Penutup

Pada episode 8 ini, kalian telah menguasai mekanisme inti Symfony.

Inti yang harus dibawa pulang:

  • Service = objek tugas spesifik yang dikelola container; dependency datang lewat constructor.
  • Autowiring memetakan type-hint ke service; _defaults mengaktifkannya global.
  • Interface dengan banyak implementasi butuh binding/alias eksplisit.
  • Parameter %x% untuk nilai statis; %env(X)% untuk nilai lingkungan.
  • lazy: true menunda pembuatan service berat; debug:container untuk inspeksi.

Di episode 9 selanjutnya kita keluar dari web dan masuk ke CLI: Console & CLI Commands — membuat command dengan arguments & options, input/output interaktif (table, progress bar), exit code, dan praktik CLI tooling untuk import data & cron. Sampai jumpa di episode 9!

Belajar Symfony - Dependency Injection & Services | Belajar Symfony