+endBody() ?>
+
+
+endPage() ?>
+```
+
+### 7. Console commands
+
+**Yii2** (`commands/HelloController.php`):
+```php
+setDescription('Says hello')
+ ->addArgument(
+ 'message',
+ InputArgument::OPTIONAL,
+ 'Message to display',
+ 'hello world'
+ );
+ }
+
+ protected function execute(
+ InputInterface $input,
+ OutputInterface $output
+ ): int {
+ $message = $input->getArgument('message');
+ $output->writeln($message);
+
+ return Command::SUCCESS;
+ }
+}
+```
+
+Register the command in `config/console/commands.php`:
+```php
+ \App\Command\HelloCommand::class,
+];
+```
+
+Run with: `./yii hello "Hello Yii3"`
+
+### 8. Configuration
+
+**Yii2** uses a single configuration file with components:
+```php
+ 'basic',
+ 'basePath' => dirname(__DIR__),
+ 'components' => [
+ 'db' => [
+ 'class' => 'yii\db\Connection',
+ 'dsn' => 'mysql:host=localhost;dbname=yii2basic',
+ 'username' => 'root',
+ 'password' => '',
+ ],
+ 'mailer' => [
+ 'class' => 'yii\swiftmailer\Mailer',
+ ],
+ ],
+];
+```
+
+**Yii3** uses multiple configuration files organized by purpose:
+
+`config/common/params.php` - Parameters:
+```php
+ [
+ 'dsn' => 'mysql:host=localhost;dbname=yii3app',
+ 'username' => 'root',
+ 'password' => '',
+ ],
+ 'app' => [
+ 'name' => 'My Application',
+ ],
+];
+```
+
+`config/common/di.php` - Service definitions:
+```php
+ static function (ContainerInterface $container) {
+ $params = $container->get('params');
+ $config = new DatabaseConfig([
+ 'default' => 'default',
+ 'databases' => [
+ 'default' => ['connection' => 'mysql'],
+ ],
+ 'connections' => [
+ 'mysql' => $params['yiisoft/db-mysql'],
+ ],
+ ]);
+ return new DatabaseManager($config);
+ },
+];
+```
+
+### 9. Database access
+
+**Yii2** uses ActiveRecord:
+```php
+all();
+$user = User::findOne(['username' => 'admin']);
+$user->email = 'newemail@example.com';
+$user->save();
+```
+
+**Yii3** uses Cycle ORM or database abstraction:
+
+With Cycle ORM:
+```php
+userRepository->findAll();
+ }
+
+ public function updateEmail(string $username, string $email): void
+ {
+ $user = $this->userRepository->findByUsername($username);
+ if ($user !== null) {
+ $user = new User(
+ $user->getId(),
+ $user->getUsername(),
+ $email
+ );
+ $this->userRepository->save($user);
+ }
+ }
+}
+```
+
+With database abstraction:
+```php
+db
+ ->createCommand('SELECT * FROM user')
+ ->queryAll();
+ }
+}
+```
+
+### 10. Authentication and authorization
+
+**Yii2** uses built-in user component:
+```php
+user->isGuest) {
+ // User is not logged in
+}
+Yii::$app->user->login($user);
+```
+
+**Yii3** uses `yiisoft/user` package:
+
+Install the package:
+```shell
+composer require yiisoft/user
+```
+
+Implementation:
+```php
+viewRenderer = $viewRenderer->withController($this);
+ }
+
+ public function index(): ResponseInterface
+ {
+ if ($this->currentUser->isGuest()) {
+ // Redirect to login
+ }
+
+ return $this->viewRenderer->render('profile', [
+ 'user' => $this->currentUser->getIdentity(),
+ ]);
+ }
+}
+```
+
+### 11. Session and cookies
+
+**Yii2** accesses session via component:
+```php
+session;
+$session->set('key', 'value');
+$value = $session->get('key');
+```
+
+**Yii3** uses PSR-7 server request:
+```php
+session->set('key', 'value');
+ $value = $this->session->get('key');
+
+ // ...
+ }
+}
+```
+
+For cookies:
+```php
+getCookieParams()
+ );
+ $value = $cookies->get('name')?->getValue();
+
+ $response = // ... create response
+ return $response->withAddedHeader(
+ 'Set-Cookie',
+ (string) (new Cookie('name', 'value'))
+ );
+ }
+}
+```
+
+### 12. Error handling
+
+**Yii2** uses error handler component:
+```php
+ [
+ 'errorHandler' => [
+ 'errorAction' => 'site/error',
+ ],
+],
+```
+
+**Yii3** uses middleware:
+```php
+viewRenderer->render('error', [
+ 'exception' => $throwable,
+ ]);
+ }
+}
+```
+
+### 13. Assets
+
+**Yii2** asset bundles:
+```php
+register(AppAsset::class);
+?>
+```
+
+## Migration checklist
+
+When migrating from Yii2 basic app to Yii3:
+
+- [ ] Set up a new Yii3 project with `yiisoft/app`
+- [ ] Map directory structure from Yii2 to Yii3
+- [ ] Convert controllers to use dependency injection
+- [ ] Update routing from URL rules to route configuration
+- [ ] Move views to `resources/views/` directory
+- [ ] Separate data access logic into repositories
+- [ ] Convert models to entities and form models
+- [ ] Update console commands to use Symfony Console
+- [ ] Reorganize configuration into multiple files
+- [ ] Replace ActiveRecord with Cycle ORM or database abstraction
+- [ ] Update authentication to use `yiisoft/user` package
+- [ ] Convert session/cookie handling to PSR-7
+- [ ] Update error handling to use middleware
+- [ ] Convert asset bundles to Yii3 format
+- [ ] Test all functionality thoroughly
+
+## Key takeaways
+
+Yii3 introduces several modern concepts:
+
+1. **Explicit dependencies**: Use constructor injection instead of accessing global components
+2. **Immutability**: Configuration and objects are immutable by design
+3. **PSR compliance**: Follow PHP-FIG standards for interoperability
+4. **Separation of concerns**: Split models into entities, repositories, and form models
+5. **Middleware-based**: Use PSR-15 middleware for request processing
+6. **Type safety**: Full type declarations for better IDE support and error detection
+
+While Yii2 basic app was simpler for beginners, Yii3's architecture provides better scalability, testability, and maintainability for modern applications.
+
+## See also
+
+- [Yii3 official guide](/guide)
+- [Upgrading from Yii2](/guide/intro/upgrade-from-v2)
+- [Application structure](/guide/structure/overview)
+- [Dependency injection](/guide/concept/di-container)
+- [yiisoft/app template](https://github.com/yiisoft/app)
From 8b9deff5558d5404d543e5fba716119d0fe4300a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 8 Jan 2026 20:59:43 +0000
Subject: [PATCH 3/4] Improve User entity with immutable withEmail() method
Co-authored-by: samdark <47294+samdark@users.noreply.github.com>
---
src/cookbook/yii2-basic-to-yii3.md | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/cookbook/yii2-basic-to-yii3.md b/src/cookbook/yii2-basic-to-yii3.md
index 27210a92..8e76ce78 100644
--- a/src/cookbook/yii2-basic-to-yii3.md
+++ b/src/cookbook/yii2-basic-to-yii3.md
@@ -269,6 +269,13 @@ final class User
{
return $this->email;
}
+
+ public function withEmail(string $email): self
+ {
+ $new = clone $this;
+ $new->email = $email;
+ return $new;
+ }
}
```
@@ -610,12 +617,8 @@ final class UserService
{
$user = $this->userRepository->findByUsername($username);
if ($user !== null) {
- $user = new User(
- $user->getId(),
- $user->getUsername(),
- $email
- );
- $this->userRepository->save($user);
+ $updatedUser = $user->withEmail($email);
+ $this->userRepository->save($updatedUser);
}
}
}
From 358178d2491c52da9aaac074903a375a61f8633b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 8 Jan 2026 22:50:29 +0000
Subject: [PATCH 4/4] Rewrite cookbook to focus on implementation, not
migration/comparison
Co-authored-by: samdark <47294+samdark@users.noreply.github.com>
---
src/cookbook/yii2-basic-to-yii3.md | 854 +++++++++++++----------------
1 file changed, 389 insertions(+), 465 deletions(-)
diff --git a/src/cookbook/yii2-basic-to-yii3.md b/src/cookbook/yii2-basic-to-yii3.md
index 8e76ce78..96b38dca 100644
--- a/src/cookbook/yii2-basic-to-yii3.md
+++ b/src/cookbook/yii2-basic-to-yii3.md
@@ -1,17 +1,9 @@
-# Replicating Yii2 basic app structure with Yii3
+# Implementing Yii2 basic app structure with Yii3
-This guide explains how to replicate the [Yii2 basic application template](https://github.com/yiisoft/yii2-app-basic) structure and features using Yii3. If you're migrating from Yii2 or want to implement a similar simple application structure, this recipe is for you.
+This guide shows how to implement a [Yii2 basic application template](https://github.com/yiisoft/yii2-app-basic) style structure in Yii3. This approach is useful if you prefer the familiar, simple organization of Yii2 basic app: controllers in a `Controller` directory, models in a `Model` directory, and so on.
-## Understanding the differences
-
-Yii3 is a complete rewrite of Yii2 with a modern architecture. Key differences include:
-
-- **Package-based**: Yii3 is split into multiple packages instead of a monolithic framework
-- **PSR compliance**: Full support for PSR-7, PSR-11, PSR-15, and other PHP-FIG standards
-- **Dependency injection**: Built-in DI container with constructor injection
-- **Immutability**: Configuration and services are immutable by design
-- **Middleware**: Request handling uses PSR-15 middleware instead of filters
-- **No static methods**: No global state or static framework methods
+> [!NOTE]
+> This recipe demonstrates one way to structure a Yii3 application. Yii3's flexible architecture supports many organizational patterns. For alternative approaches, see [Structuring code by use-case with vertical slices](/cookbook/organizing-code/structuring-by-use-case-with-vertical-slices.md).
## Prerequisites
@@ -58,35 +50,34 @@ docker-compose up -d
Access your application at `http://localhost:8080`.
-## Directory structure comparison
+## Target directory structure
-Here's how Yii2 basic app directories map to Yii3:
+We'll organize the Yii3 application similar to Yii2 basic app:
-| Yii2 Basic | Yii3 | Notes |
-|------------|------|-------|
-| `web/` | `public/` | Entry point and web assets |
-| `views/` | `resources/views/` | View templates |
-| `assets/` | `resources/asset/` | Asset bundles |
-| `controllers/` | `src/Controller/` | Controller classes |
-| `models/` | `src/` | Domain classes (not just "models") |
-| `commands/` | `src/Command/` | Console commands |
-| `config/` | `config/` | Configuration files |
-| `runtime/` | `runtime/` | Generated files and logs |
-| `mail/` | `resources/mail/` | Email view templates |
+```
+config/ Configuration files
+public/ Web root (entry point, assets)
+resources/
+ ├── asset/ Asset bundles
+ ├── mail/ Email templates
+ └── views/ View templates
+ └── layout/ Layout files
+runtime/ Generated files and logs
+src/
+ ├── Command/ Console commands
+ ├── Controller/ Web controllers
+ ├── Form/ Form models
+ └── Model/ Business logic and data models
+tests/ Tests
+vendor/ Dependencies
+```
-## Replicating Yii2 basic app features
+## Implementation guide
### 1. Entry script
-**Yii2** (`web/index.php`):
-```php
-run();
-```
+The entry script in `public/index.php` bootstraps the application:
-**Yii3** (`public/index.php`):
```php
run();
```
-The Yii3 runner handles configuration loading automatically from the `config/` directory.
+The runner automatically loads configuration from the `config/` directory.
### 2. Controllers
-**Yii2** style:
-```php
-render('index');
- }
-}
-```
-
-**Yii3** equivalent (`src/Controller/SiteController.php`):
```php
viewRenderer->render('index');
}
+
+ public function about(): ResponseInterface
+ {
+ return $this->viewRenderer->render('about');
+ }
+
+ public function contact(): ResponseInterface
+ {
+ return $this->viewRenderer->render('contact');
+ }
}
```
-Key differences:
-- No base controller class required
-- Services are injected via constructor
-- Methods return PSR-7 `ResponseInterface`
-- No `action` prefix needed
+Controllers are plain PHP classes that receive dependencies through constructor injection.
### 3. Routing
-**Yii2** uses URL rules in configuration:
-```php
-'urlManager' => [
- 'enablePrettyUrl' => true,
- 'showScriptName' => false,
- 'rules' => [
- '' => 'site/index',
- 'about' => 'site/about',
- ],
-],
-```
+Define routes in `config/routes.php`:
-**Yii3** uses route configuration files (`config/routes.php`):
```php
action([SiteController::class, 'about'])
->name('site/about'),
+ Route::methods(['GET', 'POST'], '/contact')
+ ->action([SiteController::class, 'contact'])
+ ->name('site/contact'),
];
```
### 4. Views
-Views in Yii3 are similar to Yii2 but located in `resources/views/`.
-
-**Yii2** (`views/site/index.php`):
-```php
-title = 'Home';
-?>
-
= Html::encode($this->title) ?>
-```
+Views are stored in `resources/views/` with the same organization as Yii2:
-**Yii3** (`resources/views/site/index.php`):
+`resources/views/site/index.php`:
```php
setTitle('Home');
?>
-
= Html::encode($this->getTitle()) ?>
+
+
+
+
Congratulations!
+
You have successfully created your Yii3 application.
+
+
+
+
+
+
Heading
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit.