From fbd1a1f1738931a4e818299699a9672e36eacd01 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:51:59 +0000 Subject: [PATCH 1/4] Initial plan From eb420dd17c80d2a2c20adebb7e27efa9f7c6f811 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:57:32 +0000 Subject: [PATCH 2/4] Add cookbook recipe for replicating Yii2 basic app with Yii3 Co-authored-by: samdark <47294+samdark@users.noreply.github.com> --- src/.vitepress/config.js | 1 + src/cookbook/index.md | 1 + src/cookbook/yii2-basic-to-yii3.md | 902 +++++++++++++++++++++++++++++ 3 files changed, 904 insertions(+) create mode 100644 src/cookbook/yii2-basic-to-yii3.md diff --git a/src/.vitepress/config.js b/src/.vitepress/config.js index 1cd9fd08..27c04512 100644 --- a/src/.vitepress/config.js +++ b/src/.vitepress/config.js @@ -157,6 +157,7 @@ export default { text: 'Cookbook', items: [ {text: 'Preface', link: '/cookbook/preface'}, + {text: 'Yii2 Basic to Yii3', link: '/cookbook/yii2-basic-to-yii3'}, {text: 'Making HTTP Requests', link: '/cookbook/making-http-requests'}, {text: 'Disabling CSRF Protection', link: '/cookbook/disabling-csrf-protection'}, {text: 'Sentry Integration', link: '/cookbook/sentry-integration'} diff --git a/src/cookbook/index.md b/src/cookbook/index.md index a16f5e03..e5ded4ac 100644 --- a/src/cookbook/index.md +++ b/src/cookbook/index.md @@ -13,6 +13,7 @@ This book conforms to the [Terms of Yii Documentation](https://www.yiiframework. --- - [Preface](preface.md) +- [Replicating Yii2 basic app structure with Yii3](yii2-basic-to-yii3.md) - [Structuring code by use-case with vertical slices](organizing-code/structuring-by-use-case-with-vertical-slices.md) - [Making HTTP requests](making-http-requests.md) - [Disabling CSRF protection](disabling-csrf-protection.md) diff --git a/src/cookbook/yii2-basic-to-yii3.md b/src/cookbook/yii2-basic-to-yii3.md new file mode 100644 index 00000000..27210a92 --- /dev/null +++ b/src/cookbook/yii2-basic-to-yii3.md @@ -0,0 +1,902 @@ +# Replicating 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. + +## 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 + +## Prerequisites + +Before starting, ensure you have: + +- PHP 8.1 or higher +- Composer installed +- Basic understanding of Yii3 concepts (see the [official guide](/guide)) + +## Installation + +### Creating a Yii3 project + +Instead of `yiisoft/yii2-app-basic`, use the Yii3 application template: + +```shell +composer create-project yiisoft/app my-project +cd my-project +``` + +This creates a project with the following structure: + +``` +config/ Configuration files +public/ Web root directory +resources/ Non-PHP resources (views, assets) +runtime/ Generated files and logs +src/ Application source code +tests/ Tests +vendor/ Composer dependencies +``` + +### Starting the development server + +```shell +./yii serve +``` + +Or with Docker: + +```shell +docker-compose up -d +``` + +Access your application at `http://localhost:8080`. + +## Directory structure comparison + +Here's how Yii2 basic app directories map to Yii3: + +| 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 | + +## Replicating Yii2 basic app features + +### 1. Entry script + +**Yii2** (`web/index.php`): +```php +run(); +``` + +**Yii3** (`public/index.php`): +```php +run(); +``` + +The Yii3 runner handles configuration loading automatically from the `config/` directory. + +### 2. Controllers + +**Yii2** style: +```php +render('index'); + } +} +``` + +**Yii3** equivalent (`src/Controller/SiteController.php`): +```php +viewRenderer = $viewRenderer->withController($this); + } + + public function index(): ResponseInterface + { + return $this->viewRenderer->render('index'); + } +} +``` + +Key differences: +- No base controller class required +- Services are injected via constructor +- Methods return PSR-7 `ResponseInterface` +- No `action` prefix needed + +### 3. Routing + +**Yii2** uses URL rules in configuration: +```php +'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'rules' => [ + '' => 'site/index', + 'about' => 'site/about', + ], +], +``` + +**Yii3** uses route configuration files (`config/routes.php`): +```php +action([SiteController::class, 'index']) + ->name('site/index'), + Route::get('/about') + ->action([SiteController::class, 'about']) + ->name('site/about'), +]; +``` + +### 4. Views + +Views in Yii3 are similar to Yii2 but located in `resources/views/`. + +**Yii2** (`views/site/index.php`): +```php +title = 'Home'; +?> +

title) ?>

+``` + +**Yii3** (`resources/views/site/index.php`): +```php +setTitle('Home'); +?> +

getTitle()) ?>

+``` + +### 5. Forms and models + +**Yii2** ActiveRecord model: +```php +id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getEmail(): string + { + return $this->email; + } +} +``` + +Repository (`src/Repository/UserRepository.php`): +```php +repository = $this->entityManager + ->getRepository(User::class); + } + + public function findAll(): array + { + return $this->repository + ->select() + ->fetchAll(); + } + + public function findByUsername(string $username): ?User + { + return $this->repository + ->select() + ->where('username', $username) + ->fetchOne(); + } + + public function save(User $user): void + { + $this->entityManager->persist($user); + $this->entityManager->run(); + } +} +``` + +For form handling, use `yiisoft/form`: + +```php +name; + } + + public function getEmail(): string + { + return $this->email; + } + + public function getSubject(): string + { + return $this->subject; + } + + public function getBody(): string + { + return $this->body; + } + + public function getAttributeLabels(): array + { + return [ + 'name' => 'Name', + 'email' => 'Email', + 'subject' => 'Subject', + 'body' => 'Body', + ]; + } + + public function getRules(): array + { + return [ + 'name' => [new Required()], + 'email' => [new Required(), new Email()], + 'subject' => [new Required()], + 'body' => [new Required()], + ]; + } +} +``` + +### 6. Layouts + +**Yii2** (`views/layouts/main.php`): +```php + +beginPage() ?> + + + + head() ?> + <?= Html::encode($this->title) ?> + + +beginBody() ?> + +endBody() ?> + + +endPage() ?> +``` + +**Yii3** (`resources/views/layout/main.php`): +```php + +beginPage() ?> + + + + + + <?= Html::encode($this->getTitle()) ?> + head() ?> + + +beginBody() ?> +
+ +
+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'; -?> -

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'); ?> -

getTitle()) ?>

+ +
+
+

Congratulations!

+

You have successfully created your Yii3 application.

+
+ +
+
+
+

Heading

+

Lorem ipsum dolor sit amet, consectetur adipisicing elit.

+
+
+
+
``` -### 5. Forms and models +### 5. Layouts + +Main layout in `resources/views/layout/main.php`: -**Yii2** ActiveRecord model: ```php +beginPage() ?> + + + + + + <?= Html::encode($this->getTitle()) ?> + head() ?> + + +beginBody() ?> + + + +
+ +
+ + + +endBody() ?> + + +endPage() ?> ``` -**Yii3** separates concerns: +### 6. Models -Entity (`src/Entity/User.php`): +Business logic and data models go in `src/Model/`: + +`src/Model/User.php`: ```php email; } - public function withEmail(string $email): self + public function getStatus(): string { - $new = clone $this; - $new->email = $email; - return $new; + return $this->status; + } + + public function isActive(): bool + { + return $this->status === 'active'; } } ``` -Repository (`src/Repository/UserRepository.php`): +For data access, create repository classes in `src/Model/`: + +`src/Model/UserRepository.php`: ```php repository = $this->entityManager - ->getRepository(User::class); } public function findAll(): array { - return $this->repository - ->select() - ->fetchAll(); + return $this->db + ->createCommand('SELECT * FROM {{%user}}') + ->queryAll(); } - public function findByUsername(string $username): ?User + public function findByUsername(string $username): ?array { - return $this->repository - ->select() - ->where('username', $username) - ->fetchOne(); + return $this->db + ->createCommand('SELECT * FROM {{%user}} WHERE username = :username') + ->bindValues([':username' => $username]) + ->queryOne() ?: null; } public function save(User $user): void { - $this->entityManager->persist($user); - $this->entityManager->run(); + // Implementation for saving user } } ``` -For form handling, use `yiisoft/form`: +### 7. Form models +Form models for handling user input go in `src/Form/`: + +`src/Form/ContactForm.php`: ```php -beginPage() ?> - - - - head() ?> - <?= Html::encode($this->title) ?> - - -beginBody() ?> - -endBody() ?> - - -endPage() ?> -``` - -**Yii3** (`resources/views/layout/main.php`): -```php - -beginPage() ?> - - - - - - <?= Html::encode($this->getTitle()) ?> - head() ?> - - -beginBody() ?> -
- -
-endBody() ?> - - -endPage() ?> -``` - -### 7. Console commands - -**Yii2** (`commands/HelloController.php`): -```php -getMethod() === 'POST') { + $formHydrator->populate($form, $request->getParsedBody()); + $result = $validator->validate($form); + + if ($result->isValid()) { + // Process the form data + // Send email, save to database, etc. + + return $this->responseFactory + ->createResponse('Form submitted successfully!'); + } } + + return $this->viewRenderer->render('contact', [ + 'form' => $form, + ]); } ``` -**Yii3** (`src/Command/HelloCommand.php`): +### 8. Console commands + +Console commands live in `src/Command/`: + +`src/Command/HelloCommand.php`: ```php setDescription('Says hello') + ->setDescription('Displays a greeting message') ->addArgument( 'message', InputArgument::OPTIONAL, 'Message to display', - 'hello world' + 'Hello, World!' ); } @@ -501,6 +485,7 @@ final class HelloCommand extends Command ``` Register the command in `config/console/commands.php`: + ```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', - ], - ], -]; -``` +Configuration files are organized in `config/`: -**Yii3** uses multiple configuration files organized by purpose: +``` +config/ + ├── common/ # Shared configuration + │ ├── params.php + │ └── di.php + ├── web/ # Web-specific configuration + │ └── application.php + ├── console/ # Console-specific configuration + │ └── commands.php + └── routes.php # Route definitions +``` -`config/common/params.php` - Parameters: +`config/common/params.php` - Application parameters: ```php [ + 'name' => 'My Application', + 'charset' => 'UTF-8', + ], 'yiisoft/db-mysql' => [ - 'dsn' => 'mysql:host=localhost;dbname=yii3app', + 'dsn' => 'mysql:host=localhost;dbname=myapp', 'username' => 'root', 'password' => '', ], - 'app' => [ - 'name' => 'My Application', - ], ]; ``` -`config/common/di.php` - Service definitions: +`config/common/di.php` - Dependency injection configuration: ```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); + UserRepository::class => static function (ContainerInterface $container) { + return new UserRepository( + $container->get(ConnectionInterface::class) + ); }, ]; ``` -### 9. Database access +### 10. 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: +Access the database through dependency injection: -With Cycle ORM: ```php viewRenderer = $viewRenderer->withController($this); } - public function getAllUsers(): array + public function index(): ResponseInterface { - return $this->userRepository->findAll(); - } + $users = $this->userRepository->findAll(); - public function updateEmail(string $username, string $email): void - { - $user = $this->userRepository->findByUsername($username); - if ($user !== null) { - $updatedUser = $user->withEmail($email); - $this->userRepository->save($updatedUser); - } + return $this->viewRenderer->render('index', [ + 'users' => $users, + ]); } } ``` -With database abstraction: +### 11. Assets + +Asset bundles go in `resources/asset/`: + +`resources/asset/AppAsset.php`: ```php db - ->createCommand('SELECT * FROM user') - ->queryAll(); - } -} -``` + public array $css = [ + 'css/site.css', + ]; -### 10. Authentication and authorization + public array $js = [ + 'js/app.js', + ]; -**Yii2** uses built-in user component: -```php -user->isGuest) { - // User is not logged in + public array $depends = [ + // Add dependencies like Bootstrap here + ]; } -Yii::$app->user->login($user); ``` -**Yii3** uses `yiisoft/user` package: +Register assets in your layout: -Install the package: -```shell -composer require yiisoft/user -``` - -Implementation: ```php register(AppAsset::class); +?> +``` -use Psr\Http\Message\ResponseInterface; -use Yiisoft\User\CurrentUser; -use Yiisoft\Yii\View\Renderer\ViewRenderer; +Place CSS and JavaScript files in `public/assets/` for direct access, or let the asset manager publish them from `resources/asset/`. -final class ProfileController -{ - public function __construct( - private ViewRenderer $viewRenderer, - private CurrentUser $currentUser - ) { - $this->viewRenderer = $viewRenderer->withController($this); - } +### 12. Authentication - public function index(): ResponseInterface - { - if ($this->currentUser->isGuest()) { - // Redirect to login - } +For user authentication, use the `yiisoft/user` package: - return $this->viewRenderer->render('profile', [ - 'user' => $this->currentUser->getIdentity(), - ]); - } -} +```shell +composer require yiisoft/user ``` -### 11. Session and cookies +Create an identity class in `src/Model/`: -**Yii2** accesses session via component: -```php -session; -$session->set('key', 'value'); -$value = $session->get('key'); -``` - -**Yii3** uses PSR-7 server request: +`src/Model/UserIdentity.php`: ```php session->set('key', 'value'); - $value = $this->session->get('key'); - - // ... + return $this->id; } -} -``` - -For cookies: -```php -getCookieParams() - ); - $value = $cookies->get('name')?->getValue(); - - $response = // ... create response - return $response->withAddedHeader( - 'Set-Cookie', - (string) (new Cookie('name', 'value')) - ); + return $this->username; } } ``` -### 12. Error handling +Use it in controllers: -**Yii2** uses error handler component: -```php - [ - 'errorHandler' => [ - 'errorAction' => 'site/error', - ], -], -``` - -**Yii3** uses middleware: ```php viewRenderer = $viewRenderer->withController($this); } - public function handle(\Throwable $throwable): ResponseInterface + public function index(): ResponseInterface { - return $this->viewRenderer->render('error', [ - 'exception' => $throwable, + if ($this->currentUser->isGuest()) { + // Redirect to login page + } + + return $this->viewRenderer->render('profile', [ + 'user' => $this->currentUser->getIdentity(), ]); } } ``` -### 13. Assets - -**Yii2** asset bundles: -```php -register(AppAsset::class); -?> +$newRenderer = $this->viewRenderer->withViewPath('/new/path'); ``` -## 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) +- [Structuring code with vertical slices](/cookbook/organizing-code/structuring-by-use-case-with-vertical-slices) - [yiisoft/app template](https://github.com/yiisoft/app)