Difference between #29 and #52 of
Yii3 - How to start

Changes

Title unchanged

Yii3 - How to start

Category unchanged

Tutorials

Yii version unchanged

3.0

Tags unchanged

Content changed

(draft - all will be retested later) In Yii3 it is not as easy to start as it was with Yii2. You have to install and configure basic things on your own. Yii3 uses the modern approach based on independent packages and dependency injection, but it makes it harder for newcomers. I am here to show all how I did it.
 
 
Note:
 
- Instead of installing local WAMP- or XAMPP-server I will be using Docker.
 
- Do not forget about a modern IDE like PhpStorm, which comes bunled with all you will ever need.
 
 
> All the code is available in my new [GitHub repository](https://github.com/rackycz/yii3api). I will be using is as a boiler-plate for my future projects so it should be always up to date and working.
 
 
# Yii3 - How to start
 
 
Yii3 offers more basic applications: Web, Console, API. I will be using the API application:
 
- https://github.com/yiisoft/app-api
 
- Other apps are linked on the page
 
 
Clone it like this:
 
- git clone https://github.com/yiisoft/app-api.git yii3api
 
 
.. and follow the **docker** instructions in the documentation.
 
 
If you don't have Docker, I recommend installing the **latest** version of Docker Desktop:
 
- https://docs.docker.com/get-started/introduction/get-docker-desktop/
 
 
## Running the demo application
 
 
You may be surprised that docker-compose.yml is missing in the root. Instead the "make" commands are prepared.
 
If you run both basic commands as mentioned in the documentation:
 
 
- make composer update
 
- make up
 
 
... then the web will be available on URL
 
- http://localhost:80
 
- If run via browser, XML is returned
 
- If run via Postman or Ajax, JSON is returned
 
 
If you want to modify the data that was returned by the endpoint, just open the action-class `src/Api/IndexAction.php` and add one more element to the returned array.
 
 
## Adding DB into your project
 
 
Your project now does not contain any DB. Let's add MariaDB and Adminer (DB browser) into file docker/dev/compose.yml:
 
 
In my case the resulting file looks like this:
 
 
```yaml
 
services:
 
  app:
 
    container_name: yii3api_php
 
    build:
 
      dockerfile: docker/Dockerfile
 
      context: ..
 
      target: dev
 
      args:
 
        USER_ID: ${UID}
 
        GROUP_ID: ${GID}
 
    env_file:
 
      - path: ./dev/.env
 
      - path: ./dev/override.env
 
        required: false
 
    restart: unless-stopped
 
    depends_on:
 
      - db
 
    ports:
 
      - "${DEV_PORT:-80}:80"
 
    volumes:
 
      - ../:/app
 
      - ../runtime:/app/runtime
 
      - caddy_data:/data
 
      - caddy_config:/config
 
    tty: true
 
  db:
 
    image: mariadb:12.0.2-noble
 
    container_name: yii3api_db
 
    environment:
 
      MARIADB_ROOT_PASSWORD: root
 
      MARIADB_DATABASE: db
 
      MARIADB_USER: db
 
      MARIADB_PASSWORD: db
 
  adminer:
 
    image: adminer:latest
 
    container_name: yii3api_adminer
 
    environment:
 
      ADMINER_DEFAULT_SERVER: db
 
    ports:
 
      - ${DEV_ADMINER_PORT}:8080
 
    depends_on:
 
      - db
 
volumes:
 
  mariadb_data:
 
```
 
 
Plus add/modify these variables in file `docker/.env`
 
- DEV_PORT=9080
 
- DEV_ADMINER_PORT=9081
 
 
Then run following commands:
 
- make down
 
- make build
 
- make up
 
 
Now you should see a DB browser on URL http://localhost:9081/?server=db&username=db&db=db
 
 
Login, server and pwd is defined in the snippet above.
 
 
If you type "docker ps" into your host console, you should see 3 running containers: yii3api_php, yii3api_adminer, yii3api_db.
 
 
The web will be, from now on, available on URL http://localhost:9080 which is more handy than just ":80" I think. (Later you may run 4 different projects at the same time and all cannot run on port 80)
 
 
## Enabling MariaDB (MySQL) and migrations
 
 
Now when your project contains MariaDB, you may wanna use it in the code ...
 
 
### **Installing composer packages**
 
 
After some time of searching you will discover you need to install these composer packages:
 
- https://github.com/yiisoft/db-mysql
 
- https://github.com/yiisoft/cache
 
- https://github.com/yiisoft/db-migration
 
 
So you need to run following commands:
 
 
```sh
 
composer require yiisoft/db-mysql
 
composer require yiisoft/cache
 
composer require yiisoft/db-migration --dev
# Intro
 
 
In Yii3 it is not as easy to start as it was with Yii2. You have to install and configure basic things on your own. Yii3 uses the modern approach based on independent packages and dependency injection, but it makes it harder for newcomers. I am here to show all how I did it.
 
 
## Git
 
 
All my code is available in my GitHub repositories:
 
- [Yii3 API demo](https://github.com/rackycz/yii3api) (yii3api) - May be dropped in the future as `yii3web` offers more)
 
- [Yii3 WEB demo](https://github.com/rackycz/yii3web) (yii3web) - I think I will use this one more
 
 
I will be using it as a boiler-plate for my future projects so it should be always up-to-date and working.
 
 
## Docker
 
 
Instead of installing local WAMP- or XAMPP-server I will be using Docker. Do not forget about a modern IDE like PhpStorm, which comes bundled with all you will ever need.
 
 
 
## PSR Standards by Framework Interoperability Group
 
First of all, learn what [PHP Standards Recommendations](https://en.wikipedia.org/wiki/PHP_Standard_Recommendation) by [Framework Interoperability Group (FIG)](https://www.php-fig.org/psr) are. It will help you understand why so many "weird" PSR imports are in the Yii3 code. In short: These interfaces help authors of different frameworks to write compatible classes so they can be reused in any other framework following these principles.
 
 
## Dependency injection + container
 
Check [this YouTube video](https://www.youtube.com/watch?v=TqMXzEK0nsA) for explanation
 
 
 
## 135 packages by Yii
 
 
The Yii team split the functionalities if Yii3 into 135 packages which you should check in advance to know what is available. They are listed [here](https://www.yiiframework.com/status/3.0) or the same is also [here](https://www.yiiframework.com/doc/api/3.0).
 
 
For example [login](https://github.com/yiisoft/user) and [RBAC](https://github.com/yiisoft/rbac), which can be stored in [DB](https://github.com/yiisoft/rbac-db) or in [PHP files](https://github.com/yiisoft/rbac-php/). And many more like "active-record" (which is now finished), "form-model" or "boostrap5".
 
 
## invoke()
 
The `__invoke()` public method is called when you call the **instance** as a method. (Therefore the constructor was already executed)
 
 
```php
 
$obj = new MyObj(); // Now the __construct() is executed.
 
$obj(); // Now the __invoke() is executed (The instance is needed!)
 
```
 
 
I never used it, but prepared a following example that shows when invoking can be applied:
 
 
```php
 
class MyUpper
 
{
 
    public function __invoke($a) { return $this->go($a); }
 
    public function go($a) { return strtoupper($a); }
 
}
 
$instance = new MyUpper();
 
$array = ['a', 'B', 1, '1'];
 
 
// __invoke is used:
 
var_dump($instance($array[0])); 
 
var_dump(array_map($instance, $array));
 
 
// These do the same without invoking:
 
var_dump(array_map('strtoupper', $array));
 
var_dump(array_map([$instance, 'go'], $array));
 
var_dump(array_map(function($a) use ($instance) { return $instance->go($a); }, ['a','B',1,'1']));
 
```
 
 
## Theory around __invoke():
 
- If a class implements the `__invoke()` method it is a `callable-` or `invokable-object`.
 
- Its instance can be used as an anonymous function, callable or closure.
 
- `__invoke()` implements the main (or the only) functionality of the object.
 
 
- Why not to use `$instance->myMethod()` instead? You would need to implement an API and others would have to know it. Calling `$instance()` is a "universal anonymous API". Plus modern middleware or handlers often need to be passed as "callables".
 
- Usual anonymous functions can only do a simple task. When you use `$instance()`, you are backed with a large object which can do much more. It can also use traits, state or OOP benefits.
 
- Method `__invoke($a ,$b)` can take input parameters. But the application must know about them, which brings me back to interfaces. I am confused a little. So the invoke-params should probably be mostly provided by the DI I guess.
 
- But you still can use method instead of invokation. For example in file `config/common/routes.php` you can use both:
 
  - `->action(Web\HomePage\Action::class)` // \__invoke() needed
 
  - `->action([Web\HomePage\Action::class, 'run'])`
 
- Invokable objects are often used for middleware as it fits naturally into dispatcher and pipeline systems. Middleware can then be stateful. But the same aplies to interface-based approaches, you only need to specify the method.
 
 
Summary:
 
 
Whenever a method requires a **callable** as the input parameter, you can supply "named function", "anonymous function" or  "invokable object". It is up to you what you pick.
 
 
 
## Hash annotations for class attributes
 
PHP 8 introduces annotations like this (not only for class attributes):
 
 
- `#[Column(type: 'primary')]`
 
- `#[Column(type: 'string(255)', nullable: true)]`
 
- `#[Entity(repository: UserRepository::class)]`
 
- `#[ManyToMany(target: Role::class, through: UserRole::class)]`
 
 
They should replace the original DocBlock annotatinos and provide more new functionalities.
 
 
Learn what they mean and how they are used by Yii3. To me this is a brand new topic as well.
 
 
 
# Yii3 - How to start
 
 
Yii3 offers more basic applications: Web, Console, API. I will be using the API application:
 
- https://github.com/yiisoft/app-api
 
- Other apps are linked on the page
 
 
Clone it like this:
 
- git clone https://github.com/yiisoft/app-api.git yii3api
 
 
.. and follow the **docker** instructions in the documentation.
 
 
If you don't have Docker, I recommend installing the **latest** version of Docker Desktop:
 
- https://docs.docker.com/get-started/introduction/get-docker-desktop/
 
 
## Running the demo application
 
 
You may be surprised that docker-compose.yml is missing in the root. Instead the "make up" and "make down" commands are prepared.
 
If you run both basic commands as mentioned in the documentation:
 
 
- make composer update
 
- make up
 
 
... then the web will be available on URL
 
- http://localhost:80
 
- If run via browser, XML is returned
 
- If run via Postman or Ajax, JSON is returned
 
 
If you want to modify the data that was returned by the endpoint, just open the action-class `src/Api/IndexAction.php` and add one more element to the returned array.
 
 
> You may be missing 'docker compose stop' or 'make stop', because 'make down' removes your containers and drops your DB. In that case you can add it to the Makefile in the root (see below). If you then type 'make help' you will see the new command. 
 
 
```sh
 
ifeq ($(PRIMARY_GOAL),stop)
 
stop: ## Stop the dev environment
 
$(DOCKER_COMPOSE_DEV) stop
 
endif
 
```
 
 
## Adding DB into your project
 
 
Your project now does not contain any DB. Let's add MariaDB and Adminer (DB browser) into file docker/dev/compose.yml:
 
 
In my case the resulting file looks like this:
 
 
```yaml
 
services:
 
  app:
 
    container_name: yii3api_php
 
    build:
 
      dockerfile: docker/Dockerfile
 
      context: ..
 
      target: dev
 
      args:
 
        USER_ID: ${UID}
 
        GROUP_ID: ${GID}
 
    env_file:
 
      - path: ./dev/.env
 
      - path: ./dev/override.env
 
        required: false
 
    restart: unless-stopped
 
    depends_on:
 
      - db
 
    ports:
 
      - "${DEV_PORT:-80}:80"
 
    volumes:
 
      - ../:/app
 
      - ../runtime:/app/runtime
 
      - caddy_data:/data
 
      - caddy_config:/config
 
    tty: true
 
  db:
 
    image: mariadb:12.0.2-noble
 
    container_name: yii3api_db
 
    environment:
 
      MARIADB_ROOT_PASSWORD: root
 
      MARIADB_DATABASE: db
 
      MARIADB_USER: db
 
      MARIADB_PASSWORD: db
 
  adminer:
 
    image: adminer:latest
 
    container_name: yii3api_adminer
 
    environment:
 
      ADMINER_DEFAULT_SERVER: db
 
    ports:
 
      - ${DEV_ADMINER_PORT}:8080
 
    depends_on:
 
      - db
 
volumes:
 
  mariadb_data:
 
```
 
 
Plus add/modify these variables in file `docker/.env`
 
- DEV_PORT=9080
 
- DEV_ADMINER_PORT=9081
 
 
Then run following commands:
 
- make down
 
- make build
 
- make up
 
 
Now you should see a DB browser on URL http://localhost:9081/?server=db&username=db&db=db
 
 
Login, server and pwd is defined in the snippet above.
 
 
If you type "docker ps" into your host console, you should see 3 running containers: yii3api_php, yii3api_adminer, yii3api_db.
 
 
The web will be, from now on, available on URL http://localhost:9080 which is more handy than just ":80" I think. (Later you may run 4 different projects at the same time and all cannot run on port 80)
 
 
## Enabling MariaDB (MySQL) and migrations
 
 
Now when your project contains MariaDB, you may wanna use it in the code ...
 
 
### **Installing composer packages**
 
 
After some time of searching you will discover you need to install these composer packages:
 
- https://github.com/yiisoft/db-mysql
 
- https://github.com/yiisoft/cache
 
- https://github.com/yiisoft/db-migration
 
 
So you need to run following commands:
 
 
```sh
 
composer require yiisoft/db-mysql
 
composer require yiisoft/cache
 
composer require yiisoft/db-migration

```
[...]
Next we also need an algorithm that will enforce these tokens in each request, will validate and refresh them and will restrict access only to endpoints that the user can use. This is a bigger topic for later. It may be covered by the package https://github.com/yiisoft/auth/ which offers "HTTP bearer authentication".

##
UI
 
 
**Pjax**
 
 
Pjax does not exist any more, but you can use [HTMX](https://htmx.org/docs/#introduction) instead:
 
 
```
 
<script src="htmx.min.js"></script>
 
```
 
 
Your action:
 
 
 
```
 
<?php
 
declare(strict_types=1);
 
namespace App\Web\HomePage;
 
use Psr\Http\Message\ResponseFactoryInterface;
 
use Psr\Http\Message\ResponseInterface;
 
final readonly class Htmx
 
{
 
    public function __invoke(
 
        ResponseFactoryInterface $responseFactory
 
    ): ResponseInterface
 
    {
 
        $response = $responseFactory->createResponse();
 
        $response
 
            ->getBody()
 
            ->write('You are at homepage.<div id="id2">Welcome</div>');
 
        return $response;
 
    }
 
}
 
```
 
 
And then a simple HTML
 
 
```
 
<h2>HTMX test</h2>
 
<div id="htmx">
 
    <p>This is the original text</p>
 
</div>
 
<button data-hx-get="/htmx"
 
        data-hx-trigger="click"
 
        data-hx-target="#htmx"
 
        data-hx-select="#id2"
 
        data-hx-swap="innerHTML">
 
    Click me (and watch the traffic in devtools)
 
</button>
 
```
 
 
**
JS client - Installable Vuejs3 PWA** If you create a REST API you may be interested in a JS frontend that will communicate with it using Ajax. Below you can peek into my very simple VueJS3 attempt. It is an installable PWA application that works in offline mode (=1 data transfer per day, not on every mouse click) and is meant for situations when customer does not have wifi everywhere. See my [Gitlab](https://gitlab.com/radin.cerny/vuejs3-pwa-demo-plus).
2 0
5 followers
Viewed: 61 485 times
Version: 3.0
Category: Tutorials
Tags:
Written by: rackycz rackycz
Last updated by: rackycz rackycz
Created on: Oct 8, 2025
Last updated: 8 days ago
Update Article

Revisions

View all history