## 1. Project Overview & Quickstart (yiisoft/output-formatting)

# output-formatting

Open-source repository yiisoft/output-formatting

### Repository Details
- **Repository:** [yiisoft/output-formatting](https://github.com/yiisoft/output-formatting)
- **Primary Language:** Code

*Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.*

## 2. Official Technical Reference & Guides (yiisoft/docs)

## File: src/id/guide/databases/db-migrations.md

# Migrations

During the course of developing and maintaining a database-driven
application, the structure of the database being used evolves just like the
source code does. For example, during the development of an application, a
new table may be found necessary; after the application is deployed to
production, it may be discovered that an index should be created to improve
the query performance; and so on. Because a database structure change often
requires some source code changes, Yii supports the so-called *database
migration* feature that allows you to keep track of database changes in
terms of *database migrations* which are version-controlled together with
the source code.

The following steps show how database migration can be used by a team during
development:

1. Tim creates a new migration (e.g. creates a new table, changes a column
   definition, etc.).
2. Tim commits the new migration into the source control system (e.g. Git,
   Mercurial).
3. Doug updates his repository from the source control system and receives
   the new migration.
4. Doug applies the migration to his local development database, thereby
   synchronizing his database to reflect the changes that Tim has made.

And the following steps show how to deploy a new release with database
migrations to production:

1. Scott creates a release tag for the project repository that contains some
   new database migrations.
2. Scott updates the source code on the production server to the release
   tag.
3. Scott applies any accumulated database migrations to the production
   database.

```mermaid
sequenceDiagram
    participant Tim
    participant VCS as Source control
    participant Doug
    participant LocalDB as Local development database
    participant Scott
    participant ProdServer as Production server
    participant ProdDB as Production database

    Tim->>Tim: Creates a new migration
    Tim->>VCS: Commits the new migration
    Doug->>VCS: Updates his repository and receives the new migration
    Doug->>LocalDB: Applies the migration locally

    Scott->>VCS: Creates a release tag with new migrations
    Scott->>ProdServer: Updates source code to the release tag
    Scott->>ProdDB: Applies accumulated migrations
```

Yii provides a set of migration command line tools that allow you to:

* create new migrations;
* apply migrations;
* revert migrations;
* re-apply migrations;
* show migration history and status.

All these tools are accessible through the command `yii migrate`. In this
section we will describe in detail how to accomplish various tasks using
these tools.

> [!TIP]
> Migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC
hierarchy or clean up cache.

> [!NOTE]
> When manipulating data using a migration you may find that using your Active Record or entity classes
> for this might be useful because some of the logic is already implemented there. Keep in mind however, that in contrast
> to code written in the migrations, whose nature is to stay constant forever, application logic is subject to change.
> So when using Active Record or entity classes in migration code, changes to the logic in the source code
> may accidentally break the existing migrations. For this reason migration code should be kept independent of other
> application logic such.

## Initial configuration

To use migrations, install
[yiisoft/db-migration](https://github.com/yiisoft/db-migration/) package:

```shell
make composer require yiisoft/db-migration
```

Create a directory to store migrations `src/Migration` right in the project
root.

Add the following configuration to `config/common/params.php`:

```php
'yiisoft/db-migration' => [
    'newMigrationNamespace' => 'App\\Migration',
    'sourceNamespaces' => ['App\\Migration'],
],
```

If you want to place migrations elsewhere, you can define the path in
`newMigrationPath`. If your migrations to be applied are from multiple
sources, such as external modules, `sourcePaths` could be used to define
these.

You need a database connection configured as well. See [Working with
databases](../start/databases.md) for an example of configuring it for
PostgreSQL.

## Creating a migration

To create a new empty migration, run the following command:

```sh
make shell
./yii migrate:create <name>
```

The required `name` argument gives a brief description about the new
migration. For example, if the migration is about creating a new table named
*news*, you may use the name `create_news_table` and run the following
command:

```
make shell
./yii migrate:create create_news_table
```


> [!NOTE]
> Because the `name` argument will be used as part of the generated migration class name,
> it should only contain letters, digits, and/or underscore characters.

The above command will create a new PHP class file named
`src/Migration/M251225221906CreateNewsTable.php`.  The file contains the
following code which mainly declares a migration class with the skeleton
code:

```php
<?php

declare(strict_types=1);

namespace App\Migration;

use Yiisoft\Db\Migration\MigrationBuilder;
use Yiisoft\Db\Migration\RevertibleMigrationInterface;

final class M251225221906CreateNewsTable implements RevertibleMigrationInterface
{
    public function up(MigrationBuilder $b): void
    {
        // TODO: Implement the logic to apply the migration.
    }

    public function down(MigrationBuilder $b): void
    {
        // TODO: Implement the logic to revert the migration.
    }
}
```

In the migration class, you are expected to write code in the `up()` method
that makes changes to the database structure.  You may also want to write
code in the `down()` method to revert the changes made by `up()`. The `up()`
method is invoked when you upgrade the database with this migration, while
the `down()` method is invoked when you downgrade the database.  The
following code shows how you may implement the migration class to create a
`news` table:

```php
<?php

declare(strict_types=1);

namespace App\Migration;

use Yiisoft\Db\Migration\MigrationBuilder;
use Yiisoft\Db\Migration\RevertibleMigrationInterface;

final class M251225221906CreateNewsTable implements RevertibleMigrationInterface
{
    public function up(MigrationBuilder $b): void
    {
        $cb = $b->columnBuilder();

        $b->createTable('news', [
            'id' => $cb::uuidPrimaryKey(),
            'title' => $cb::string()->notNull(),
            'content' => $cb::text(),
        ]);
    }

    public function down(MigrationBuilder $b): void
    {
        $b->dropTable('news');
    }
}
```

Migration builder `$b` in the above manages database schema while the column
builder `$cb` manages column types. Both allow using *abstract types*. When
a migration is applied to a particular database, the abstract types will be
translated into the corresponding database physical types and corresponding
SQL to define them.

Methods available in migration builder belong to the following types:

- Raw queries
  - getDb — to get database connection instance.
  - execute — to execute raw SQL query.
- Data
  - insert / update / delete
  - batchInsert
  - upsert
- Tables and views
  - createTable / renameTable / dropTable
  - truncateTable
  - addCommentOnTable / dropCommentFromTable
  - createView / dropView
- Columns
  - addColumn / renameColumn / alterColumn / dropColumn
  - addCommentOnColumn / dropCommentFromColumn
- Keys and indexes
  - addPrimaryKey / dropPrimaryKey
  - addForeignKey / dropForeignKey
  - createIndex / dropIndex

Additionally, there's a `columnBuilder()` which is used to obtain a column
builder as in example above. The builder has static methods that define
various column types:

- Keys
    - primaryKey
    - smallPrimaryKey
    - bigPrimaryKey
    - uuidPrimaryKey
- Boolean
  - boolean
- Numbers
  - bit
  - tinyint
  - smallint
  - integer
  - bigint
  - flat
  - double
  - decimal
- Strings
  - char
  - string
  - text
- Date and time
  - timestamp
  - datetime
  - datetimeWithTimezone
  - time
  - timeWithTimezone
  - date
- Special types
  - money
  - binary
  - uuid
  - array
  - structured
  - json
- enum

All the above methods create a base type which could be adjusted with
additional methods:

- null / notNull
- defaultValue
- unique
- scale / size / unsigned
- primaryKey / autoIncrement
- check
- comment
- computed
- extra
- reference

### Irreversible migrations

Not all migrations are reversible. For example, if the `up()` method deletes
a row of a table, you may not be able to recover this row in the `down()`
method. Sometimes, you may be just too lazy to implement the `down()`,
because it is not very common to revert database migrations. In this case,
you should implement `Yiisoft\Db\Migration\MigrationInterface` that has
`up()` only.


### Transactional migrations

While performing complex DB migrations, it is important to ensure each
migration to either succeed or fail as a whole so that the database can
maintain integrity and consistency. To achieve this goal, it is recommended
that you may enclose the DB operations of each migration in a transaction
automatically by adding `TransactionalMigrationInterface` to `implements` of
your migration.

As a result, if any operation in the `up()` or `down()` method fails, all
prior operations will be rolled back automatically.

> Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,
please refer to [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).

## Generating a migration

Instead of writing migrations by hand, the command provides a convenient way
generate some of the code.

```shell
make shell
./yii migrate:create -- my_first_table --command=table --fields=name,example --table-comment=my_first_table
```

That would generate the following:

```php
<?php

declare(strict_types=1);

namespace App\Migration;

use Yiisoft\Db\Migration\MigrationBuilder;
use Yiisoft\Db\Migration\RevertibleMigrationInterface;
use Yiisoft\Db\Migration\TransactionalMigrationInterface;

/**
 * Handles the creation of table `my_first_table`.
 */
final class M251227095006CreateMyFirstTableTable implements RevertibleMigrationInterface, TransactionalMigrationInterface
{
    public function up(MigrationBuilder $b): void
    {
        $columnBuilder = $b->columnBuilder();

        $b->createTable('my_first_table', [
            'id' => $columnBuilder::primaryKey(),
            'name',
            'example',
        ]);

        $b->addCommentOnTable('my_first_table', 'my_first_table');
    }

    public function down(MigrationBuilder $b): void
    {
        $b->dropTable('my_first_table');
    }
}
```

Commands available are:

- create - empty migration.
- table - creating a table. Use `--fields` specify a list of fields to
  use. Types could be specified as well such as
  `id:primaryKey,name:string:defaultValue("Alex"),user_id:integer:foreignKey,category_id2:integer:foreignKey(category
  id2)`.
- dropTable - dropping a table.
- addColumn - adding a column.
- dropColumn - dropping a column.
- junction - creating a junction table. Use `--and` specify a second table.

## Applying Migrations

To upgrade a database to its latest structure, you should apply all
available new migrations using the following command:

```
./yii migrate:up
```

This command will list all migrations that have not been applied so far. If
you confirm that you want to apply these migrations, it will run the `up()`
method in every new migration class, one after another, in the order of
their timestamp values. If any of the migrations fails, the command will
quit without applying the rest of the migrations.

For each migration that has been successfully applied, the command will
insert a row into a database table named `migration` to record the
successful application of the migration. This will allow the migration tool
to identify which migrations have been applied and which have not.

Sometimes, you may only want to apply one or a few new migrations, instead
of all available migrations.  You can do so by specifying the number of
migrations that you want to apply when running the command.  For example,
the following command will try to apply the next three available migrations:

```
./yii migrate:up --limit=3
```

## Reverting Migrations

To revert (undo) one or multiple migrations that have been applied before,
you can run the following command:

```
./yii migrate:down            # revert the most recently applied migration
./yii migrate:down --limit=3  # revert the most 3 recently applied migrations
./yii migrate:down --all      # revert all migrations
```

> Note: Not all migrations are reversible. Trying to revert such migrations will cause an error and stop the
entire reverting process.


## Redoing Migrations

Redoing migrations means first reverting the specified migrations and then
applying again. This can be done as follows:

```
./yii migrate:redo            # redo the last applied migration
./yii migrate:redo --limit=3  # redo the last 3 applied migrations
./yii migrate:redo --all      # redo all migrations
```

> Note: If a migration is not reversible, you will not be able to redo it.

## Listing Migrations

To list which migrations have been applied and which are not, you may use
the following commands:

```
./yii migrate/history            # showing the last 10 applied migrations
./yii migrate:history --limit=5  # showing the last 5 applied migrations
./yii migrate:history --all      # showing all applied migrations

./yii migrate:new                # showing the first 10 new migrations
./yii migrate:new --limit=5      # showing the first 5 new migrations
./yii migrate:new --all          # showing all new migrations
```

### Upgrading from Yii 2.0

Migrations in Yii 2.0 and the
[yiisoft/db-migration](https://github.com/yiisoft/db-migration/) package are
not compatible, and the `migration` table is also not compatible.

A probable solution is to use structure dumps and rename the old `migration`
table. Upon the initial execution of migrations, a new `migration` table
with new fields will be created. All further changes in the database schema
are applied using the new `migration` component and recorded in the new
migration table.

---

## File: src/id/guide/concept/aliases.md

# 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',
```

The `yiisoft/aliases` parameter initializes `Aliases` service from
[`yiisoft/aliases`](https://github.com/yiisoft/aliases) package.  You can
set extra aliases in runtime by using the service:

```php
use \Yiisoft\Aliases\Aliases;

public function actionIndex(Aliases $aliases)
{
    $aliases->set('@uploads', '@root/uploads');
}
```

## Using aliases in configuration

Disarankan untuk me-resolve alias pada level konfigurasi, sehingga service
menerima URL dan path sebagai string siap pakai:

```php
<?php

declare(strict_types=1);

use Yiisoft\Aliases\Aliases;
use Yiisoft\Cache\File\FileCache;

/* @var $params array */

return [
    FileCache::class => static fn (Aliases $aliases) => new FileCache(
        $aliases->get($params['yiisoft/cache-file']['fileCache']['path'])
    ),
];
```

## Resolving aliases

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
} 
```

Jika `@foo/bar` tidak didefinisikan sebagai alias akar, pernyataan terakhir
akan menghasilkan `/path/to/foo/bar/file.php`.


## Predefined aliases

[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.

---

## File: src/id/guide/concept/autoloading.md

# 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).

---

## File: src/id/guide/concept/configuration.md

# Konfigurasi

Ada banyak cara untuk mengonfigurasi aplikasi Anda. Kita akan fokus pada
konsep yang digunakan dalam [template proyek
default](https://github.com/yiisoft/app).

Yii3 configs are part of the application. You can change many aspects of how
the application works by editing configuration under `config/` directory.

## Plugin konfigurasi

The application template uses
[yiisoft/config](https://github.com/yiisoft/config) as a composer plugin to
assemble configs.

Packages provide default configurations through defined [config
groups](https://github.com/yiisoft/config#config-groups)  in the
`config-plugin` key in the `extra` section of the `composer.json`:

```json
"extra": {
    "config-plugin-options": {
        "source-directory": "config",
    },
    "config-plugin": {
        "params": "params.php",
        "web": "web.php"
    }
}
```

After Composer updates autoload files, such as during `dump-autoload`,
`install`, `require`, `update`, or `remove`, the plugin scans installed
packages for configurations and writes a merge plan to
`config/.merge-plan.php`.  The merge plan declares the order of merging
found configurations into the final configuration to be passed to [DI
container](di-container.md).

At runtime, `Yiisoft\Config\Config` loads the defined config groups
according to that merge plan.  Configs are read in three layers:

1. Vendor package configs with default values.
2. Root package configs from the application `config/` directory.
3. Environment-specific configs from the application.

> [!WARNING]
> Config keys with the same name are not allowed within a single layer.

The application template stores configurations in a PHP file instead of
inline JSON:

```json
"extra": {
    "config-plugin-file": "config/configuration.php"
}
```

That file returns the same keys you could otherwise define in
`composer.json`: `config-plugin`, `config-plugin-options`, and
`config-plugin-environments`.

```php
use App\Environment;

return [
    'config-plugin-options' => [
        'source-directory' => 'config',
    ],
    'config-plugin' => [
        'params' => 'common/params.php',
        'params-web' => [
            '$params',
            'web/params.php',
        ],
        'params-console' => [
            '$params',
            'console/params.php',
        ],
        'bootstrap' => 'common/bootstrap.php',
        'bootstrap-web' => '$bootstrap',
        'bootstrap-console' => '$bootstrap',
        'di' => 'common/di/*.php',
        'di-web' => [
            '$di',
            'web/di/*.php',
        ],
        'di-console' => '$di',
        'di-delegates' => [],
        'di-delegates-console' => '$di-delegates',
        'di-delegates-web' => '$di-delegates',
        'di-providers' => [],
        'di-providers-web' => '$di-providers',
        'di-providers-console' => '$di-providers',
        'events' => [],
        'events-web' => '$events',
        'events-console' => '$events',
        'routes' => 'common/routes.php',
    ],
    'config-plugin-environments' => [
        Environment::DEV => [
            'params' => [
                'environments/dev/params.php',
            ],
        ],
        Environment::TEST => [
            'params' => [
                'environments/test/params.php',
            ],
        ],
        Environment::PROD => [
            'params' => [
                'environments/prod/params.php',
            ],
        ],
    ],
];
```

Config group names are mapping keys. Their values are file paths relative to
the `source-directory` option, which defaults to the directory containing
`composer.json`.

- A string value loads one file.
- An array loads and merges multiple files in the given order.
- `?` marks an optional file.
- `*` marks a wildcard path.
- `$group` references another config group to be merged.

The `params` group is special: its values are available as `$params` in
other config files by default.

You can read more in the [yiisoft/config
documentation](https://github.com/yiisoft/config/blob/master/README.md).

## Berkas konfigurasi

Now, as you know how the plugin assembles configs, look at the `config/`
directory structure used by the default application template:

```
common/
    bootstrap.php
    params.php
    routes.php
    di/
        *.php
console/
    params.php
environments/
    dev/
        params.php
    prod/
        params.php
    test/
        params.php
web/
    params.php
    di/
        *.php
.merge-plan.php
configuration.php
```

This structure follows the groups defined in `config/configuration.php`:

- `params`, `params-web`, and `params-console` load parameter files from
  `common/`, `web/`, and `console/`.
- `di` and `di-web` load container definitions from `common/di/` and
  `web/di/`.
- `routes` and `bootstrap` load `common/routes.php` and
  `common/bootstrap.php`.
- `config-plugin-environments` adds environment-specific parameter files
  from `environments/dev/`, `environments/test/`, and `environments/prod/`.

At runtime, `.merge-plan.php` stores the assembled merge plan generated from
`configuration.php`.

### Konfigurasi container

The application consists of services registered in a [dependency
container](di-container.md). In the default template, direct container
definitions live in `config/common/di/*.php` and `config/web/di/*.php`.

Definitions from `common/di/` are shared by web and console entry
points. The `di-web` group adds web-specific definitions from `web/di/`. The
`di-console` group currently reuses the shared `di` definitions without
extra files.

```php
<?php

declare(strict_types=1);

use App\Shared\ApplicationParams;

/** @var array $params */

return [
    ApplicationParams::class => [
        '__construct()' => [
            'name' => $params['application']['name'],
            'charset' => $params['application']['charset'],
            'locale' => $params['application']['locale'],
        ],
    ],
];
```

Config plugin passes the special `$params` variable to all config files. The
definition uses it to configure services.

Panduan tentang ["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 prefiks paket,
   mis. `custom-definition`.

### Service provider

As an alternative to registering dependencies directly, you can use service
providers. A service provider is a class that returns a reusable set of
container definitions and extensions.

The default template exposes three config groups for service providers:
`di-providers`, `di-providers-web`, and `di-providers-console`. In the
template they are initialized as empty arrays in `config/configuration.php`,
and you can later populate them inline or switch them to load values from
files.

Prefer direct container configuration for application services with a simple
definition: a class name, an interface implementation, constructor arguments
from `$params`, or a closure that creates a single service.

Use a service provider when the registration itself is a reusable unit:

- A package registers several related services, aliases, or extensions.
- A service registration also needs supporting definitions.
- The same registration should be reused in several applications.
- Registration depends on optional classes or environment-specific
  decisions.

Use a factory class when construction logic belongs to one service. A
factory returns an object. A service provider returns definitions for
several related services.

```php
/* @var array $params */

// ...
use App\Provider\CacheProvider;
use App\Provider\MiddlewareProvider;
// ...

return [
    // ...
    'yiisoft/yii-web/middleware' => MiddlewareProvider::class,
    'yiisoft/cache/cache' => new CacheProvider($params['yiisoft/cache-file']['file-cache']['path']),
    // ...
];
```

In this config keys are provider names. By convention these are
`vendor/package-name/provider-name`. Values are provider class names or
provider instances. These classes could be either created in the project
itself or provided by a package.

Jika Anda perlu mengonfigurasi beberapa opsi untuk sebuah layanan, mirip
dengan konfigurasi container langsung, ambil nilai dari `$params` dan
teruskan ke provider.

Provider should implement `Yiisoft\Di\ServiceProviderInterface`. Below is a
provider for a cache service:

```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\ServiceProviderInterface;

final readonly class CacheProvider implements ServiceProviderInterface
{
    public function __construct(
        private string $cachePath = '@runtime/cache'
    )
    {
    }

    public function getDefinitions(): array
    {
        return [
            CacheInterface::class => function (ContainerInterface $container) {
                $aliases = $container->get(Aliases::class);

                return new FileCache($aliases->get($this->cachePath));
            },
            YiiCacheInterface::class => Cache::class,
        ];
    }

    public function getExtensions(): array
    {
        return [];
    }
}
```

### Rute

You can configure how the web application responds to certain URLs in
`config/common/routes.php`:

```php
use App\Web;
use Yiisoft\Router\Group;
use Yiisoft\Router\Route;

return [
    Group::create()
        ->routes(
            Route::get('/')
                ->action(Web\HomePage\Action::class)
                ->name('home'),
        ),
];
``` 

Baca lebih lanjut tentang ini di ["Routes"](../runtime/routing.md).

### Event

Many services emit certain events that you can attach to.  The default
template defines three event config groups in `config/configuration.php`:
`events`, `events-web`, and `events-console`. They are empty by default, but
you can populate them inline or point them to event config files.

The configuration is an array where keys are event names and values are
arrays of handlers:

```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 invokable class definition may be here as long as `$container->has('the definition')` is true.
        'di-alias'
    ],
];
```

Baca lebih lanjut di ["Events"](events.md).


### Parameter

Base parameters are stored in `config/common/params.php`. They are then
extended by `config/web/params.php`, `config/console/params.php`, and
environment-specific files under `config/environments/`.

> [!TIP]
> Don't use parameters, constants, or environment variables directly in your application, configure
> services instead.

The default application `common/params.php` looks like the following:

```php
<?php

declare(strict_types=1);

use App\Shared\ApplicationParams;
use Yiisoft\Aliases\Aliases;
use Yiisoft\Assets\AssetManager;
use Yiisoft\Definitions\Reference;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\UrlGeneratorInterface;
use Yiisoft\Yii\View\Renderer\CsrfViewInjection;

return [
    'application' => require __DIR__ . '/application.php',

    'yiisoft/aliases' => [
        'aliases' => require __DIR__ . '/aliases.php',
    ],

    'yiisoft/view' => [
        'basePath' => null,
        'parameters' => [
            'assetManager' => Reference::to(AssetManager::class),
            'applicationParams' => Reference::to(ApplicationParams::class),
            'aliases' => Reference::to(Aliases::class),
            'urlGenerator' => Reference::to(UrlGeneratorInterface::class),
            'currentRoute' => Reference::to(CurrentRoute::class),
        ],
    ],

    'yiisoft/yii-view-renderer' => [
        'viewPath' => null,
        'layout' => '@src/Web/Shared/Layout/Main/layout.php',
        'injections' => [
            Reference::to(CsrfViewInjection::class),
        ],
    ],
];
```

Untuk kenyamanan, ada konvensi penamaan mengenai parameter:

1. Kelompokkan parameter berdasarkan nama paket seperti
   `yiisoft/cache-file`.
2. In case parameters are for the application itself, as in `application`,
   skip package prefix.
3. Jika ada banyak layanan dalam paket, seperti `file-target` dan
   `file-rotator` dalam paket `yiisoft/log-target-file`, kelompokkan
   parameter berdasarkan nama layanan.
4. Gunakan `enabled` sebagai nama parameter agar Anda dapat menonaktifkan
   atau mengaktifkan layanan, mis. `yiisoft/yii-debug`.

### Konfigurasi paket

Config plugin does not copy vendor package configs automatically. It loads
them directly from `vendor/` as the vendor layer and combines them with the
root package and environment-specific layers.

If you want to customize a package config file in your application, use the
`yii-config-copy` Composer command to copy it into your `config/` directory,
usually under `config/packages/`. Once copied, the file becomes part of the
root package layer, so it can override vendor defaults.

The merge plan stored in `.merge-plan.php` defines how config groups are
assembled. If you add new config files or directories and update the root
package configuration, rebuild the merge plan with `composer
yii-config-rebuild`.

---

## File: src/id/guide/concept/di-container.md

# Dependency injection and container

## Dependency injection

There are two ways of re-using things in OOP: inheritance and composition.

Inheritance is simple:

```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;
        }
        // ...        
    }
}
```

The issue here is that these two are becoming unnecessarily coupled or
inter-dependent, making them more fragile.

Another way to handle this is composition:

```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;
        }
        // ...        
    }
}
```

We've avoided unnecessary inheritance and used `CacheInterface` in the
`CacheWidget` to reduce coupling.  You can replace cache implementation
without changing `CachedWidget` so it's becoming more stable. The less edits
are made to the code, the less chance of breaking it.

The `CacheInterface` here is a dependency: a contract our object needs to
function. In other words, our object depends on the contract.

The process of putting an instance of a contract into an object
(`CachedWidget`) is called dependency injection.  There are many ways to
perform it:

- Constructor injection. Best for mandatory dependencies.
- Method injection. Best for optional dependencies.
- Property injection. Better to be avoided in PHP except maybe data transfer
  objects.

### Why use private properties

In the composition example above, note that the `$cache` property is
declared as `private`.

This approach embraces composition by ensuring objects have well-defined
interfaces for interaction rather than direct property access, making the
code more maintainable and less prone to certain types of mistakes.

This design choice provides several benefits:

- **Encapsulation**: Private properties with getters/setters allow you to
  control access and make future changes without breaking existing code.
- **Data integrity**: Setters can validate, normalize, or format values
  before storing them, ensuring properties contain valid data.
- **Immutability**: Private properties enable immutable object patterns
  where setter `with*()` methods return new instances rather than modifying
  the current one.
- **Flexibility**: You can create read-only or write-only properties or add
  additional logic to property access later.


## DI container

Injecting basic dependencies is straightforward. You're choosing a place
where you don't care about dependencies, which is usually an action handler,
which you aren't going to unit-test ever, create instances of dependencies
needed and pass these to dependent classes.

It works well when there are few dependencies overall and when there are no
nested dependencies. When there are many and each dependency has
dependencies itself, instantiating the whole hierarchy becomes a tedious
process, which requires lots of code and may lead to hardly debuggable
mistakes.

Additionally, lots of dependencies, such as certain third-party API
wrappers, are the same for any class using it.  So it makes sense to:

- Define how to instantiate such common dependencies.
- Instantiate them when required and only once per request.

That's what dependency containers are for.

A dependency injection (DI) container is an object that knows how to
instantiate and configure objects and all objects they depend on.

Yii provides the DI container feature through the
[yiisoft/di](https://github.com/yiisoft/di) package and
[yiisoft/injector](https://github.com/yiisoft/injector) package.

> [!NOTE]
> The container contains only shared instances. If you need a factory, use the dedicated
> [yiisoft/factory](https://github.com/yiisoft/factory) package.

> [!TIP]
> [Martin Fowler's article](https://martinfowler.com/articles/injection.html) has well
> explained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii.

### Configuring container

Because to create a new object you need its dependencies, you should
register them as early as possible.  You can do it in the application
configuration, `config/web.php`. For the following service:

```php
final class MyService implements MyServiceInterface
{
    public function __construct(int $amount)
    {
    }

    public function setDiscount(int $discount): void
    {
    
    }
}
```

configuration could be:

```php
return [
    MyServiceInterface::class => [
        'class' => MyService::class,
        '__construct()' => [42],
        'setDiscount()' => [10],
    ],
];
```

That's equal to the following:

```php
$myService = new MyService(42);
$myService->setDiscount(10);
```

You can provide arguments with names as well:

```php
return [
    MyServiceInterface::class => [
        'class' => MyService::class,
        '__construct()' => ['amount' => 42],
        'setDiscount()' => ['discount' => 10],
    ],
];
```

That's basically it. You define a map of interfaces to classes and define
how to configure them. When an interface is requested in constructor or
elsewhere, container creates an instance of a class and configures it as per
the configuration:

```php
final class MyAction
{
    public function __construct(
        private readonly MyServiceInterface $myService
    ) {
    }
    
    public function __invoke() 
    {
        $this->myService->doSomething();
    }
}
```

There are extra methods of declaring dependency configuration.

For simplest cases where there are no custom values needed and all the
constructor dependencies could be obtained from a container, you can use a
class name as a value.

```php
interface EngineInterface
{
    
}

final class EngineMarkOne implements EngineInterface
{
    public function __construct(CacheInterface $cache) {
    }   
}
```

In the above example, if we already have cache defined in the container,
nothing besides the class name is needed:

```php
return [
    // declare a class for an interface, resolve dependencies automatically
    EngineInterface::class => EngineMarkOne::class,
];
```

If you have a dependency that has public properties, you can configure it as
well.


```php
final class NameProvider
{
    public string $name;
}
```

Here's how to do it for the example above:

```php
NameProvider::class => [
    'class' => NameProvider::class, 
    '$name' => 'Alex',
],
```

In this example, you may notice `NameProvider` specified twice. The key is
what you may request as dependency and the value is how to create it.

If the configuration is tricky and requires some logic, a closure can be
used:

```php
MyServiceInterface::class => static function(ContainerInterface $container) {
    return new MyService($container->get('db'));
},
```

Additionally, to `ContainerInterface`, you can request any registered
service directly as a closure parameter.  The injector will automatically
resolve and inject these:

```php
MyServiceInterface::class => static function(ConnectionInterface $db) {
    return new MyService($db);
},
```

It's possible to use a static method call:

```php
MyServiceInterface::class => [MyFactory::class, 'create'],
```

Or an instance of an object:

```php
MyServiceInterface::class => new MyService(),
```

### Injecting dependencies properly

Directly referencing a container in a class is a bad idea since the code
becomes non-generic, coupled to the container interface and, what's worse,
dependencies are becoming hidden.  Because of that, Yii inverts the control
by automatically injecting objects from a container in some constructors and
methods based on method argument types.

This is primarily done in constructor and handing method of action handlers:

```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,
        ]);
    }
}
```

Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that
instantiates and calls action handler, it checks the constructor and method
argument types, gets dependencies of these types from a container and passes
them as arguments. That's usually called auto-wiring. It happens for
sub-dependencies as well, that's if you don't give dependency explicitly,
the container would check if it has such a dependency first.  It's enough to
declare a dependency you need, and it would be got from a container
automatically.


## Referensi

- [Inversion of Control Containers and the Dependency Injection pattern by
  Martin Fowler](https://martinfowler.com/articles/injection.html)

---

## File: src/id/guide/concept/events.md

# Event

Events allow you to run custom code at certain execution points without
changing the code that emits the event.  You attach a custom code called a
listener to an event. When the event is dispatched, the listener is
executed.

For example, when a user signs up, you need to send a welcome email. You can
do it in `SignupService`.  Later, when you also need to resize the user's
avatar image, the service has to know about that too.

A cleaner flow is to dispatch a `UserSignedUp` event from
`SignupService`. The welcome email sender and the avatar processor listen to
this event. Additional signup-related work can be added by registering
another listener.

Yii uses [yiisoft/yii-event](https://github.com/yiisoft/yii-event) for
application event configuration. It builds on the
[yiisoft/event-dispatcher](https://github.com/yiisoft/event-dispatcher)
package, which provides a [PSR-14](https://www.php-fig.org/psr/psr-14/)
compatible event dispatcher.

## Event classes

An event is an object. It usually contains data that listeners need:

```php
final readonly class UserSignedUp
{
    public function __construct(
        public SignupForm $form,
    ) {}
}
```

## Event listeners

An event listener is a [PHP
callable](https://www.php.net/manual/en/language.types.callable.php) that
receives an event:

```php
static function (UserSignedUp $event): void {
    // Send a welcome email.
}
```

You can also use an invokable class:

```php
final readonly class WelcomeEmailSender
{
    public function __invoke(UserSignedUp $event): void
    {
        // Send a welcome email.
    }
}
```

## Configuring event listeners

In an application, configure listeners in:

- `config/events.php` for all application types.
- `config/events-web.php` for web application events.
- `config/events-console.php` for console application events.

The configuration is an array where keys are event class names and values
are lists of listeners:

```php
<?php

declare(strict_types=1);

use App\Event\UserSignedUp;
use App\EventListener\WelcomeEmailSender;
use App\Service\AvatarProcessor;

return [
    UserSignedUp::class => [
        WelcomeEmailSender::class,
        static fn (UserSignedUp $event, AvatarProcessor $avatarProcessor) => $avatarProcessor->resize($event->form),
    ],
];
```

The listener may be a closure, a callable array, an invokable object, an
invokable class name, or a DI alias.  For closure and callable array
listeners, parameters after the first event parameter are resolved from the
DI container.  Invokable class names are instantiated by the container.

Dependencies are resolved when the event is dispatched.

## Dispatching events

To dispatch an event, use `Psr\EventDispatcher\EventDispatcherInterface`:

```php
use Psr\EventDispatcher\EventDispatcherInterface;

final readonly class SignupService
{
    public function __construct(
        private EventDispatcherInterface $eventDispatcher,
    ) {}

    public function signup(SignupForm $form): void
    {
        // Sign up the user.

        $this->eventDispatcher->dispatch(new UserSignedUp($form));
    }
}
```

The dispatcher asks a listener provider for listeners that match the event
and calls them one by one.

## Event listener order

When several listeners are configured for the same event, they are called in
the order they are listed in the config.  If an event implements
`Psr\EventDispatcher\StoppableEventInterface`, the dispatcher checks
`isPropagationStopped()` before calling the next listener. When it returns
`true`, the dispatcher stops processing the event.

Keep listeners independent where possible. Ordering is useful for technical
needs, but business logic is usually easier to maintain when each listener
can run on its own.

## Event hierarchy

Events don't have names or wildcard matching. Event class names, interfaces,
and inheritance can be used when one listener should handle several related
events:

```php
interface DocumentEvent
{
}

final readonly class BeforeDocumentProcessed implements DocumentEvent
{
}

final readonly class AfterDocumentProcessed implements DocumentEvent
{
}
```

With the interface, you can listen to all document-related events:

```php
use Yiisoft\EventDispatcher\Provider\ListenerCollection;
use Yiisoft\EventDispatcher\Provider\Provider;

$listeners = (new ListenerCollection())
    ->add(static function (DocumentEvent $event): void {
        // Log document events.
    });

$provider = new Provider($listeners);
```

## Manual listener provider setup

In an application, use event configuration. For package-level code or tests,
you can create a listener provider manually:

```php
use Yiisoft\EventDispatcher\Dispatcher\Dispatcher;
use Yiisoft\EventDispatcher\Provider\ListenerCollection;
use Yiisoft\EventDispatcher\Provider\Provider;

$listeners = (new ListenerCollection())
    ->add(static function (UserSignedUp $event): void {
        // Send a welcome email.
    });

$provider = new Provider($listeners);
$dispatcher = new Dispatcher($provider);

$dispatcher->dispatch(new UserSignedUp($form));
```

`ListenerCollection::add()` returns a new collection instance. Assign the result when building a collection step by step:

```php
$listeners = new ListenerCollection();
$listeners = $listeners->add(
    static function (UserSignedUp $event): void {
        // Send a welcome email.
    },
    UserSignedUp::class,
);
```

---

## File: src/id/guide/concept/immutability.md

# Immutability

Immutability means an object's state cannot change after it has been
created.  Instead of modifying an instance, you create a new instance with
the desired changes.  This approach is common for value objects such as
Money, IDs, and DTOs. It helps to avoid accidental side effects: methods
cannot silently change shared state, which makes code easier to reason
about.

## Mutable pitfalls (what we avoid)

```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.
```

## Creating an immutable object in PHP

There is no direct way to modify an instance, but you can use clone to
create a new instance with the desired changes.  That is what `with*`
methods do.

```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
```

- We mark the class `final` to prevent subclass mutations; alternatively,
  design for extension carefully.
- Validate in the constructor and `with*` methods so every instance is
  always valid.

> [!TIP]
> If you define a simple DTO, you can use modern PHP `readonly` and leave properties `public`. The `readonly` keyword
> would ensure that the properties cannot be modified after the object is created.

## Using clone (and why it is inexpensive)

PHP's clone performs a shallow copy of the object. For immutable value
objects that contain only scalars or other immutable objects, shallow
cloning is enough and fast. In modern PHP, cloning small value objects is
inexpensive in both time and memory.

If your object holds mutable sub-objects that must also be copied, implement
`__clone` to deep-clone them:

```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;
    }
}
```

## Usage style

- Build a value object once and pass it around. If you need a change, use a
  `with*` method that returns a new instance.
- Prefer scalar/immutable fields inside immutable objects; if a field can
  mutate, isolate it and deep-clone in `__clone` when needed.

Immutability aligns well with Yii's preference for predictable,
side-effect-free code and makes services, caching, and configuration more
robust.

---

## File: src/id/guide/caching/data.md

# 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.

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 a 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://www.php.net/manual/en/book.apcu.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) — uses a PHP
  [memcached](https://secure.php.net/manual/en/book.memcached.php)
  extension. You can consider this option as the fastest one when dealing
  with cache in a distributed application
  (e.g., with several servers, load balancers, etc.)
- [Wincache](https://github.com/yiisoft/cache-wincache) — uses PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension)
  ([see also](https://secure.php.net/manual/en/book.wincache.php)) extension.

[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.

You may set the default TTL for the 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.

The `\Yiisoft\Cache\Cache` uses a built-in "Probably early expiration"
algorithm that prevents cache stampede.  This algorithm randomly fakes a
cache miss for one user while others are still served the cached value.  You
can control its behavior with the fifth optional parameter of `getOrSet()`,
which is a float value called `$beta`.  By default, beta is `1.0`, which is
usually enough.  The higher the value, the earlier cache will be re-created.

```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);
```

---

## File: src/id/guide/caching/overview.md

# 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
* Page caching
* HTTP caching

---

## File: src/id/guide/intro/upgrade-from-v1.md

# Upgrading from Version 1.1

> If you haven't used Yii 1.1, you can skip this section and get directly to
> "[getting started](../start/prerequisites.md)" section.

Yii3 is not an incremental upgrade from Yii 1.1. Yii 2.0 was a full rewrite,
and Yii3 changes the architecture again: Composer packages, namespaces, PHP
8, PSR interfaces, dependency injection, explicit routes, middleware, and
separate packages for features that were built into the framework in Yii
1.1.

Treat this as building a new Yii3 application with the benefit of Yii 1.1
experience. Reuse the domain knowledge, database schema, user workflows, and
selected code after refactoring. Do not try to reproduce the Yii 1.1
application structure in Yii3. A direct search-and-replace conversion is not
realistic.

For additional background, read the Yii 2.0 guide section [Upgrading from
Version
1.1](https://www.yiiframework.com/doc/guide/2.0/en/intro-upgrade-from-v1).
It explains the first major break from Yii 1.1: Composer, namespaces, object
configuration, view changes, controller return values, widgets, assets,
helpers, forms, query builder, Active Record, and URL management. Yii3
builds on many of those changes and makes dependencies more explicit.

## Recommended approach

Choose one of these paths:

- Build a new Yii3 application directly when the Yii 1.1 application is
  small or you are already rewriting request flows.
- Move Yii 1.1 concepts to Yii 2.0 first when the team needs a smaller
  conceptual step.
- Keep Yii 1.1 running and rebuild critical flows in Yii3 one by one.

For most long-lived applications, the third option is the least
risky. Create a Yii3 application, connect it to the same database, and build
vertical slices: route, action, input validation, persistence, view, assets,
and tests.  Each slice should be idiomatic Yii3, even when it is based on
behavior from the Yii 1.1 application.

## Prepare the Yii 1.1 application

Useful preparation can happen before the Yii3 application is ready. These
refactorings make old behavior easier to understand and reduce the amount of
Yii 1.1 framework state that crosses into the new code:

- Remove direct `Yii::app()` access from reusable code where practical. If a
  full refactoring is too large, keep `Yii::app()` calls near controllers,
  widgets, and commands, then pass values and services to lower-level code
  explicitly.
- Introduce repositories or query services for database reads currently
  spread across controllers, views, `CActiveRecord` methods, and
  widgets. Yii3 can use Active Record, but it does not require it for every
  persistence task.
- Separate business rules from framework infrastructure. Code that does not
  extend `CComponent`, read global application state, or render views is
  easier to reuse as a specification for Yii3 code.
- Move reusable behavior out of large controllers and Active Record classes
  into Yii 1.1 application components or plain services. Yii3 services are
  configured and injected differently, but the responsibility boundary is
  similar.
- Add tests or characterization checks around routes and workflows you plan
  to rebuild first. They become the executable specification for the Yii3
  slice.

For example, instead of reading post archive data directly from several
controllers or widgets, create a small service that describes the
application need:

```php
class PostRepository extends CComponent
{
    public function getArchive()
    {
        // ...
    }

    public function getTopForFrontPage($limit = 10)
    {
        // ...
    }
}
```

The Yii3 implementation may use `yiisoft/active-record`, `yiisoft/db`, or
another persistence layer. The important point is that controllers and views
no longer need to know that detail.

## Things to learn before starting

Some Yii3 application-template choices are likely to be new in a Yii 1.1
codebase:

- [Composer](https://getcomposer.org/). Yii3 applications and packages are
  installed through Composer and loaded with PSR-4 autoloading.
- [Namespaces](https://www.php.net/manual/en/language.namespaces.php). Yii3
  classes are namespaced instead of using Yii 1.1-style class prefixes and
  path aliases for class loading.
- [Docker](https://www.docker.com/get-started/). The default Yii3
  application template includes Docker configuration.  Using it gives each
  application its own repeatable environment and reduces differences between
  local development and production.
- [Environment
  variables](https://en.wikipedia.org/wiki/Environment_variable). Yii3
  templates commonly use them for environment-specific settings and secrets,
  especially in Docker-based deployments. This follows the same idea as
  [12-factor app configuration](https://12factor.net/config).
- [Actions](../structure/action.md). Yii3 does not require controller
  inheritance. A route can point to any callable, and the default template
  uses invokable action classes.
- [Application structure](../structure/overview.md). Yii3's default
  structure is different from Yii 1.1. Start with the Yii3 template and
  change it only when the application needs it.

## PHP, Composer, and namespaces

Yii 1.1 applications commonly rely on PHP 5-era code, class prefixes such as
`CController`, and autoloading by class name. Yii3 applications require
modern PHP and Composer autoloading.

Yii 1.1:

```php
class SiteController extends CController
{
}
```

Yii3:

```php
namespace App\Web\HomePage;

final readonly class Action
{
}
```

Configure your application namespace with PSR-4:

```json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
```

Run:

```shell
composer dump-autoload
```

When reusing a class, give it a namespace, add strict types, replace Yii 1.1
base classes, and inject dependencies instead of reading them from global
application state. If these changes make the class awkward, use the old
class as a specification and write a new Yii3 class instead.

## Project structure

A Yii 1.1 application often uses:

```text
protected/
  commands/
  components/
  config/
  controllers/
  extensions/
  messages/
  migrations/
  models/
  views/
themes/
webroot/
```

The current Yii3 application template uses:

```text
assets/
config/
public/
runtime/
src/
tests/
```

Typical mappings to consider are:

| Yii 1.1 | Yii3 |
|---------|------|
| `protected/controllers` | `src/Web/*/Action.php` or controller-like services |
| `protected/models` | `src/Domain`, `src/Shared`, `src/Db`, or form models by responsibility |
| `protected/views` | templates near the feature, for example `src/Web/HomePage/template.php` |
| `protected/views/layouts` | layout templates and assets under `src/Web/Shared/Layout` |
| `protected/components` | services configured in the dependency injection container |
| `protected/extensions` | Composer packages or local packages |
| `protected/commands` | `src/Console/*Command.php` |
| `protected/messages` | translator message files |
| `protected/config` | `config/common`, `config/web`, `config/console`, `config/environments` |
| `webroot` | `public` |

Do not recreate `protected` in Yii3. Put PHP source under `src`, public
files under `public`, generated/runtime files under `runtime`, and
configuration under `config`.

## Configuration and application components

Yii 1.1 configuration is usually one large array:

```php
return [
    'basePath' => dirname(__DIR__),
    'components' => [
        'db' => [
            'connectionString' => 'mysql:host=localhost;dbname=app',
            'username' => 'root',
            'password' => '',
        ],
        'urlManager' => [
            'urlFormat' => 'path',
            'rules' => [
                'post/<id:\d+>' => 'post/view',
            ],
        ],
    ],
];
```

In Yii3, configuration is split by purpose and services are wired through
the container:

- aliases in `config/common/aliases.php`;
- parameters in `config/common/params.php` and environment files;
- shared service definitions in `config/common/di/*.php`;
- web-only service definitions in `config/web/di/*.php`;
- routes in `config/common/routes.php`;
- console commands in `config/console/*.php`.

The default template's console DI group reuses shared definitions from
`config/common/di/*.php`. If you need console-only container definitions,
add files for them to the `di-console` group in `config/configuration.php`
and rebuild the merge plan.

Replace `Yii::app()->componentName` with constructor-injected services:

```php
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Router\UrlGeneratorInterface;

final readonly class PostRepository
{
    public function __construct(
        private ConnectionInterface $db,
        private UrlGeneratorInterface $urlGenerator,
    ) {
    }
}
```

See [Configuration](../concept/configuration.md),
[Aliases](../concept/aliases.md), and [Dependency injection
container](../concept/di-container.md).

## Entry script

Yii 1.1 entry scripts commonly load `yii.php` and create a web application:

```php
require_once __DIR__ . '/../framework/yii.php';

Yii::createWebApplication($config)->run();
```

Yii3 entry scripts load Composer and bootstrap the configured
application. Start with the `public/index.php` file from `yiisoft/app` and
adjust configuration instead of building the application manually in the
entry script.

See [Entry scripts](../structure/entry-script.md) and [Application
workflow](../start/workflow.md).

## Controllers, actions, and routes

Yii 1.1 controllers extend `CController`, and actions are methods named
`actionName`:

```php
class PostController extends CController
{
    public function actionView($id)
    {
        $post = Post::model()->findByPk($id);

        if ($post === null) {
            throw new CHttpException(404);
        }

        $this->render('view', ['post' => $post]);
    }
}
```

Yii3 does not require controller inheritance. The current template uses
invokable action classes:

```php
namespace App\Web\Post\View;

use App\Post\PostRepository;
use Psr\Http\Message\ResponseInterface;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Yii\View\Renderer\WebViewRenderer;

final readonly class Action
{
    public function __construct(
        private CurrentRoute $currentRoute,
        private PostRepository $posts,
        private WebViewRenderer $viewRenderer,
    ) {
    }

    public function __invoke(): ResponseInterface
    {
        $post = $this->posts->findById((int) $this->currentRoute->getArgument('id'));

        return $this->viewRenderer->render(__DIR__ . '/template', [
            'post' => $post,
        ]);
    }
}
```

The route is explicit:

```php
use App\Web\Post\View;
use Yiisoft\Router\Route;

return [
    Route::get('/post/{id:\d+}')
        ->action(View\Action::class)
        ->name('post/view'),
];
```

Define routes before action bodies. It gives you a checklist of request
flows you decided to keep in the Yii3 application.

See [Actions](../structure/action.md),
[Middleware](../structure/middleware.md), and [Routing and URL
generation](../runtime/routing.md).

## Requests, responses, redirects, sessions, and cookies

Yii 1.1 code often reads and writes through `Yii::app()`:

```php
$id = Yii::app()->request->getParam('id');
Yii::app()->user->setFlash('success', 'Saved.');
$this->redirect(['post/view', 'id' => $id]);
```

Yii3 uses PSR-7 request and response objects and injectable
services. Request input is available from `ServerRequestInterface`;
redirects, sessions, cookies, and responses are explicit dependencies.

See [Request](../runtime/request.md), [Response](../runtime/response.md),
[Sessions](../runtime/sessions.md), and [Cookies](../runtime/cookies.md).

## Views and layouts

Yii 1.1 views use `$this` as the controller or widget context. Layouts and
partials are rendered through controller methods:

```php
<?php
/* @var $this PostController */
/* @var $post Post */
?>

<h1><?php echo CHtml::encode($post->title); ?></h1>
```

In Yii3 templates, `$this` is a `Yiisoft\View\WebView` instance:

```php
use Yiisoft\Html\Html;
use Yiisoft\View\WebView;

/**
 * @var WebView $this
 * @var Post $post
 */

$this->setTitle($post->getTitle());
?>

<h1><?= Html::encode($post->getTitle()) ?></h1>
```

Move controller-specific view logic to the action or to a presenter. Pass
only the data a template needs.

See [View](../views/view.md), [View
injections](../views/view-injections.md), and [Template
engines](../views/template-engines.md).

## Assets and client scripts

Yii 1.1 applications often use `CClientScript`, themes, and
extension-specific asset publishing:

```php
Yii::app()->clientScript->registerCssFile('/css/site.css');
Yii::app()->clientScript->registerScriptFile('/js/site.js');
```

Yii3 uses asset bundles from `yiisoft/assets`:

```php
namespace App\Web\Shared\Layout\Main;

use Yiisoft\Assets\AssetBundle;

final class MainAsset extends AssetBundle
{
    public ?string $basePath = '@assets/main';
    public ?string $baseUrl = '@assetsUrl/main';
    public ?string $sourcePath = '@assetsSource/main';

    public array $css = ['site.css'];
    public array $js = ['site.js'];
}
```

Register bundles through the asset manager in layouts or views.

See [Assets](../views/asset.md) and [Scripts, styles and
metatags](../views/script-style-meta.md).

## Models, forms, and validation

Yii 1.1 has `CModel`, `CFormModel`, and `CActiveRecord`. It is common for
one model class to contain input data, labels, validation rules, database
persistence, and business logic.

Yii3 works better when these responsibilities are split:

- form data and labels: `yiisoft/form-model`;
- validation rules: `yiisoft/validator`;
- form rendering: `yiisoft/form`;
- persistence: repositories, `yiisoft/db`, or `yiisoft/active-record`;
- business workflows: application services.

Yii 1.1 form model:

```php
class ContactForm extends CFormModel
{
    public $name;
    public $email;

    public function rules()
    {
        return [
            ['name, email', 'required'],
            ['email', 'email'],
        ];
    }
}
```

Yii3 form model:

```php
use Yiisoft\FormModel\FormModel;
use Yiisoft\Validator\Rule\Email;
use Yiisoft\Validator\Rule\Required;

final class ContactForm extends FormModel
{
    #[Required]
    public ?string $name = null;

    #[Required]
    #[Email]
    public ?string $email = null;
}
```

See [Working with forms](../start/forms.md), [Validating
input](https://github.com/yiisoft/validator/blob/master/docs/guide/en/README.md),
[yiisoft/form](https://github.com/yiisoft/form), and
[yiisoft/form-model](https://github.com/yiisoft/form-model).

## Active Record

Yii 1.1 Active Record uses `CActiveRecord::model()` and dynamic attributes:

```php
class Post extends CActiveRecord
{
    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return '{{post}}';
    }

    public function rules()
    {
        return [
            ['title, body', 'required'],
            ['title', 'length', 'max' => 255],
        ];
    }
}
```

The incremental Yii3 option is
[yiisoft/active-record](https://github.com/yiisoft/active-record):

```php
use Yiisoft\ActiveRecord\ActiveRecord;

final class Post extends ActiveRecord
{
    protected int $id;
    protected string $title;
    protected string $body;

    public function tableName(): string
    {
        return '{{%post}}';
    }

    public function getTitle(): ?string
    {
        return $this->title ?? null;
    }

    public function setTitle(string $title): void
    {
        $this->title = $title;
    }
}
```

Move Yii 1.1 `rules()` to a form model, validator rules, or a service. Keep
Active Record focused on persistence: table mapping, properties, relations,
and database operations.

For more complex applications, consider replacing Active Record-heavy code
with repositories and domain objects. That is more work initially, but it
avoids carrying Yii 1.1 form and persistence coupling into the new
application.

See [Working with databases](../start/databases.md),
[Migrations](../databases/db-migrations.md), and
[yiisoft/active-record](https://github.com/yiisoft/active-record).

## Database queries and migrations

Yii 1.1 code may use `CDbCriteria`, `CDbCommand`, `CActiveDataProvider`, and
`CGridView` data providers. In Yii3, use `yiisoft/db` for database access
and query building, and `yiisoft/data` / `yiisoft/yii-dataview` for data
readers and widgets.

Migrations are not compatible across Yii 1.1 and Yii3. For an existing
application, create a baseline from the current schema and use Yii3
migrations for new changes.

See [Working with databases](../start/databases.md) and
[Migrations](../databases/db-migrations.md).

## GridView and data widgets

Yii 1.1 `CGridView` is often configured with `CActiveDataProvider`:

```php
$this->widget('zii.widgets.grid.CGridView', [
    'dataProvider' => $dataProvider,
    'columns' => [
        'id',
        'title',
        [
            'class' => 'CButtonColumn',
        ],
    ],
]);
```

Yii3 uses [yiisoft/yii-dataview](https://github.com/yiisoft/yii-dataview):

```php
use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Yii\DataView\Column\DataColumn;
use Yiisoft\Yii\DataView\GridView\Column\ActionColumn;
use Yiisoft\Yii\DataView\GridView\GridView;

/**
 * @var ReadableDataInterface $dataReader
 */

echo GridView::widget()
    ->dataReader($dataReader)
    ->columns(
        new DataColumn('id'),
        new DataColumn('title'),
        new ActionColumn(),
    );
```

Replace the old data provider with a query service that returns a data
reader. Model filters as validator rules and route or query parameters.

See [Using htmx for partial
reloads](../../cookbook/using-htmx-for-partial-reloads.md) and [yii-dataview
GridView
docs](https://github.com/yiisoft/yii-dataview/blob/master/docs/en/guide/gridview.md).

## URL management

Yii 1.1:

```php
echo CHtml::link('View', ['post/view', 'id' => $post->id]);
$url = Yii::app()->createUrl('post/view', ['id' => $post->id]);
```

Yii3:

```php
use Yiisoft\Html\Html;
use Yiisoft\Router\UrlGeneratorInterface;

$url = $urlGenerator->generate('post/view', ['id' => $post->getId()]);

echo Html::a('View', $url)->render();
```

Prefer named routes. They make views and GridView action columns easier to
design and maintain.

See [Routing and URL generation](../runtime/routing.md).

## Filters, access control, and authentication

Yii 1.1 controller filters are controller methods or filter classes:

```php
public function filters()
{
    return [
        'accessControl',
    ];
}
```

Yii3 uses middleware and focused packages. Move authentication,
authorization, CSRF, locale detection, and request body parsing out of
controllers and into middleware or services.

See [Middleware](../structure/middleware.md),
[Authentication](../security/authentication.md), and
[Authorization](../security/authorization.md).

## Widgets, helpers, and extensions

Yii 1.1 widgets and helpers usually depend on Yii-specific base classes such
as `CWidget`, `CHtml`, and extension autoloading. In Yii3:

- replace helper calls with `yiisoft/html`, `yiisoft/arrays`,
  `yiisoft/strings`, or application helpers;
- replace extensions with Composer packages;
- rebuild custom widgets with `yiisoft/widget` or plain services/templates;
- replace jQuery UI and Bootstrap widgets with current frontend packages or
  Yii3 widget packages where available.

See [Widgets](../views/widget.md).

## Console applications

Yii 1.1 console commands extend `CConsoleCommand`:

```php
class ReportCommand extends CConsoleCommand
{
    public function actionSend()
    {
    }
}
```

Yii3 console commands are Symfony Console commands configured as services:

```php
namespace App\Console;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'report/send')]
final class SendReportCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        return Command::SUCCESS;
    }
}
```

See [Console applications](../tutorial/console-applications.md).

## Internationalization

Yii 1.1 message files under `protected/messages` can usually be kept
conceptually, but configuration changes. Yii3 uses translator packages and
explicit message sources. Move message files into the structure expected by
the selected translator message package and inject translation services
where needed.

See [Internationalization](../tutorial/i18n.md).

## What to refactor before reusing

These Yii 1.1 refactorings make the old behavior easier to understand and
reuse:

- isolate database queries from controllers;
- remove direct `Yii::app()` access from domain code;
- move validation out of Active Record when it describes request input
  rather than database invariants;
- replace large controller actions with services;
- document all routes and URL rules;
- add tests around the flows you plan to rebuild first;
- upgrade PHP syntax gradually if the old runtime allows it.

The goal is not to make Yii 1.1 look like Yii3. The goal is to reduce hidden
coupling before the code crosses the framework boundary.

---

## File: src/id/guide/intro/upgrade-from-v2.md

# Upgrading from Version 2.0

> If you haven't used Yii 2.0, you can skip this section and get directly to "[getting started](../start/prerequisites.md)"
> section.

Yii3 keeps many Yii ideas, but it is not a drop-in replacement for Yii
2.0. It uses PHP 8 features, PSR interfaces, dependency injection, explicit
package composition, and a different application template. Use your Yii 2.0
experience to design a new Yii3 application deliberately instead of trying
to reproduce the old application structure.

For source applications, compare against:

- [yiisoft/yii2-app-basic](https://github.com/yiisoft/yii2-app-basic)
- [yiisoft/yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced)

For the target structure, compare against:

- [yiisoft/app](https://github.com/yiisoft/app)
- [Yii3 guide](../index.md)

Before starting, [check maintenance policy and end-of-life dates for Yii
2.0](https://www.yiiframework.com/release-cycle).  Keeping a mature Yii 2.0
application on Yii 2.0 can be a valid choice. A Yii3 project is most useful
when you want the new package ecosystem, stricter typing, PSR middleware,
and a more explicit architecture.

## Recommended approach

Think of the work as building a Yii3 application that is informed by the old
one. The old project tells you which routes, forms, database tables,
business rules, and user workflows matter. The Yii3 project should decide
again where these responsibilities belong.

Build it in vertical slices:

1. Create a new Yii3 application with `yiisoft/app`.
2. Configure Composer autoloading and add only the code needed for the slice
   you are building.
3. Define configuration and routes before writing actions.
4. Build one request flow at a time: route, action, input model, service,
   persistence, view, assets, tests.
5. Keep the Yii 2.0 application running until each flow is verified in Yii3.

Avoid moving the whole directory tree first. Yii3's defaults are
intentionally different, and a mechanical copy tends to preserve Yii 2.0
service-locator assumptions. Prefer small, reviewed decisions over carrying
old structure forward because it is familiar.

## Prepare the Yii 2.0 application

Useful preparation can happen before the Yii3 application is ready. These
refactorings improve the Yii 2.0 project itself and make the later rewrite
less dependent on hidden framework state:

- Replace `Yii::$app` access in reusable code with constructor arguments
  where practical. If a full dependency injection refactoring is too large,
  keep `Yii::$app` calls near controllers and widgets, then pass values and
  services to lower-level code explicitly.
- Introduce repositories or query services for database reads that are
  currently scattered across controllers, views, widgets, and Active Record
  methods. Yii3 does not require Active Record for persistence, so a
  repository boundary gives you a natural place to change the implementation
  later.
- Separate business rules from framework infrastructure. Domain code that
  does not extend Yii classes, read global application state, or render
  views is much easier to reuse or rewrite.
- Move reusable behavior into Yii 2.0 components or services instead of
  keeping it inside large controllers and Active Record classes. Yii3
  services are configured and injected differently, but the responsibility
  boundary is similar.
- Add tests around routes and workflows you plan to rebuild first. They
  become the executable specification for the Yii3 slice.

For example, instead of reading post archive data directly from several
controllers or widgets, create a small service that describes the
application need:

```php
final class PostRepository
{
    public function getArchive(): array
    {
        // ...
    }

    public function getTopForFrontPage(int $limit = 10): array
    {
        // ...
    }
}
```

The Yii3 implementation may still use Active Record, or it may use
`yiisoft/db` queries. The important point is that controllers and views no
longer need to know that detail.

## Things to learn before starting

Some Yii3 application-template choices are worth learning before you start
moving features:

- [Docker](https://www.docker.com/get-started/). The default Yii3
  application template includes Docker configuration.  Using it gives each
  application its own repeatable environment and reduces differences between
  local development and production.
- [Environment
  variables](https://en.wikipedia.org/wiki/Environment_variable). Yii3
  templates commonly use them for environment-specific settings and secrets,
  especially in Docker-based deployments. This follows the same idea as
  [12-factor app configuration](https://12factor.net/config).
- [Actions](../structure/action.md). Yii3 does not require controller
  inheritance. A route can point to any callable, and the default template
  uses invokable action classes.
- [Application structure](../structure/overview.md). Yii3's default
  structure is different from Yii 2.0. The structure is flexible, but start
  with the Yii3 template and change it only when the application needs it.

## PHP requirements

Yii3 application templates require PHP 8.2 or above. As a result,
application code can use language features that were not common in Yii 2.0
applications:

- [Type
  declarations](https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration)
- [Return type
  declarations](https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration)
- [Class constant
  visibility](https://www.php.net/manual/en/language.oop5.constants.php)
- [Named
  arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments)
- [Anonymous
  classes](https://www.php.net/manual/en/language.oop5.anonymous.php)
- [::class](https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class)
- [Generators](https://www.php.net/manual/en/language.generators.php)
- [Variadic
  functions](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list)
- [Readonly
  properties](https://www.php.net/manual/en/language.oop5.properties.php#language.oop5.properties.readonly-properties)
- [Readonly
  classes](https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.readonly)
- [Constructor property
  promotion](https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion)
- [Attributes](https://www.php.net/manual/en/language.attributes.php)

Run static analysis and tests on Yii 2.0 before reusing code in Yii3. Code
with explicit types, small services, and few implicit `Yii::$app`
dependencies is easier to understand and easier to place in the new
application.

## Project structure

Yii 2.0 applications commonly keep framework roles in top-level directories:

```text
assets/
commands/
config/
controllers/
models/
runtime/
views/
web/
widgets/
```

The Yii 2.0 advanced template splits the same roles by application:

```text
backend/
common/
console/
frontend/
```

The current Yii3 application template is organized around source code,
configuration, public files, and runtime files:

```text
assets/
config/
public/
runtime/
src/
tests/
```

Typical mappings to consider are:

| Yii 2.0 basic | Yii 2.0 advanced | Yii3 |
|---------------|------------------|------|
| `controllers/` | `frontend/controllers/`, `backend/controllers/` | `src/Web/*/Action.php` or controller-like classes |
| `models/` | `common/models/`, app-specific models | `src/Domain`, `src/Shared`, `src/Web/*`, or `src/Db` by responsibility |
| `views/` | `frontend/views/`, `backend/views/` | templates near the feature, for example `src/Web/HomePage/template.php` |
| `views/layouts/` | app layout directories | layout classes/templates, for example `src/Web/Shared/Layout/Main` |
| `assets/` | app asset bundles | `assets/` for source files and `src/Web/.../*Asset.php` for bundles |
| `web/` | `frontend/web/`, `backend/web/` | `public/` |
| `commands/` | `console/controllers/` | `src/Console/*Command.php` |
| `config/*.php` | app and common config files | `config/common`, `config/web`, `config/console`, `config/environments` |

There is no single required layout. The important change is that Yii3
applications should group code by responsibility and make dependencies
explicit. If you are coming from the advanced template, treat `frontend` and
`backend` as separate web contexts. Shared domain code can live in
`src/Shared` or in application-specific packages.

## Composer and autoloading

Yii 2.0 applications usually require `vendor/autoload.php` and
`vendor/yiisoft/yii2/Yii.php` in `web/index.php`.  Yii3 applications require
only Composer's autoloader and application bootstrap code.

Configure your application namespace with PSR-4:

```json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
```

Then rebuild Composer autoload files:

```shell
composer dump-autoload
```

Install Yii3 packages intentionally. Yii3 is split into focused packages, so
features such as database access, forms, validation, assets, data widgets,
authentication, and caching are separate Composer dependencies.

## Configuration and services

In Yii 2.0, many services are configured as application components and
accessed through the service locator:

```php
$cache = Yii::$app->cache;
$db = Yii::$app->db;
$url = Yii::$app->urlManager->createUrl(['post/view', 'id' => $id]);
```

In Yii3, services are configured for the container and injected where they
are needed:

```php
use Psr\SimpleCache\CacheInterface;
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Router\UrlGeneratorInterface;

final readonly class PostService
{
    public function __construct(
        private ConnectionInterface $db,
        private CacheInterface $cache,
        private UrlGeneratorInterface $urlGenerator,
    ) {
    }
}
```

Move configuration in groups:

- aliases to `config/common/aliases.php`;
- route definitions to `config/common/routes.php`;
- shared service definitions to `config/common/di/*.php`;
- web-only service definitions to `config/web/di/*.php`;
- console parameters and commands to `config/console/*.php`;
- shared parameters to `config/common/params.php`;
- environment overrides to `config/environments`.

The default template's `di-console` group reuses shared definitions from
`config/common/di/*.php`. If you need console-only container definitions,
add the corresponding files to the `di-console` group in
`config/configuration.php` and rebuild the merge plan.

See [Configuration](../concept/configuration.md),
[Aliases](../concept/aliases.md), and [Dependency injection
container](../concept/di-container.md).

## Entry script and bootstrap

A Yii 2.0 basic entry script commonly creates the application directly:

```php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';

$config = require __DIR__ . '/../config/web.php';

(new yii\web\Application($config))->run();
```

In Yii3, use the application template entry script as the source of
truth. It loads Composer and bootstraps the configured application
runner. Avoid recreating the old `yii\web\Application` pattern in
`public/index.php`; put configuration in config files and dependencies in
the container.

See [Entry scripts](../structure/entry-script.md) and [Application
workflow](../start/workflow.md).

## Controllers, actions, and routing

Yii 2.0 routes usually point to controller action methods:

```php
namespace app\controllers;

use yii\web\Controller;

final class SiteController extends Controller
{
    public function actionIndex(): string
    {
        return $this->render('index');
    }
}
```

Yii3 does not require controller base classes. The current application
template uses invokable action classes:

```php
namespace App\Web\HomePage;

use Psr\Http\Message\ResponseInterface;
use Yiisoft\Yii\View\Renderer\WebViewRenderer;

final readonly class Action
{
    public function __construct(
        private WebViewRenderer $viewRenderer,
    ) {
    }

    public function __invoke(): ResponseInterface
    {
        return $this->viewRenderer->render(__DIR__ . '/template');
    }
}
```

Routes are explicit:

```php
use App\Web;
use Yiisoft\Router\Group;
use Yiisoft\Router\Route;

return [
    Group::create()
        ->routes(
            Route::get('/')
                ->action(Web\HomePage\Action::class)
                ->name('home'),
        ),
];
```

When redesigning a controller flow, list all Yii 2.0 actions and create
named routes for the flows you still need.  Then implement each flow as an
invokable class or a controller-like service. Constructor injection replaces
calls to `Yii::$app` and controller properties.

See [Actions](../structure/action.md),
[Middleware](../structure/middleware.md), and [Routing and URL
generation](../runtime/routing.md).

## Requests, responses, sessions, and cookies

Yii 2.0 actions often read request data and write response data through
application components:

```php
$id = Yii::$app->request->get('id');
Yii::$app->session->setFlash('success', 'Saved.');

return $this->redirect(['post/view', 'id' => $id]);
```

Yii3 uses PSR-7 request and response objects and injectable services:

```php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Http\Status;
use Yiisoft\Router\UrlGeneratorInterface;

final readonly class SavePostAction
{
    public function __construct(
        private ResponseFactoryInterface $responseFactory,
        private UrlGeneratorInterface $urlGenerator,
    ) {
    }

    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        $id = (int) ($request->getQueryParams()['id'] ?? 0);
        $url = $this->urlGenerator->generate('post/view', ['id' => $id]);

        return $this->responseFactory
            ->createResponse(Status::SEE_OTHER)
            ->withHeader('Location', $url);
    }
}
```

Design request handling early because it affects forms, filters, REST
endpoints, redirects, and tests.

See [Request](../runtime/request.md), [Response](../runtime/response.md),
[Sessions](../runtime/sessions.md), and [Cookies](../runtime/cookies.md).

## Views, layouts, assets, and URLs

In Yii 2.0, controller rendering finds files under `views/{controller}` and
layouts under `views/layouts`:

```php
return $this->render('view', [
    'model' => $model,
]);
```

In Yii3, the template path is explicit in the action:

```php
return $this->viewRenderer->render(__DIR__ . '/view', [
    'post' => $post,
]);
```

A Yii3 template receives a `Yiisoft\View\WebView` instance as `$this`:

```php
use App\Shared\ApplicationParams;
use Yiisoft\Html\Html;
use Yiisoft\View\WebView;

/**
 * @var WebView $this
 * @var ApplicationParams $applicationParams
 * @var string $title
 */

$this->setTitle($title . ' - ' . $applicationParams->name);
?>

<h1><?= Html::encode($title) ?></h1>
```

Asset bundles move from `yii\web\AssetBundle` to
`Yiisoft\Assets\AssetBundle`:

```php
namespace App\Web\Shared\Layout\Main;

use Yiisoft\Assets\AssetBundle;

final class MainAsset extends AssetBundle
{
    public ?string $basePath = '@assets/main';
    public ?string $baseUrl = '@assetsUrl/main';
    public ?string $sourcePath = '@assetsSource/main';

    public array $css = [
        'site.css',
    ];
}
```

Register assets in a layout or view via the asset manager:

```php
$assetManager->register(MainAsset::class);

$this->addCssFiles($assetManager->getCssFiles());
$this->addJsFiles($assetManager->getJsFiles());
```

Replace `yii\helpers\Url` and `Yii::$app->urlManager` usage with named routes and
`Yiisoft\Router\UrlGeneratorInterface`:

```php
$url = $urlGenerator->generate('post/view', ['id' => $post->getId()]);
```

See [View](../views/view.md), [Assets](../views/asset.md), [Scripts, styles
and metatags](../views/script-style-meta.md), and [Template
engines](../views/template-engines.md).

## Forms and validation

Yii 2.0 frequently combines form data, labels, validation, and sometimes
persistence in a `yii\base\Model` or `yii\db\ActiveRecord` class:

```php
final class ContactForm extends \yii\base\Model
{
    public $name;
    public $email;

    public function rules(): array
    {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}
```

Yii3 separates these concerns. Use `yiisoft/form-model` for form data and
metadata, and `yiisoft/validator` for rules:

```php
use Yiisoft\FormModel\FormModel;
use Yiisoft\Validator\Rule\Email;
use Yiisoft\Validator\Rule\Required;

final class ContactForm extends FormModel
{
    #[Required]
    public ?string $name = null;

    #[Required]
    #[Email]
    public ?string $email = null;
}
```

For form rendering, replace `yii\widgets\ActiveForm` with `yiisoft/form`
fields and form model integration. For input hydration, prefer the
form-model and hydrator packages instead of mass-assigning arbitrary request
data to persistence objects.

See [Working with forms](../start/forms.md), [Validating
input](https://github.com/yiisoft/validator/blob/master/docs/guide/en/README.md),
[yiisoft/form](https://github.com/yiisoft/form), and
[yiisoft/form-model](https://github.com/yiisoft/form-model).

## Active Record and persistence

There are two common Yii3 choices for code that used Yii 2.0 Active Record
classes.

The first path is architectural: move business logic to services and domain
objects, then use repositories for persistence. This is recommended when the
Yii 2.0 model contains validation, form scenarios, authorization checks,
mailing, file uploads, and database access in the same class.

The second path is incremental: represent the database model with
[yiisoft/active-record](https://github.com/yiisoft/active-record). This is
closer to Yii 2.0 and can be significantly faster when your Active Record
classes mostly describe tables and relations.

Yii 2.0:

```php
final class Post extends \yii\db\ActiveRecord
{
    public static function tableName(): string
    {
        return '{{%post}}';
    }

    public function rules(): array
    {
        return [
            [['title', 'body'], 'required'],
            ['title', 'string', 'max' => 255],
        ];
    }
}
```

Yii3 Active Record:

```php
use Yiisoft\ActiveRecord\ActiveRecord;

final class Post extends ActiveRecord
{
    protected int $id;
    protected string $title;
    protected string $body;

    public function tableName(): string
    {
        return '{{%post}}';
    }

    public function getId(): ?int
    {
        return $this->id ?? null;
    }

    public function getTitle(): ?string
    {
        return $this->title ?? null;
    }

    public function setTitle(string $title): void
    {
        $this->title = $title;
    }
}
```

Do not move Yii 2.0 `rules()` directly into the Active Record class. In
Yii3, validation belongs to validator rules, form models, command objects,
or services:

```php
use Yiisoft\FormModel\FormModel;
use Yiisoft\Validator\Rule\Length;
use Yiisoft\Validator\Rule\Required;

final class PostInput extends FormModel
{
    #[Required]
    #[Length(max: 255)]
    public ?string $title = null;

    #[Required]
    public ?string $body = null;
}
```

Then copy validated data into the record or pass it to an application
service:

```php
$post = new Post();
$post->setTitle($input->title);
$post->save();
```

When deciding what to reuse, check each Active Record method:

- table mapping and relations can usually move to `yiisoft/active-record`;
- validation should move to `yiisoft/validator`;
- form labels and hints should move to `yiisoft/form-model`;
- search models should become query/data-reader services;
- business workflows should become services;
- events should use PSR-14 events or package-specific events;
- direct `Yii::$app->db` usage should become injected database dependencies.

Yii 2.0 migrations and Yii3 `yiisoft/db-migration` migrations are not
compatible. Start a new migration history or create structure dumps for the
current schema, then use Yii3 migrations for future schema changes.

See [Working with databases](../start/databases.md),
[Migrations](../databases/db-migrations.md), and
[yiisoft/active-record](https://github.com/yiisoft/active-record).

## GridView and data widgets

Yii 2.0 `GridView` is commonly used with `ActiveDataProvider`:

```php
use yii\grid\GridView;

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        'id',
        'title',
        'created_at:datetime',
        ['class' => 'yii\grid\ActionColumn'],
    ],
]);
```

Yii3 uses
[yiisoft/yii-dataview](https://github.com/yiisoft/yii-dataview). It renders
data from a data reader and uses fluent widget configuration:

```php
use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Yii\DataView\Column\DataColumn;
use Yiisoft\Yii\DataView\GridView\Column\ActionColumn;
use Yiisoft\Yii\DataView\GridView\GridView;

/**
 * @var ReadableDataInterface $dataReader
 */

echo GridView::widget()
    ->dataReader($dataReader)
    ->columns(
        new DataColumn('id'),
        new DataColumn('title', header: 'Title'),
        new DataColumn('created_at', header: 'Created'),
        new ActionColumn(),
    );
```

For sorting, pagination, filtering, and action URLs, wire the widget to Yii
Router:

```php
use Yiisoft\Yii\DataView\YiiRouter\ActionColumnUrlConfig;
use Yiisoft\Yii\DataView\YiiRouter\ActionColumnUrlCreator;
use Yiisoft\Yii\DataView\YiiRouter\UrlCreator;
use Yiisoft\Yii\DataView\YiiRouter\UrlParameterProvider;

echo GridView::widget()
    ->dataReader($dataReader)
    ->urlParameterProvider(new UrlParameterProvider($currentRoute))
    ->urlCreator(new UrlCreator($urlGenerator))
    ->columns(
        new DataColumn('id'),
        new DataColumn('title'),
        new ActionColumn(
            urlCreator: new ActionColumnUrlCreator($urlGenerator, $currentRoute),
            urlConfig: new ActionColumnUrlConfig(baseRouteName: 'post'),
        ),
    );
```

A Yii 2.0 search model usually becomes a query service that:

1. reads filter and sort parameters from the request;
2. validates them with `yiisoft/validator`;
3. builds a database query or repository query;
4. exposes a data reader to GridView.

See [Using htmx for partial
reloads](../../cookbook/using-htmx-for-partial-reloads.md) for a complete
Yii3 GridView workflow and [yii-dataview GridView
docs](https://github.com/yiisoft/yii-dataview/blob/master/docs/en/guide/gridview.md).

## Filters, middleware, and access control

Yii 2.0 controller filters and behaviors usually look like this:

```php
public function behaviors(): array
{
    return [
        'access' => [
            'class' => \yii\filters\AccessControl::class,
            'rules' => [
                ['allow' => true, 'roles' => ['@']],
            ],
        ],
    ];
}
```

In Yii3, request processing is middleware-based. Move cross-cutting concerns
such as authentication, authorization, body parsing, locale negotiation,
CSRF checks, and error handling to middleware or dedicated services. Keep
actions focused on the request flow they implement.

See [Middleware](../structure/middleware.md),
[Authentication](../security/authentication.md), and
[Authorization](../security/authorization.md).

## Console commands

Yii 2.0 console commands extend `yii\console\Controller`:

```php
final class ReportController extends \yii\console\Controller
{
    public function actionSend(): int
    {
        // ...
        return self::EXIT_CODE_NORMAL;
    }
}
```

Yii3 console commands are services in `src/Console`:

```php
namespace App\Console;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'report/send')]
final class SendReportCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        // ...
        return Command::SUCCESS;
    }
}
```

Move command dependencies to constructor injection and register commands in
console configuration.

See [Console applications](../tutorial/console-applications.md).

## REST APIs

Yii 2.0 REST controllers, serializers, and response formatters do not move
directly to Yii3. Start from PSR-7 responses and explicit routing. Build one
resource at a time:

1. define routes for collection and item operations;
2. inject request, persistence, serializer, and response factory
   dependencies;
3. validate input with `yiisoft/validator`;
4. return JSON responses through Yii3 response helpers or PSR-7 factories;
5. move authentication and rate limiting to middleware.

See [Response](../runtime/response.md),
[Authentication](../security/authentication.md), and
[Authorization](../security/authorization.md).

## Testing

Do not postpone tests until the whole Yii3 application is done. For each new
request flow, add:

- unit tests for services and validators;
- functional tests for actions and middleware;
- end-to-end tests for critical browser flows;
- static analysis for stricter type checks.

Yii3 code is more explicit, so tests should assert services directly instead
of constructing large application component arrays.

See [Testing overview](../testing/overview.md), [Unit
tests](../testing/unit.md), [Functional tests](../testing/functional.md),
and [End-to-end tests](../testing/end-to-end.md).

## Refactoring Yii 2.0 code before reusing it

These changes are useful even if the old application stays on Yii 2.0 for a
while:

- Replace `Yii::$app` access in domain code with constructor arguments.
- Move queries out of controllers and views into repositories or query
  services.
- Move business workflows out of Active Record classes.
- Separate form models from persistence models.
- Add return types and parameter types.
- Replace route arrays scattered in views with named route constants or
  helper methods.
- Add tests around each flow before rebuilding it in Yii3.

The less framework state your Yii 2.0 code assumes, the easier it is to
understand what should be reused and what should be redesigned for Yii3.

---

## File: src/id/guide/intro/what-is-yii.md

# What is Yii

Yii is a high-performance, package-based PHP framework for developing modern
applications.  The name Yii (pronounced `Yee` or `[ji:]`) means "simple and
evolutionary" in Chinese.  You can also think about it as an acronym for
**Yes It Is**!

## What's Yii best for

Yii is a generic Web programming framework.  You can use it for developing
all kinds of Web applications using PHP.  Because of its architecture and
sophisticated caching support, it's especially suitable for developing
large-scale applications such as portals, content management systems,
e-commerce, REST APIs, etc.

## How does Yii compare with other frameworks?

If you're already familiar with another framework, you may appreciate
knowing how Yii compares:

- Yii takes the [philosophy of being practical and
  helpful](../../internals/001-yii-values.md) achieving:
  - Performance in both development and execution.
  - Convenient customizable defaults.
  - Practice-orientation.
  - Simplicity.
  - Explicitness.
  - Consistency.
  
  Yii will never try to over-design things mainly to follow some design patterns.
- Yii extensively uses PSR interfaces with the ability to reuse what PHP community created and even
  replace core implementations if needed.
- Yii is both a set of libraries and a full-stack framework providing many proven and ready-to-use features:
  caching, logging, template engine, data abstraction, development tools, code generation, and more.
- Yii is extensible. You can customize or replace every piece of the core's code. You can also
  take advantage of Yii's solid architecture to use or develop redistributable packages.
- High performance is always a primary goal of Yii.

Yii is backed up by a [strong core developer
team](https://www.yiiframework.com/team/) financially backed from an
[OpenCollective foundation](https://opencollective.com/yiisoft), as well as
a large community of professionals constantly contributing to Yii's
development. The Yii developer team keeps a close eye on the latest Web
development trends and on the best practices and features found in other
frameworks and projects. The most relevant best practices and features found
elsewhere are incorporated into the core framework and exposed via simple
and elegant interfaces.


## Yii versions

Yii currently has three major versions available: 1.1, 2.0, and 3.0.

- Version 1.1 is the old generation and is now in the feature freeze bugfix
  mode.
- Version 2.0 is a current stable version in the feature freeze bugfix mode.
- Version 3.0 is the current version in development. This guide is mainly
  about version 3.


## Requirements and prerequisites

Yii3 requires PHP 8.2 or above, but some packages work with older PHP, such
as PHP 7.4.

Using Yii requires basic knowledge of object-oriented programming (OOP), as
Yii is a pure OOP-based framework.  Yii3 also makes use of the latest PHP
features, such as type declarations and generators. Understanding these
concepts will help you pick up Yii3 faster.