From f4ad9d896ab7577a0740fce1824e15f3e6260e6f Mon Sep 17 00:00:00 2001 From: Fachruzi Ramadhan Date: Tue, 2 Sep 2025 13:29:16 +0700 Subject: [PATCH 1/2] Translate aliases.md, autoloading.md, data.md, glosarry.md, and overview.md --- guide/id/caching/data.md | 184 ++++++++++++++++++++++++++++++++ guide/id/caching/overview.md | 18 ++++ guide/id/concept/aliases.md | 141 ++++++++++++++++++++++++ guide/id/concept/autoloading.md | 31 ++++++ guide/id/glossary.md | 76 +++++++++++++ 5 files changed, 450 insertions(+) create mode 100644 guide/id/caching/data.md create mode 100644 guide/id/caching/overview.md create mode 100644 guide/id/concept/aliases.md create mode 100644 guide/id/concept/autoloading.md create mode 100644 guide/id/glossary.md diff --git a/guide/id/caching/data.md b/guide/id/caching/data.md new file mode 100644 index 00000000..f6c541f4 --- /dev/null +++ b/guide/id/caching/data.md @@ -0,0 +1,184 @@ +# Caching data + +Caching data adalah tentang menyimpan beberapa variabel PHP ke dalam cache dan mengambilnya kembali dari cache. +Ini juga menjadi fondasi untuk fitur caching yang lebih lanjut, seperti [caching halaman](page.md). + +Untuk menggunakan cache, pasang paket [yiisoft/cache](https://github.com/yiisoft/cache): + +```shell +composer require yiisoft/cache +``` + +Kode berikut adalah pola penggunaan umum caching data, di mana `$cache` merujuk ke +instance `Cache` dari paket tersebut: + +```php +public function getTopProducts(\Yiisoft\Cache\CacheInterface $cache): array +{ + $key = ['top-products', $count = 10]; + + // Try retrieving $data from cache. + $data = $cache->getOrSet($key, function (\Psr\SimpleCache\CacheInterface $cache) use ($count) { + // Can't find $data in cache, calculate it from scratch. + return getTopProductsFromDatabase($count); + }, 3600); + + return $data; +} +``` + +Ketika cache memiliki data yang terkait dengan `$key`, ia akan mengembalikan nilai yang di-cache. +Jika tidak, ia akan mengeksekusi fungsi anonim yang diberikan untuk menghitung nilai yang akan di-cache dan dikembalikan. + +Jika fungsi anonim membutuhkan beberapa data dari scope luar, Anda dapat meneruskannya menggunakan pernyataan `use`. + +## Cache handlers + +Layanan cache menggunakan handler cache yang kompatibel dengan [PSR-16](https://www.php-fig.org/psr/psr-16/) yang merepresentasikan berbagai +penyimpanan cache, seperti memori, berkas, dan basis data. + +Yii menyediakan handler berikut: + +- `NullCache` — placeholder cache yang tidak melakukan caching nyata. Tujuan handler ini adalah menyederhanakan + kode yang perlu memeriksa ketersediaan cache. Misalnya, selama pengembangan atau jika server tidak memiliki + dukungan cache, Anda dapat mengonfigurasi layanan cache untuk menggunakan handler ini. + Saat Anda mengaktifkan dukungan cache nyata, Anda dapat beralih menggunakan handler cache yang sesuai. + Pada kedua kasus, Anda dapat menggunakan kode yang sama tanpa pemeriksaan tambahan. +- `ArrayCache` — menyediakan caching hanya untuk permintaan saat ini dengan menyimpan nilai dalam sebuah array. +- [APCu](https://github.com/yiisoft/cache-apcu) — menggunakan ekstensi PHP [APC](https://secure.php.net/manual/en/book.apc.php). + Ini bisa dianggap sebagai opsi tercepat saat berurusan dengan cache untuk aplikasi terpusat (mis., satu + server, tanpa load balancer khusus, dll.). +- [Database](https://github.com/yiisoft/cache-db) — menggunakan tabel basis data untuk menyimpan data yang di-cache. +- [File](https://github.com/yiisoft/cache-file) — menggunakan berkas standar untuk menyimpan data yang di-cache. Ini sangat cocok + untuk menyimpan potongan data besar, seperti konten halaman. +- [Memcached](https://github.com/yiisoft/cache-memcached) — menggunakan ekstensi PHP [memcached](https://secure.php.net/manual/en/book.memcached.php). + Ini bisa dianggap sebagai opsi tercepat saat berurusan dengan cache di aplikasi terdistribusi + (mis., beberapa server, load balancer, dll.) +- [Wincache](https://github.com/yiisoft/cache-wincache) — menggunakan ekstensi PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension) + ([lihat juga](https://secure.php.net/manual/en/book.wincache.php)). + +[Anda dapat menemukan lebih banyak handler di packagist.org](https://packagist.org/providers/psr/simple-cache-implementation). + +> [!TIP] +> Anda dapat menggunakan penyimpanan cache yang berbeda dalam aplikasi yang sama. Strategi umum adalah: +> - Gunakan penyimpanan cache berbasis memori untuk menyimpan data kecil tetapi sering digunakan (mis., statistik) +> - Gunakan penyimpanan cache berbasis berkas atau basis data untuk menyimpan data besar dan jarang digunakan (mis., konten halaman) + +Handler cache biasanya disetel dalam [dependency injection container](../concept/di-container.md) sehingga dapat +dikonfigurasi dan diakses secara global. + +Karena semua handler cache mendukung kumpulan API yang sama, Anda dapat menukar handler cache yang digunakan +dengan yang lain. Anda dapat melakukannya dengan mengonfigurasi ulang aplikasi tanpa mengubah kode yang menggunakan cache. + +### Cache keys + +Sebuah kunci mengidentifikasi secara unik setiap item data yang disimpan di cache. Saat Anda menyimpan sebuah item data, +Anda harus menentukan kunci untuknya. Nantinya, ketika Anda mengambil item data tersebut, Anda perlu memberikan +kunci yang sesuai. + +Anda dapat menggunakan string atau nilai arbitrer sebagai kunci cache. Ketika kunci bukan string, ia akan +diserialisasi menjadi string secara otomatis. + +Strategi umum dalam mendefinisikan kunci cache adalah menyertakan semua faktor penentu dalam bentuk array. + +Ketika aplikasi yang berbeda menggunakan penyimpanan cache yang sama, Anda harus menentukan prefiks kunci cache yang unik +untuk setiap aplikasi guna menghindari konflik kunci cache. +Anda dapat melakukannya dengan menggunakan dekorator `\Yiisoft\Cache\PrefixedCache`: + +```php +$arrayCacheWithPrefix = new \Yiisoft\Cache\PrefixedCache(new \Yiisoft\Cache\ArrayCache(), 'myapp_'); +$cache = new \Yiisoft\Cache\Cache($arrayCacheWithPrefix); +``` + +### Cache expiration + +Item data yang disimpan di cache akan tetap di sana selamanya kecuali dihapus karena beberapa kebijakan +caching. Misalnya, ruang cache penuh dan penyimpanan cache menghapus data tertua. +Untuk mengubah perilaku ini, Anda dapat menyetel parameter TTL saat memanggil metode untuk menyimpan item data: + +```php +$ttl = 3600; +$data = $cache->getOrSet($key, function (\Psr\SimpleCache\CacheInterface $cache) use ($count) { +return getTopProductsFromDatabase($count); +}, $ttl); +``` + +Parameter `$ttl` menunjukkan berapa detik item data dapat tetap valid di cache. Ketika Anda mengambil +item data tersebut, jika waktu kedaluwarsanya telah lewat, metode akan mengeksekusi fungsi dan menyetel nilai yang dihasilkan +ke dalam cache. + +Anda dapat menyetel TTL bawaan untuk cache: + +```php +$cache = new \Yiisoft\Cache\Cache($arrayCache, 60 * 60); // 1 hour +``` + +Selain itu, Anda dapat menginvalidasi kunci cache secara eksplisit: + +```php +$cache->remove($key); +``` + +### Invalidation dependencies + +Selain pengaturan kedaluwarsa, perubahan pada yang disebut sebagai **ketergantungan invalidasi** juga dapat menginvalidasi item data yang di-cache. +Misalnya, `\Yiisoft\Cache\Dependency\FileDependency` merepresentasikan ketergantungan pada waktu modifikasi sebuah berkas. +Ketika ketergantungan ini berubah, itu berarti ada sesuatu yang memodifikasi berkas terkait. +Akibatnya, konten berkas yang usang yang ditemukan di cache harus diinvalidasi. + +Ketergantungan cache adalah objek dari kelas turunan `\Yiisoft\Cache\Dependency\Dependency`. Ketika Anda +menyimpan sebuah item data ke cache, Anda dapat menyertakan objek ketergantungan cache terkait. Contohnya, + +```php +/** + * @var callable $callable + * @var \Yiisoft\Cache\CacheInterface $cache + */ + +use Yiisoft\Cache\Dependency\TagDependency; + +// Set many cache values marking both with a tag. +$cache->getOrSet('item_42_price', $callable, null, new TagDependency('item_42')); +$cache->getOrSet('item_42_total', $callable, 3600, new TagDependency('item_42')); + +// Trigger invalidation by tag. +TagDependency::invalidate($cache, 'item_42'); +``` + +Berikut ringkasan ketergantungan cache yang tersedia: + +- `\Yiisoft\Cache\Dependency\ValueDependency`: menginvalidasi cache ketika nilai yang ditentukan berubah. +- `\Yiisoft\Cache\Dependency\CallbackDependency`: menginvalidasi cache ketika hasil dari callback PHP yang ditentukan + berbeda. +- `\Yiisoft\Cache\Dependency\FileDependency`: menginvalidasi cache ketika waktu modifikasi terakhir berkas berbeda. +- `\Yiisoft\Cache\Dependency\TagDependency`: mengaitkan item data yang di-cache dengan satu atau banyak tag. Anda dapat menginvalidasi + item data yang di-cache dengan tag tertentu dengan memanggil `TagDependency::invalidate()`. + +Anda dapat mengombinasikan banyak ketergantungan menggunakan `\Yiisoft\Cache\Dependency\AnyDependency` atau `\Yiisoft\Cache\Dependency\AllDependencies`. + +Untuk mengimplementasikan ketergantungan Anda sendiri, turunkan dari `\Yiisoft\Cache\Dependency\Dependency`. + +### Cache stampede prevention + +[Cache stampede](https://en.wikipedia.org/wiki/Cache_stampede) adalah jenis kegagalan berantai yang dapat terjadi ketika sistem komputasi paralel masif +dengan mekanisme cache berada di bawah beban tinggi. +Perilaku ini terkadang juga disebut dog-piling. + +`\Yiisoft\Cache\Cache` menggunakan algoritma bawaan "Probably early expiration" yang mencegah cache stampede. +Algoritma ini secara acak memalsukan cache miss untuk satu pengguna sementara yang lain masih dilayani nilai dari cache. +Anda dapat mengontrol perilakunya dengan parameter opsional kelima dari `getOrSet()`, yaitu nilai float bernama `$beta`. +Secara bawaan, beta adalah `1.0`, yang biasanya sudah cukup. +Semakin tinggi nilainya, semakin awal cache akan dibuat ulang. + +```php +/** + * @var mixed $key + * @var callable $callable + * @var \DateInterval $ttl + * @var \Yiisoft\Cache\CacheInterface $cache + * @var \Yiisoft\Cache\Dependency\Dependency $dependency + */ + +$beta = 2.0; +$cache->getOrSet($key, $callable, $ttl, $dependency, $beta); +``` diff --git a/guide/id/caching/overview.md b/guide/id/caching/overview.md new file mode 100644 index 00000000..72f5d0bb --- /dev/null +++ b/guide/id/caching/overview.md @@ -0,0 +1,18 @@ +# Caching + +Caching adalah cara yang murah dan efektif untuk meningkatkan performa aplikasi. +Dengan menyimpan data yang relatif statis di cache dan menyajikannya dari cache saat diminta, +aplikasi menghemat waktu yang sebaliknya diperlukan untuk menghasilkan data dari awal setiap kali. + +Caching dapat terjadi pada berbagai level dan lokasi dalam sebuah aplikasi. Di sisi server, pada level rendah, +cache dapat digunakan untuk menyimpan data dasar, seperti daftar artikel terbaru yang diambil dari basis data; +dan pada level yang lebih tinggi, cache dapat digunakan untuk menyimpan fragmen atau seluruh halaman web, seperti hasil rendering +artikel terbaru. Di sisi klien, Anda dapat menggunakan HTTP caching untuk menyimpan konten halaman yang terakhir dikunjungi +di cache browser. + +Yii mendukung semua mekanisme caching berikut: + +* [Data caching](data.md) +* [Fragment caching](fragment.md) +* [Page caching](page.md) +* [HTTP caching](http.md) diff --git a/guide/id/concept/aliases.md b/guide/id/concept/aliases.md new file mode 100644 index 00000000..849f9adf --- /dev/null +++ b/guide/id/concept/aliases.md @@ -0,0 +1,141 @@ +# Aliases + +Anda dapat menggunakan alias untuk merepresentasikan path file atau URL sehingga Anda tidak perlu menulis path absolut atau URL secara hard-code di +proyek Anda. Sebuah alias harus diawali karakter `@` agar dapat dibedakan dari path file dan URL biasa. Alias yang +didefinisikan tanpa `@` di awal akan diprefiks otomatis dengan karakter `@`. + +Aplikasi Yii bawaan memiliki beberapa alias yang sudah didefinisikan di `config/params.php`. Misalnya, alias `@public` merepresentasikan +path web root; `@baseUrl` merepresentasikan URL dasar untuk aplikasi Web yang sedang berjalan. + +## Defining aliases + +Anda dapat mendefinisikan sebuah alias melalui `config/params.php` aplikasi: + +```php +return [ + // ... + + 'yiisoft/aliases' => [ + 'aliases' => [ + // ... + + // an alias of a file path + '@foo' => '/path/to/foo', + + // an alias of a URL + '@bar' => 'https://www.example.com', + + // an alias of a concrete file that contains a \foo\Bar class + '@foo/Bar.php' => '/definitely/not/foo/Bar.php', + ], + ], +]; +``` + +> [!NOTE] +> Path file atau URL yang dialias tidak harus selalu merujuk pada file atau sumber daya yang benar-benar ada. + +Dengan alias yang telah didefinisikan, Anda dapat menurunkan alias baru dengan menambahkan garis miring `/` diikuti satu atau beberapa segmen path. +Sebagai contoh, `@foo` adalah alias akar (root), sedangkan `@foo/bar/file.php` adalah alias turunan. + +Anda dapat mendefinisikan sebuah alias menggunakan alias lain (baik alias akar maupun turunan): + + +```php +'@foobar' => '@foo/bar', +``` + +Parameter `yiisoft/aliases` menginisialisasi layanan `Aliases` dari [paket `yiisoft/aliases`](https://github.com/yiisoft/aliases). +Anda dapat menyetel alias tambahan saat runtime menggunakan layanan tersebut: + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $aliases->set('@uploads', '@root/uploads'); +} +``` + +## Menggunakan aliases di konfigurasi + +Disarankan untuk me-resolve alias pada level konfigurasi, sehingga service menerima URL dan path sebagai string siap pakai: + +```php + static fn (Aliases $aliases) => new FileCache( + $aliases->get($params['yiisoft/cache-file']['fileCache']['path']) + ), +]; +``` + +## Me-resolve alias + +Anda dapat menggunakan layanan `Aliases` + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $foo = $aliases->get('@foo'); // /path/to/foo + $bar = $aliases->get('@bar'); // https://www.example.com + $file = $aliases->get('@foo/bar/file.php'); // /path/to/foo/bar/file.php +} +``` + +Path/URL yang direpresentasikan oleh alias turunan ditentukan dengan mengganti bagian alias akar dengan path/URL yang sesuai +pada alias turunan tersebut. + +> [!NOTE] +> Metode `get()` tidak memeriksa apakah path/URL hasilnya merujuk pada file atau sumber daya yang ada. + + +Alias akar juga dapat berisi karakter garis miring `/`. Metode `get()` cukup cerdas untuk menentukan bagian mana +dari sebuah alias yang merupakan alias akar, sehingga dapat menentukan path file atau URL yang sesuai dengan benar: + +```php +use \Yiisoft\Aliases\Aliases; + +public function actionIndex(Aliases $aliases) +{ + $aliases->set('@foo', '/path/to/foo'); + $aliases->set('@foo/bar', '/path2/bar'); + + $aliases->get('@foo/test/file.php'); // /path/to/foo/test/file.php + $aliases->get('@foo/bar/file.php'); // /path2/bar/file.php +} +``` + +If `@foo/bar` isn't defined as a root alias, the last statement would display `/path/to/foo/bar/file.php`. + + +Jika `@foo/bar` tidak didefinisikan sebagai alias akar, pernyataan terakhir akan menghasilkan `/path/to/foo/bar/file.php`. + + +## Alias bawaan + +[Aplikasi Yii](https://github.com/yiisoft/app) mendefinisikan serangkaian alias untuk mereferensikan path file dan URL yang umum digunakan: + +- `@root` - direktori dasar dari aplikasi yang sedang berjalan. +- `@assets` - direktori publik aplikasi tempat aset dipublikasikan. +- `@assetsUrl` - URL direktori dasar tempat aset dipublikasikan. +- `@baseUrl` - URL dasar aplikasi Web yang sedang berjalan. Bawaannya `/`. +- `@npm` - direktori paket Node. +- `@bower` - direktori paket bower. +- `@vendor` - direktori `vendor` milik Composer. +- `@public` - direktori publik aplikasi yang berisi `index.php`. +- `@runtime` - path runtime aplikasi yang sedang berjalan. Bawaannya `@root/runtime`. +- `@layout` - direktori berkas layout. +- `@resources` - direktori yang berisi view, sumber aset, dan sumber daya lainnya. +- `@views` - direktori dasar template view aplikasi. + diff --git a/guide/id/concept/autoloading.md b/guide/id/concept/autoloading.md new file mode 100644 index 00000000..775b5a8f --- /dev/null +++ b/guide/id/concept/autoloading.md @@ -0,0 +1,31 @@ +# Class autoloading + +Karena Yii menggunakan [Composer](https://getcomposer.org) untuk mengelola paket, Yii secara otomatis memuat kelas dari paket-paket tersebut +tanpa perlu melakukan `require` file-nya secara eksplisit. +Saat Composer memasang paket, ia menghasilkan sebuah [autoloader yang kompatibel PSR-4](https://www.php-fig.org/psr/psr-4/). +Untuk menggunakannya, lakukan `require_once` terhadap autoloader `/vendor/autoload.php` di file entry point `index.php` Anda. + +Anda dapat menggunakan autoloader tidak hanya untuk paket yang diinstal, tetapi juga untuk aplikasi Anda sendiri karena aplikasi Anda juga merupakan sebuah paket. +Untuk memuat kelas-kelas dari namespace tertentu, tambahkan berikut ini ke `composer.json`: + +```json +{ + "autoload": { + "psr-4": { + "App\\": "src/" + } + } +} +``` + +Di mana `App\\` adalah namespace akar dan `src/` adalah direktori tempat kelas-kelas Anda berada. Anda dapat menambahkan lebih banyak sumber (source roots) jika +diperlukan. Setelah selesai, jalankan `composer dump-autoload` atau cukup `composer du` dan kelas-kelas dari namespace terkait +akan mulai dimuat secara otomatis. + +Jika Anda membutuhkan autoloading khusus lingkungan pengembangan yang tidak digunakan saat menjalankan Composer dengan flag `--no-dev`, +tambahkan ke bagian `autoload-dev` alih-alih `autoload`. + +## Referensi + +- [PSR-4: Autoloader](https://www.php-fig.org/psr/psr-4/). +- [Panduan Composer tentang autoloading](https://getcomposer.org/doc/01-basic-usage.md#autoloading). diff --git a/guide/id/glossary.md b/guide/id/glossary.md new file mode 100644 index 00000000..be99ef00 --- /dev/null +++ b/guide/id/glossary.md @@ -0,0 +1,76 @@ +# A + +## alias + +Alias adalah sebuah string yang digunakan oleh Yii untuk merujuk ke kelas atau direktori, misalnya `@app/vendor`. +Baca lebih lanjut di ["Aliases"](concept/aliases.md). + +## asset + +Aset merujuk pada berkas sumber daya. Biasanya berisi kode JavaScript atau CSS, tetapi bisa berupa konten statis apa pun yang diakses melalui HTTP. + +# C + +## configuration + +Konfigurasi dapat merujuk pada proses mengatur properti suatu objek atau pada berkas konfigurasi yang menyimpan +pengaturan untuk sebuah objek atau kelas objek. Baca lebih lanjut di ["Configuration"](concept/configuration.md). + +# D + +## DI + +Dependency Injection (Injeksi Dependensi) adalah teknik pemrograman di mana dependensi sebuah objek disediakan (disuntikkan) dari luar. ["DI"](concept/di-container.md) + +# I + +## installation + +Instalasi adalah proses menyiapkan sesuatu agar dapat bekerja, baik dengan mengikuti berkas readme maupun menjalankan skrip +yang disiapkan khusus. Dalam konteks Yii, ini mencakup pengaturan izin dan pemenuhan persyaratan perangkat lunak. + +# M + +## middleware + +Middleware adalah pemroses dalam tumpukan pemrosesan permintaan (request). Diberikan sebuah request, ia dapat menghasilkan response +atau melakukan suatu aksi dan meneruskan pemrosesan ke middleware berikutnya. Baca lebih lanjut di ["Middleware"](structure/middleware.md). + +## module + +Modul adalah sub-aplikasi yang mengelompokkan sejumlah kode berdasarkan suatu kasus penggunaan. Biasanya digunakan di dalam aplikasi utama +dan dapat berisi handler URL atau perintah konsol. + +# N + +## namespace + +Namespace merujuk pada [fitur bahasa PHP](https://www.php.net/manual/en/language.namespaces.php). + +# P + +## package + +Paket biasanya merujuk pada [paket Composer](https://getcomposer.org/doc/). Ini adalah kode yang siap digunakan ulang dan +didistribusikan, yang dapat diinstal secara otomatis melalui manajer paket. + +# R + +## rule + +Aturan biasanya merujuk pada aturan validasi dari paket [yiisoft/validator](https://github.com/yiisoft/validator). +Aturan menyimpan serangkaian parameter untuk memeriksa apakah sebuah himpunan data valid. +"Rule handler" melakukan pemrosesan sebenarnya. + +# Q + +## queue + +Antrian mirip dengan tumpukan (stack), tetapi mengikuti metodologi First-In-First-Out. + +# V + +## vendor + +Vendor adalah organisasi atau pengembang individu yang menyediakan kode dalam bentuk paket. Istilah ini juga dapat merujuk pada +direktori `vendor` milik [Composer](https://getcomposer.org/doc/). From cd05db831d5209344dcbae5a1d8c5fe6182375bd Mon Sep 17 00:00:00 2001 From: Fachruzi Ramadhan Date: Tue, 30 Sep 2025 09:48:25 +0700 Subject: [PATCH 2/2] Translate configuration.md, db-migrations.md, di-container.md, events.md and immutability.md to indonesian language --- guide/id/concept/configuration.md | 344 ++++++++++++++++++++++++++++ guide/id/concept/di-container.md | 211 +++++++++++++++++ guide/id/concept/events.md | 142 ++++++++++++ guide/id/concept/immutability.md | 149 ++++++++++++ guide/id/databases/db-migrations.md | 100 ++++++++ 5 files changed, 946 insertions(+) create mode 100644 guide/id/concept/configuration.md create mode 100644 guide/id/concept/di-container.md create mode 100644 guide/id/concept/events.md create mode 100644 guide/id/concept/immutability.md create mode 100644 guide/id/databases/db-migrations.md diff --git a/guide/id/concept/configuration.md b/guide/id/concept/configuration.md new file mode 100644 index 00000000..1420996c --- /dev/null +++ b/guide/id/concept/configuration.md @@ -0,0 +1,344 @@ +# Konfigurasi + +Ada banyak cara untuk mengonfigurasi aplikasi Anda. Kita akan fokus pada konsep yang digunakan dalam +[template proyek default](https://github.com/yiisoft/app). + +Konfigurasi Yii3 adalah bagian dari aplikasi. Anda dapat mengubah banyak aspek cara kerja aplikasi dengan mengedit +konfigurasi di dalam `config/`. + +## Plugin konfigurasi + +Dalam template aplikasi digunakan [yiisoft/config](https://github.com/yiisoft/config). Karena menulis seluruh konfigurasi +aplikasi dari awal adalah proses yang melelahkan, banyak paket menawarkan konfigurasi default, dan plugin membantu +menyalin konfigurasi-konfigurasi tersebut ke dalam aplikasi. + +Untuk menawarkan konfigurasi default, `composer.json` dari paket harus memiliki bagian `config-plugin`. +Saat memasang atau memperbarui paket dengan Composer, plugin membaca bagian `config-plugin` untuk setiap dependensi, +menyalin berkas-berkas tersebut ke `config/packages/` aplikasi jika belum ada dan menulis rencana penggabungan ke +`config/packages/merge_plan.php`. Rencana penggabungan mendefinisikan bagaimana menggabungkan konfigurasi-konfigurasi menjadi +satu array besar yang siap diteruskan ke [DI container](di-container.md). + +Lihat apa yang ada di `composer.json` "yiisoft/app" secara default: + +```json +"config-plugin-options": { + "output-directory": "config/packages" +}, +"config-plugin": { + "common": "config/common/*.php", + "params": [ + "config/params.php", + "?config/params-local.php" + ], + "web": [ + "$common", + "config/web/*.php" + ], + "console": [ + "$common", + "config/console/*.php" + ], + "events": "config/events.php", + "events-web": [ + "$events", + "config/events-web.php" + ], + "events-console": [ + "$events", + "config/events-console.php" + ], + "providers": "config/providers.php", + "providers-web": [ + "$providers", + "config/providers-web.php" + ], + "providers-console": [ + "$providers", + "config/providers-console.php" + ], + "routes": "config/routes.php" +}, +``` + +Terdapat banyak konfigurasi bernama. Untuk setiap nama ada sebuah konfigurasi. + +String berarti plugin mengambil konfigurasi apa adanya dan menggabungkannya dengan konfigurasi bernama sama dari paket yang Anda butuhkan. +Hal ini terjadi jika paket-paket tersebut memiliki `config-plugin` di `composer.json` mereka. + +Array berarti plugin akan menggabungkan banyak berkas sesuai urutan yang ditentukan. + +`?` di awal jalur berkas menunjukkan bahwa berkas tersebut mungkin tidak ada. Dalam kasus ini, berkas dilewati. + +`$` di awal nama berarti referensi ke konfigurasi bernama lain. + +`params` agak istimewa karena dicadangkan untuk parameter aplikasi. Ini otomatis tersedia sebagai `$params` di semua berkas konfigurasi lainnya. + +Anda dapat mempelajari lebih lanjut tentang fitur plugin konfigurasi [dari dokumentasinya](https://github.com/yiisoft/config/blob/master/README.md). + +## Berkas konfigurasi + +Sekarang, setelah Anda tahu bagaimana plugin merakit konfigurasi, lihat direktori `config`: + +``` +common/ + application-parameters.php + i18n.php + router.php +console/ +packages/ + yiisoft/ + dist.lock + merge_plan.php +web/ + application.php + psr17.php +events.php +events-console.php +events-web.php +params.php +providers.php +providers-console.php +providers-web.php +routes.php +``` + +### Konfigurasi container + +Aplikasi terdiri dari sekumpulan layanan yang terdaftar di dalam [dependency container](di-container.md). Berkas-berkas konfigurasi +yang bertanggung jawab untuk konfigurasi langsung dependency container berada di bawah direktori `common/`, `console/` dan `web/`. +Kita menggunakan `web/` untuk konfigurasi spesifik aplikasi web dan `console/` untuk konfigurasi spesifik perintah konsol. Baik web maupun +console berbagi konfigurasi di bawah `common/`. + +```php + [ + 'class' => ApplicationParameters::class, + 'charset()' => [$params['app']['charset']], + 'name()' => [$params['app']['name']], + ], +]; +``` + +Plugin konfigurasi meneruskan variabel khusus `$params` ke semua berkas konfigurasi. +Kode di atas meneruskan nilainya ke layanan. + +Panduan ["Dependency injection and container"](di-container.md) menjelaskan +format konfigurasi dan gagasan dependency injection secara rinci. + +Untuk kenyamanan, ada konvensi penamaan untuk kunci string kustom: + +1. Awali dengan nama paket seperti `yiisoft/cache-file/custom-definition`. +2. Jika konfigurasi untuk aplikasi itu sendiri, lewati prefix paket, misalnya `custom-definition`. + +### Service provider + +Sebagai alternatif mendaftarkan dependensi secara langsung, Anda dapat menggunakan service provider. Pada dasarnya, ini adalah kelas-kelas yang +mengonfigurasi dan mendaftarkan layanan di dalam container berdasarkan parameter yang diberikan. Mirip dengan tiga berkas konfigurasi +dependensi yang dijelaskan sebelumnya, ada tiga konfigurasi untuk menentukan service provider: `providers-console.php` untuk perintah konsol, +`providers-web.php` untuk aplikasi web dan `providers.php` untuk keduanya: + +```php +/* @var array $params */ + +// ... +use App\Provider\CacheProvider; +use App\Provider\MiddlewareProvider; +// ... + +return [ + // ... + 'yiisoft/yii-web/middleware' => MiddlewareProvider::class, + 'yiisoft/cache/cache' => [ + 'class' => CacheProvider::class, + '__construct()' => [ + $params['yiisoft/cache-file']['file-cache']['path'], + ], + ], + // ... +``` + +Dalam konfigurasi ini kunci adalah nama provider. Menurut konvensi, ini berupa `vendor/package-name/provider-name`. Nilai adalah nama kelas provider. +Kelas-kelas ini bisa dibuat dalam proyek itu sendiri atau disediakan oleh paket. + +Jika Anda perlu mengonfigurasi beberapa opsi untuk sebuah layanan, mirip dengan konfigurasi container langsung, ambil nilai +dari `$params` dan teruskan ke provider. + +Provider harus mengimplementasikan satu metode, `public function register(Container $container): void`. Dalam metode ini Anda +perlu menambahkan layanan ke container menggunakan metode `set()`. Di bawah ini adalah contoh provider untuk layanan cache: + +```php +use Psr\Container\ContainerInterface; +use Psr\SimpleCache\CacheInterface; +use Yiisoft\Aliases\Aliases; +use Yiisoft\Cache\Cache; +use Yiisoft\Cache\CacheInterface as YiiCacheInterface; +use Yiisoft\Cache\File\FileCache; +use Yiisoft\Di\Container; +use Yiisoft\Di\Support\ServiceProvider; + +final readonly class CacheProvider extends ServiceProvider +{ + public function __construct( + private string $cachePath = '@runtime/cache' + ) + { + $this->cachePath = $cachePath; + } + + public function register(Container $container): void + { + $container->set(CacheInterface::class, function (ContainerInterface $container) { + $aliases = $container->get(Aliases::class); + + return new FileCache($aliases->get($this->cachePath)); + }); + + $container->set(YiiCacheInterface::class, Cache::class); + } +} +``` + +### Routes + +Anda dapat mengonfigurasi bagaimana aplikasi web merespons URL tertentu di `config/routes.php`: + +```php +use App\Controller\SiteController; +use Yiisoft\Router\Route; + +return [ + Route::get('/')->action([SiteController::class, 'index'])->name('site/index') +]; +``` + +Baca lebih lanjut tentang ini di ["Routes"](../runtime/routing.md). + +### Events + +Banyak layanan memancarkan event tertentu yang bisa Anda lampirkan (attach). +Anda dapat melakukannya melalui tiga berkas konfigurasi: `events-web.php` untuk event aplikasi web, +`events-console.php` untuk event konsol dan `events.php` untuk keduanya. +Konfigurasinya adalah sebuah array di mana kunci adalah nama event dan nilainya adalah array handler: + +```php +return [ + EventName::class => [ + // Just a regular closure, it will be called from the Dispatcher "as is". + static fn (EventName $event) => someStuff($event), + + // A regular closure with an extra dependency. All the parameters after the first one (the event itself) + // will be resolved from your DI container within `yiisoft/injector`. + static fn (EventName $event, DependencyClass $dependency) => someStuff($event), + + // An example with a regular callable. If the `staticMethodName` method has some dependencies, + // they will be resolved the same way as in the earlier example. + [SomeClass::class, 'staticMethodName'], + + // Non-static methods are allowed too. In this case, `SomeClass` will be instantiated by your DI container. + [SomeClass::class, 'methodName'], + + // An object of a class with the `__invoke` method implemented + new InvokableClass(), + + // In this case, the `InvokableClass` with the `__invoke` method will be instantiated by your DI container + InvokableClass::class, + + // Any definition of an invokable class may be here while your `$container->has('the definition)` + 'di-alias' + ], +]; +``` + +Read more about it in ["Events"](events.md). + + +Baca lebih lanjut mengenai ini di ["Events"](events.md). + +### Parameter + +Parameter pada `config/params.php` menyimpan nilai konfigurasi yang digunakan di berkas konfigurasi lain untuk mengonfigurasi layanan +dan service provider. + +> [!TIP] +> Jangan menggunakan parameter, konstanta, atau variabel environment secara langsung di aplikasi Anda; konfigurasikan +> layanan sebagai gantinya. + +Params `params.php` aplikasi default terlihat seperti berikut: + +```php + [ + 'charset' => 'UTF-8', + 'locale' => 'en', + 'name' => 'My Project', + ], + + 'yiisoft/aliases' => [ + 'aliases' => [ + '@root' => dirname(__DIR__), + '@assets' => '@root/public/assets', + '@assetsUrl' => '/assets', + '@baseUrl' => '/', + '@message' => '@root/resources/message', + '@npm' => '@root/node_modules', + '@public' => '@root/public', + '@resources' => '@root/resources', + '@runtime' => '@root/runtime', + '@vendor' => '@root/vendor', + '@layout' => '@resources/views/layout', + '@views' => '@resources/views', + ], + ], + + 'yiisoft/yii-view' => [ + 'injections' => [ + Reference::to(ContentViewInjection::class), + Reference::to(CsrfViewInjection::class), + Reference::to(LayoutViewInjection::class), + ], + ], + + 'yiisoft/yii-console' => [ + 'commands' => [ + 'hello' => Hello::class, + ], + ], +]; +``` + +Untuk kenyamanan, terdapat konvensi penamaan mengenai parameter: + +1. Kelompokkan parameter berdasarkan nama paket seperti `yiisoft/cache-file`. +2. Jika parameter untuk aplikasi itu sendiri, seperti `app`, lewati prefix paket. +3. Jika terdapat banyak layanan dalam paket, seperti `file-target` dan `file-rotator` di paket `yiisoft/log-target-file`, + kelompokkan parameter berdasarkan nama layanan. +4. Gunakan `enabled` sebagai nama parameter agar dapat menonaktifkan atau mengaktifkan sebuah layanan, misalnya `yiisoft/yii-debug`. + +### Konfigurasi paket + +Plugin konfigurasi yang dijelaskan menyalin konfigurasi paket default ke direktori `packages/`. Setelah disalin Anda +menjadi pemilik konfigurasi tersebut, sehingga Anda dapat menyesuaikannya sesuka hati. `yiisoft/` dalam template default menunjukkan vendor paket. Karena +hanya paket `yiisoft` yang ada di template, terdapat satu direktori. `merge_plan.php` digunakan saat runtime untuk mendapatkan urutan +bagaimana konfigurasi-konfigurasi digabungkan. +Perlu dicatat bahwa untuk kunci konfigurasi seharusnya ada satu sumber kebenaran (single source of truth). +Satu konfigurasi tidak bisa menimpa nilai dari konfigurasi lain. + +`dist.lock` digunakan oleh plugin untuk melacak perubahan dan menampilkan diff antara konfigurasi saat ini dan contoh. diff --git a/guide/id/concept/di-container.md b/guide/id/concept/di-container.md new file mode 100644 index 00000000..5388cf2b --- /dev/null +++ b/guide/id/concept/di-container.md @@ -0,0 +1,211 @@ +# Dependency injection dan container + +## Dependency injection + +Ada dua cara untuk menggunakan kembali (re-use) sesuatu dalam OOP: pewarisan (inheritance) dan komposisi (composition). + +Pewarisan itu sederhana: + +```php +class Cache +{ + public function getCachedValue($key) + { + // .. + } +} + +final readonly class CachedWidget extends Cache +{ + public function render(): string + { + $output = $this->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +Masalahnya, kedua kelas ini menjadi saling terkait secara berlebihan (coupled) atau saling bergantung sehingga menjadi lebih rapuh. + +Cara lain untuk menangani ini adalah komposisi: + + +```php +interface CacheInterface +{ + public function getCachedValue($key); +} + +final readonly class Cache implements CacheInterface +{ + public function getCachedValue($key) + { + // .. + } +} + +final readonly class CachedWidget +{ + public function __construct( + private CacheInterface $cache + ) + { + } + + public function render(): string + { + $output = $this->cache->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +Kita menghindari pewarisan yang tidak perlu dan menggunakan interface untuk mengurangi keterikatan (coupling). Anda dapat mengganti implementasi cache tanpa mengubah `CachedWidget`, sehingga komponennya menjadi lebih stabil. + +`CacheInterface` di sini adalah sebuah dependency: sebuah objek yang menjadi ketergantungan objek lain. +Proses memasukkan instance dependency ke dalam objek (`CachedWidget`) disebut dependency injection. +Ada banyak cara untuk melakukannya: + +- Injeksi konstruktor (Constructor injection). Terbaik untuk dependency yang wajib. +- Injeksi metode (Method injection). Terbaik untuk dependency yang opsional. +- Injeksi properti (Property injection). Sebaiknya dihindari di PHP kecuali mungkin untuk data transfer object. + + +## DI container + +Menginjeksikan dependency dasar itu sederhana dan mudah. Anda memilih tempat di mana Anda tidak peduli tentang dependency, +biasanya sebuah action handler (penangan aksi), yang biasanya tidak akan Anda unit-test, membuat instance dependency yang dibutuhkan +dan meneruskannya ke kelas-kelas yang bergantung. + +Cara ini bekerja baik saat jumlah dependency sedikit dan tidak ada dependency bersarang. Ketika jumlahnya banyak dan setiap dependency +memiliki dependency sendiri, membuat keseluruhan hirarki menjadi proses yang melelahkan, membutuhkan banyak kode, dan dapat menyebabkan +kesalahan yang sulit dideteksi. + +Selain itu, banyak dependency, seperti pembungkus (wrapper) API pihak ketiga tertentu, sama untuk setiap kelas yang menggunakannya. +Jadi masuk akal untuk: + +- Menentukan cara membuat instance pembungkus API tersebut sekali saja. +- Membuatnya saat diperlukan dan hanya sekali per permintaan. + +Itulah fungsi dependency container. + +Dependency injection (DI) container adalah sebuah objek yang tahu bagaimana membuat dan mengonfigurasi objek serta +semua objek dependensinya. [Artikel Martin Fowler](https://martinfowler.com/articles/injection.html) menjelaskan dengan baik +mengapa DI container berguna. Di sini kita akan menjelaskan penggunaan DI container yang disediakan oleh Yii. + +Yii menyediakan fitur DI container melalui paket [yiisoft/di](https://github.com/yiisoft/di) dan +paket [yiisoft/injector](https://github.com/yiisoft/injector). + +### Mengonfigurasi container + +Karena untuk membuat objek baru Anda membutuhkan dependency-nya, Anda harus mendaftarkannya sedini mungkin. +Anda dapat melakukannya di konfigurasi aplikasi, mis. `config/web.php`. Untuk layanan berikut: + +```php +final class MyService implements MyServiceInterface +{ + public function __construct(int $amount) + { + } + + public function setDiscount(int $discount): void + { + + } +} +``` + +konfigurasinya bisa seperti: + +```php +return [ + MyServiceInterface::class => [ + 'class' => MyService::class, + '__construct()' => [42], + 'setDiscount()' => [10], + ], +]; +``` + +Itu setara dengan: + +```php +$myService = new MyService(42); +$myService->setDiscount(10); +``` + +Ada metode tambahan untuk mendeklarasikan dependency: + +```php +return [ + // declare a class for an interface, resolve dependencies automatically + EngineInterface::class => EngineMarkOne::class, + + // array definition (same as above) + 'full_definition' => [ + 'class' => EngineMarkOne::class, + '__construct()' => [42], + '$propertyName' => 'value', + 'setX()' => [42], + ], + + // closure + 'closure' => static function(ContainerInterface $container) { + return new MyClass($container->get('db')); + }, + + // static call + 'static_call' => [MyFactory::class, 'create'], + + // instance of an object + 'object' => new MyClass(), +]; +``` + +### Menginjeksikan dependency + +Mereferensikan container secara langsung dalam sebuah kelas adalah ide yang buruk karena kode menjadi tidak generik, terikat pada antarmuka container +dan, yang lebih buruk, dependency menjadi tersembunyi. Karena itu, Yii membalik kontrol dengan secara otomatis menginjeksikan +objek dari container ke beberapa konstruktor dan metode berdasarkan tipe argumen metode. + +Ini terutama dilakukan pada konstruktor dan metode penangan aksi: + +```php +use \Yiisoft\Cache\CacheInterface; + +final readonly class MyController +{ + public function __construct( + private CacheInterface $cache + ) + { + $this->cache = $cache; + } + + public function actionDashboard(RevenueReport $report) + { + $reportData = $this->cache->getOrSet('revenue_report', function() use ($report) { + return $report->getData(); + }); + + return $this->render('dashboard', [ + 'reportData' => $reportData, + ]); + } +} +``` + +Karena [yiisoft/injector](https://github.com/yiisoft/injector) yang membuat dan memanggil action handler, ia +memeriksa tipe argumen konstruktor dan metode, mengambil dependency dengan tipe tersebut dari container dan meneruskannya sebagai argumen. Hal ini biasanya disebut auto-wiring. Ini juga berlaku untuk sub-dependency: jika Anda tidak memberikan dependency secara eksplisit, container akan memeriksa apakah ia memiliki dependency tersebut terlebih dahulu. +Cukup deklarasikan dependency yang Anda butuhkan, dan dependency itu akan diambil dari container secara otomatis. + + +## Referensi + +- [Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler](https://martinfowler.com/articles/injection.html) diff --git a/guide/id/concept/events.md b/guide/id/concept/events.md new file mode 100644 index 00000000..36b4371b --- /dev/null +++ b/guide/id/concept/events.md @@ -0,0 +1,142 @@ +# Events + +Events memungkinkan Anda menjalankan kode kustom pada titik-titik eksekusi tertentu tanpa memodifikasi kode yang sudah ada. +Anda dapat melampirkan kode kustom yang disebut "handler" ke sebuah event sehingga ketika event tersebut dipicu, handler +akan dijalankan secara otomatis. + +Contohnya, ketika seorang pengguna mendaftar, Anda perlu mengirim email sambutan. Anda bisa melakukannya langsung di +`SignupService` tetapi kemudian, ketika Anda juga perlu mengubah ukuran (resize) gambar avatar pengguna, Anda harus +mengubah kode `SignupService` lagi. Dengan kata lain, `SignupService` akan terikat (coupled) pada kode pengirim email dan +kode pengubah ukuran avatar. + +Untuk menghindari hal itu, alih-alih menentukan apa yang harus dilakukan setelah pendaftaran secara eksplisit, Anda dapat +mengangkat (raise) event `UserSignedUp` lalu menyelesaikan proses pendaftaran. Kode pengirim email dan kode pengubah ukuran +avatar akan melampirkan handler ke event tersebut dan, oleh karena itu, akan dijalankan. Jika sewaktu-waktu Anda perlu melakukan +tindakan tambahan saat pendaftaran, Anda bisa menambahkan handler event tanpa memodifikasi `SignupService`. + +Untuk menaikkan event dan melampirkan handler ke event-event tersebut, Yii menyediakan layanan khusus bernama event dispatcher. +Layanan ini tersedia dari paket [yiisoft/event-dispatcher](https://github.com/yiisoft/event-dispatcher). + +## Event Handlers + +Event handler adalah sebuah [PHP callable](https://www.php.net/manual/en/language.types.callable.php) yang dijalankan +ketika event yang dilampirkannya dipicu. + +Tanda tangan (signature) dari sebuah event handler adalah: + +```php +function (EventClass $event) { + // handle it +} +``` + +## Melampirkan event handlers + +Anda dapat melampirkan handler ke sebuah event seperti berikut: + +```php +use Yiisoft\EventDispatcher\Provider\Provider; + +final readonly class WelcomeEmailSender +{ + public function __construct(Provider $provider) + { + $provider->attach([$this, 'handleUserSignup']); + } + + public function handleUserSignup(UserSignedUp $event) + { + // handle it + } +} +``` + +Metode `attach()` menerima sebuah callback. Berdasarkan tipe argumen callback tersebut, jenis event akan ditentukan. + +## Urutan event handlers + +Anda dapat melampirkan satu atau lebih handler ke sebuah event. Ketika sebuah event dipicu, handler yang terlampir +akan dipanggil sesuai urutan saat mereka dilampirkan pada event. Jika sebuah event mengimplementasikan +`Psr\EventDispatcher\StoppableEventInterface`, handler event dapat menghentikan eksekusi handler-handler berikutnya +jika `isPropagationStopped()` mengembalikan `true`. + +Secara umum, lebih baik tidak bergantung pada urutan eksekusi handler event. + +## Menaikkan event + +Event dinaikkan (dispatched) seperti berikut: + +```php +use Psr\EventDispatcher\EventDispatcherInterface; + +final readonly class SignupService +{ + public function __construct( + private EventDispatcherInterface $eventDispatcher + ) + { + } + + public function signup(SignupForm $form) + { + // handle signup + + $event = new UserSignedUp($form); + $this->eventDispatcher->dispatch($event); + } +} +``` + +Pertama, Anda membuat sebuah event dengan menyertakan data yang mungkin berguna bagi handler. Lalu Anda mendispatch event itu. + +Kelas event itu sendiri dapat terlihat seperti berikut: + +```php +final readonly class UserSignedUp +{ + public function __construct( + public SignupForm $form + ) + { + } +} +``` + +## Hirarki Events + +Events tidak memiliki nama atau pencocokan wildcard atas tujuan tertentu. Nama kelas event dan hirarki kelas/interface +serta komposisi dapat digunakan untuk mencapai fleksibilitas yang baik: + +```php +interface DocumentEvent +{ +} + +final readonly class BeforeDocumentProcessed implements DocumentEvent +{ +} + +final readonly class AfterDocumentProcessed implements DocumentEvent +{ +} +``` + +Dengan menggunakan interface, Anda dapat mendengarkan semua event yang berkaitan dengan dokumen: + +```php +$provider->attach(function (DocumentEvent $event) { + // log events here +}); +``` + +## Melepas (detaching) event handlers + +Untuk melepas handler dari sebuah event Anda dapat memanggil metode `detach()`: + +```php +$provider->detach(DocmentEvent::class); +``` + +## Mengonfigurasi event aplikasi + +Biasanya Anda menugaskan (assign) event handler melalui konfigurasi aplikasi. Lihat ["Configuration"](configuration.md) untuk detail. diff --git a/guide/id/concept/immutability.md b/guide/id/concept/immutability.md new file mode 100644 index 00000000..5d7cff80 --- /dev/null +++ b/guide/id/concept/immutability.md @@ -0,0 +1,149 @@ +# Immutability + +Immutability berarti status (state) sebuah objek tidak dapat diubah setelah objek tersebut dibuat. +Alih-alih memodifikasi sebuah instance, Anda membuat instance baru dengan perubahan yang diinginkan. +Pendekatan ini umum untuk objek bernilai (value objects) seperti Money, ID, dan DTO. Ini membantu menghindari efek samping yang tidak disengaja: +metode tidak dapat diam-diam mengubah state yang dibagi, sehingga kode menjadi lebih mudah untuk dipahami. + +## Perangkap mutable (yang kita hindari) + +```php +// A shared base query built once and reused: +$base = Post::find()->where(['status' => Post::STATUS_PUBLISHED]); + +// Somewhere deep in the code we only need one post: +$one = $base->limit(1)->one(); // mutates the underlying builder (sticky limit!) + +// Later we reuse the same $base expecting a full list: +$list = $base->orderBy(['created_at' => SORT_DESC])->all(); +// Oops: still limited to 1 because the previous limit(1) modified $base. +``` + +## Membuat objek immutable di PHP + +Tidak ada cara langsung untuk memodifikasi sebuah instance, tetapi Anda dapat menggunakan clone untuk membuat instance baru dengan perubahan yang diinginkan. +Itulah yang dilakukan metode `with*`. + +```php +final class Money +{ + public function __construct( + private int $amount, + private string $currency, + ) { + $this->validateAmount($amount); + $this->validateCurrency($currency); + } + + private function validateAmount(string $amount) + { + if ($amount < 0) { + throw new InvalidArgumentException('Amount must be positive.'); + } + } + + private function validateCurrency(string $currency) + { + if (!in_array($currency, ['USD', 'EUR'])) { + throw new InvalidArgumentException('Invalid currency. Only USD and EUR are supported.'); + } + } + + public function withAmount(int $amount): self + { + $this->validateAmount($amount); + + if ($amount === $this->amount) { + return $this; + } + + $clone = clone $this; + $clone->amount = $amount; + return $clone; + } + + public function withCurrency(string $currency): self + { + $this->validateCurrency($currency); + + if ($currency === $this->currency) { + return $this; + } + + $clone = clone $this; + $clone->currency = $currency; + return $clone; + } + + public function amount(): int + { + return $this->amount; + } + + public function currency(): string + { + return $this->currency; + } + + public function add(self $money): self + { + if ($money->currency !== $this->currency) { + throw new InvalidArgumentException('Currency mismatch. Cannot add money of different currency.'); + } + return $this->withAmount($this->amount + $money->amount); + } +} + +$price = new Money(1000, 'USD'); +$discounted = $price->withAmount(800); +// $price is still 1000 USD, $discounted is 800 USD +``` + +- Kita menandai kelas sebagai `final` untuk mencegah perubahan oleh subclass; sebagai alternatif, rancang ekstensi dengan hati-hati. +- Lakukan validasi di konstruktor dan metode `with*` sehingga setiap instance selalu valid. + +> [!TIP] +> Jika Anda mendefinisikan DTO sederhana, Anda dapat menggunakan kata kunci `readonly` di PHP modern dan membiarkan properti `public`. Kata kunci `readonly` memastikan properti tidak dapat diubah setelah objek dibuat. + +## Menggunakan clone (dan mengapa ini murah) + +Clone PHP melakukan salinan dangkal (shallow copy) dari objek. Untuk objek nilai immutable yang hanya berisi skalar +atau objek immutable lainnya, cloning dangkal sudah cukup dan cepat. Di PHP modern, cloning objek nilai kecil +murah baik dari segi waktu maupun memori. + +Jika objek Anda memegang sub-objek yang mutable dan juga harus disalin, implementasikan `__clone` untuk melakukan deep-clone pada sub-objek tersebut: + +```php +final class Order +{ + public function __construct( + private Money $total + ) {} + + public function total(): Money + { + return $this->total; + } + + public function __clone(): void + { + // Money is immutable in our example, so a deep clone is not required. + // If it were mutable, you could do: $this->total = clone $this->total; + } + + public function withTotal(Money $total): self + { + $clone = clone $this; + $clone->total = $total; + return $clone; + } +} +``` + +## Gaya penggunaan + +- Bangun sebuah value object sekali lalu teruskan. Jika Anda perlu mengubahnya, gunakan metode `with*` yang mengembalikan instance baru. +- Utamakan field skalar/immutable di dalam objek immutable; jika sebuah field dapat berubah, isolasi dan lakukan deep-clone di `__clone` bila diperlukan. + +Immutability sejalan dengan preferensi Yii untuk kode yang dapat diprediksi dan bebas efek samping, serta membuat layanan, caching, +dan konfigurasi menjadi lebih tangguh. diff --git a/guide/id/databases/db-migrations.md b/guide/id/databases/db-migrations.md new file mode 100644 index 00000000..0251a2aa --- /dev/null +++ b/guide/id/databases/db-migrations.md @@ -0,0 +1,100 @@ +# Migrasi + +Untuk menggunakan migrasi, pasang paket [yiisoft/db-migration](https://github.com/yiisoft/db-migration/): + +```shell +composer require yiisoft/db-migration +``` + +### Contoh penggunaan + +Pertama, konfigurasikan DI container. Buat `config/common/db.php` dengan isi berikut: + +```php + [ + 'class' => SqliteConnection::class, + '__construct()' => [ + 'dsn' => 'sqlite:' . __DIR__ . '/Data/yiitest.sq3' + ] + ] +]; +``` + +Tambahkan hal berikut ke `config/params.php`: + +```php +... +'yiisoft/db-migration' => [ + 'newMigrationNamespace' => 'App\\Migration', + 'sourceNamespaces' => ['App\\Migration'], +], +... +``` + +Sekarang tes apakah sudah bekerja: + +```shell +./yii list migrate +``` + +### Membuat migrasi + +Untuk bekerja dengan migrasi, Anda dapat menggunakan [view](https://github.com/yiisoft/db-migration/tree/master/resources/views) yang disediakan. + +```shell +./yii migrate:create my_first_table --command=table --fields=name,example --table-comment=my_first_table +``` + +Perintah tersebut akan menghasilkan file seperti berikut: + +```php +createTable('my_first_table', [ + 'id' => $b->primaryKey(), + 'name', + 'example', + ]); + + $b->addCommentOnTable('my_first_table', 'dest'); + } + + public function down(MigrationBuilder $b): void + { + $b->dropTable('my_first_table'); + } +} +``` + +Untuk informasi lebih lanjut lihat dokumentasi paket [di sini](https://github.com/yiisoft/db-migration/tree/master/docs/guide/en). + +### Migrasi dari Yii2 + +Migrasi di Yii2 dan paket [yiisoft/db-migration](https://github.com/yiisoft/db-migration/) tidak kompatibel, +dan tabel `migration` juga tidak kompatibel. +Solusi yang mungkin adalah menggunakan dump struktur dan mengganti nama tabel `migration` lama. Pada eksekusi awal +migrasi, tabel `migration` baru dengan field yang baru akan dibuat. Semua perubahan selanjutnya pada skema basis data +akan diterapkan menggunakan komponen migrasi yang baru dan dicatat di tabel migrasi yang baru.