1
头图

Yii is a high-performance, component-based PHP framework for rapid development of modern web applications.

Today, based on the idea of Yii2 Yii2 Todo List from 0 to 1, and complete the following functions:

  1. May be based on a key create Todo Item , then according to key query corresponding Todo Item .
  2. You can top, complete, delete a single Todo Item , the top Todo Item will be arranged at the front, and the completed Todo Item will be arranged at the back.

Initialize the YII repository

Use the following command to initialize a warehouse of YII

composer create-project --prefer-dist yiisoft/yii2-app-basic basic

However, my mac to the network through this method and fails to install some dependencies, so I choose the second method here. (as follows)

Download the archive at yiiframework and unzip it into the project directory where you want to place it.

After downloading and decompressing, you need to modify the config/web.php file and cookieValidationKey configuration item (just enter a value) so that the project can start normally.

After the project is initialized, let's run the project with the following command.

php yii serve --port=8888

Then we open http://localhost:8888 and see that our page has been successfully started! (As shown below)

image

Initialize the data model

Next, let's initialize our data model.

The fields we need to create are the following:

  • id: self-incrementing primary key;
  • key: Todo's key;
  • title: Todo's title;
  • is_completed: Whether Todo is completed;
  • is_top: Whether Todo is on top;
  • is_deleted: Whether Todo is deleted;

And above these fields, we have the most scenes by key to fish out the relevant Todo Item , so it should give key establish a common index.

In summary, our sql statement should look like this:

CREATE TABLE IF NOT EXISTS `todos` (
    `id` int PRIMARY KEY AUTO_INCREMENT,
    `key` varchar(64) NOT NULL DEFAULT '',
    `title` varchar(64) NOT NULL DEFAULT '',
    `is_top` tinyint(1) NOT NULL DEFAULT 0,
    `is_completed` tinyint(1) NOT NULL DEFAULT 0,
    `is_deleted` tinyint(1) NOT NULL DEFAULT 0,
    index `key`(`key`)
) engine=InnoDB CHARSET=utf8;

SQL in the database to create the corresponding data table.

Then, we can also view the index we created with the following statement.

SHOW INDEX FROM `todos`;

image

Handling Todo business logic

After the data table is successfully created, we are ready to start writing the business logic related to Todo

Before that, we also need to do some Yii configuration initialization work.

Initialize Yii configuration

First of all, our php service needs to connect to the database, so you need to configure your database connection first, which is config/db.php :

  • Database configuration
<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=[mysql服务器地址];port=[mysql端口];dbname=[数据库名称]',
    'username' => '[数据库用户名]',
    'password' => '[数据库密码]',
    'charset' => 'utf8',
    'attributes' => [
        // 查询时将 int 类型按原类型返回
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_EMULATE_PREPARES => false
    ]
];
  • URL beautification configuration

Then, let's configure URL beautification, so that we can restful style, and adjust config/web.php in urlManager . (as follows)

'urlManager' => [
  'enablePrettyUrl' => true,
  'enableStrictParsing' => false,
  'showScriptName' => false,
  'rules' => [
  ],
],
  • JSON input parameter configuration

Then, we also need to modify request in order to accept the application/json parameters of 061fb8b89bc87d.

'components' => [
    ...
    'request' => [
        'parsers' => [
            'application/json' => 'yii\web\JsonParser',
        ]
    ],
    ...
]

After modifying the configuration, you can restart your project.

Create TodoModel + TodoRepository + TodoService + TodoController

Let's create Todo data entity class - TodoModel , this model will run through Todo List entire life cycle.

<?php
namespace app\models;

use Yii;
use Yii\base\Model;

class TodoModel extends Model {
    public $id;
    public $key;
    public $title;
    public $is_top;
    public $is_completed;
    public $is_deleted;
}

Then, we create TodoRepository for data persistence. - SQL is written here.

<?php

namespace app\repositories;

use app\models\TodoModel;

class TodoRepository {
    public static function selectAll(TodoModel $todo) {

    }

    public static function insertOne(TodoModel $todo) {

    }

    public static function deleteOne(TodoModel $todo) {

    }

    public static function updateOne(TodoModel $todo) {

    }
}

Next, let's create TodoService to handle business logic. - All business logic is placed here.

<?php

namespace app\services;

use app\models\TodoModel;
use app\repositories\TodoRepository;

class TodoService {
    public function getAllTodo(TodoModel $model) {
        return TodoRepository::selectAll($model);
    }

    public function addTodo(TodoModel $model) {
        
    }

    public function topTodo(TodoModel $model) {

    }

    public function completeTodo(TodoModel $model) {

    }

    public function deleteTodo(TodoModel $model) {

    }
}

Finally, we create TodoController , which is used to control business processes and handle interface requests. - The logic for interacting with the client is placed here.

<?php

namespace app\controllers;

use Yii;

use yii\rest\ActiveController;
use yii\web\Response;
use app\services\TodoService;
use app\models\TodoModel;

class TodoController extends ActiveController
{
    public TodoService $todoService;

    public function __construct($id, $module, $config = [])
    {
        parent::__construct($id, $module, $config);
        this.$todoService = new TodoService();
    }

    // 将响应数据转成 JSON
    public function behaviors()
    {
        return [
            [
                'class' => \yii\filters\ContentNegotiator::className(),
                'only' => ['index', 'view'],
                'formats' => [
                    'application/json' => \yii\web\Response::FORMAT_JSON,
                ],
            ],
        ];
    }

    public function actionGetTodoList() {
      
    }
}

After TodoModel basic 061fb8b89bc9b6 + TodoRepository + TodoService + TodoController , that is, MVC model, we are ready to start adding real and effective business logic.

Query key corresponding to Todo List

We are now ready to query the corresponding todo list key

We first edit TodoRepository of selectAll , and write the corresponding query logic SQL

class TodoRepository {
  /**
   * @throws \yii\db\Exception
   */
  public static function selectAll(TodoModel $todo) {
    $db = Yii::$app->db;
    // 组装 SQL 语句,查询对应 key 且未删除的数据
    // 查询的数据按照 `是否完成` 升序排列,按照 `是否置顶` 降序排列
    $sql = "SELECT * 
            FROM `todos`
            WHERE `key` = :code AND `is_deleted` = 0
            ORDER BY is_completed ASC, is_top DESC";
    return $db->createCommand($sql)->bindValue(':code', $todo->key)->queryAll();
  }
  //...
}

In TodoRepository of SQL after the statement editing is complete, we can try to execute in the database. (As shown below)

image

As can be seen from the above figure, the SQL as we expected - using key as the index, only 4 pieces of data are retrieved (the database has 10 pieces of data at this time).

This SQL also involves Using filesort , I have not thought of a better optimization plan, you can try to optimize this SQL.

We edit TodoController of actionGetTodoList method can ( TodoService not need to change).

public function actionGetTodoList() {
    $model = new TodoModel();
    $params = Yii::$app->request->get();
    // 取出 query 参数中的 key 字段
    $model->key = $params['key'];
    return $this->todoService->getAllTodo($model);
}

After the logic is added, open the page http://localhost:8888/todo/get-todo-list?key=test verify the effect. (As shown below)

image

As you can see from the image above, the data is returned as we expected it to be filtered and sorted!

Completion of the remaining business logic - additions, deletions and modifications

Next, just add the logic of adding, deleting and modifying in turn. This should be the simplest and most classic CRUD . (as follows)

  • TodoModel.php
<?php

namespace app\models;

use Yii;
use yii\base\Model;

class TodoModel extends Model
{
    public $id;
    public $key = '';
    public $title = '';
    public $is_top = 0;
    public $is_completed = 0;
    public $is_deleted = 0;

    public function rules()
    {
        return [
            [['id', 'key', 'title'], 'required']
        ];
    }
}
  • TodoRepository.php
<?php

namespace app\repositories;

use Yii;
use app\models\TodoModel;

class TodoRepository
{
    /**
     * @throws \yii\db\Exception
     */
    public static function selectAll(TodoModel $todo)
    {
        $db = Yii::$app->db;
        // 组装 SQL 语句,查询对应 key 且未删除的数据
        // 查询的数据按照 `是否完成` 升序排列,按照 `是否置顶` 降序排列
        $sql = "SELECT * 
                FROM `todos`
                WHERE `key` = :code AND `is_deleted` = 0
                ORDER BY is_completed ASC, is_top DESC";
        return $db->createCommand($sql)->bindValue(':code', $todo->key)->queryAll();
    }

    /**
     * @throws \yii\db\Exception
     */
    public static function insertOne(TodoModel $todo)
    {
        $db = Yii::$app->db;
        return $db->createCommand()->insert('todos', $todo)->execute();
    }

    /**
     * @throws \yii\db\Exception
     */
    public static function updateOne(array $todoData, string $id)
    {
        $db = Yii::$app->db;
        return $db
                ->createCommand()
                ->update('todos', $todoData, "id = :id")
                ->bindValue("id", $id)
                ->execute();
    }
}
  • TodoService.php
<?php

namespace app\services;

use app\models\TodoModel;
use app\repositories\TodoRepository;

class TodoService
{
    public function getAllTodo(TodoModel $model)
    {
        return TodoRepository::selectAll($model);
    }

    public function addTodo(TodoModel $model)
    {
        return TodoRepository::insertOne($model);
    }

    public function topTodo(TodoModel $model)
    {
        return TodoRepository::updateOne([
            'is_top' => 1
        ], $model->id);
    }

    public function completeTodo(TodoModel $model)
    {
        return TodoRepository::updateOne([
            'is_completed' => 1
        ], $model->id);
    }

    public function deleteTodo(TodoModel $model)
    {
        return TodoRepository::updateOne([
            'is_deleted' => 1
        ], $model->id);
    }
}
  • TodoController.php
<?php

namespace app\controllers;

use Yii;

use yii\web\Controller;
use app\services\TodoService;
use app\models\TodoModel;

class TodoController extends Controller
{
    public $todoService;
    public $enableCsrfValidation = false;

    public function __construct($id, $module, $config = [])
    {
        parent::__construct($id, $module, $config);
        $this->todoService = new TodoService();
    }

    // 将响应数据转成 JSON
    public function behaviors()
    {
        return [
            [
                'class' => \yii\filters\ContentNegotiator::className(),
                'formats' => [
                    'application/json' => \yii\web\Response::FORMAT_JSON,
                ],
            ],
        ];
    }

    public function actionGetTodoList()
    {
        $model = new TodoModel();
        $params = Yii::$app->request->get();
        // 取出 query 参数中的 key 字段
        $model->key = $params['key'];
        return [
            'code' => 0,
            'data' => $this->todoService->getAllTodo($model)
        ];
    }

    public function actionAdd()
    {
        $model = new TodoModel();
        $params = Yii::$app->request->post();
        $model->key = $params['key'];
        $model->title = $params['title'];
        $this->todoService->addTodo($model);
        return ['code' => 0];
    }

    public function actionTop()
    {
        $model = new TodoModel();
        $params = Yii::$app->request->post();
        $model->id = $params['id'];
        $this->todoService->topTodo($model);
        return ['code' => 0];
    }

    public function actionComplete()
    {
        $model = new TodoModel();
        $params = Yii::$app->request->post();
        $model->id = $params['id'];
        $this->todoService->completeTodo($model);
        return ['code' => 0];
    }

    public function actionDelete()
    {
        $model = new TodoModel();
        $params = Yii::$app->request->post();
        $model->id = $params['id'];
        $this->todoService->deleteTodo($model);
        return ['code' => 0];
    }
}

In this way, our Todo List system is basically completed, and it has completed the following functions:

  1. May be based on a key create Todo Item , then according to key query corresponding Todo Item .
  2. You can top, complete, delete a single Todo Item , the top Todo Item will be arranged at the front, and the completed Todo Item will be arranged at the back.

Of course, we also need to consider parameter verification, optimization of big data queries, simpler parameter binding, etc., which will not be expanded here, and may be explained in a new article.

Deploy the application

Now, let's Todo List system online.

Start the Docker container

Yii2 is very simple because Yii has built-in docker-compose configuration file.

So, we just need to run docker-compose up -d in the folder to start a docker service. (As shown below)

image

Now, let's modify docker-compose.yml and change it to a special port - 9999 .

ports:
   - '9999:80'

Then, in our server (my server is Alibaba Cloud ECS), we pull down the corresponding warehouse code and run docker-compose up -d start the container.

Configure Nginx

After the service starts, we need to configure nginx , we will specify the domain name request hacker.jt-gmall.com forwarded to 9999 port.

Then, nginx to allow front-end cross-domain requests (last few lines).

server {
    listen 443;
    server_name hacker.jt-gmall.com;
    ssl on;
    ssl_certificate /https/hacker.jt-gmall.com.pem;
    ssl_certificate_key /https/hacker.jt-gmall.com.key;
    ssl_session_timeout 5m;
    ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location / {
      index index.html index.jsp;
      client_max_body_size 300m;
      client_body_buffer_size 128k;
      proxy_connect_timeout 600;
      proxy_read_timeout 600;
      proxy_send_timeout 600;
      proxy_buffer_size 64k;
      proxy_buffers 4 64k;
      proxy_busy_buffers_size 64k;
      proxy_temp_file_write_size 64k;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_http_version 1.1;
      proxy_pass http://127.0.0.1:9999;

      add_header "Access-Control-Allow-Origin" "*"; # 全局变量获得当前请求origin,带cookie的请求不支持*
      add_header "Access-Control-Allow-Methods" "*"; # 允许请求方法
      add_header "Access-Control-Allow-Headers" "*"; # 允许请求的 header
      # 如果是 OPTIONS 请求,则返回 204
      if ($request_method = 'OPTIONS') {
        return 204;
      }
    }
  }

Install dependencies

After the service is started and nginx is configured, the following error may appear.

image

This is because Git version management, it ignores Yii of vendor directory, we only need to use composer will depend re-install it again, run the following command.

composer update
composer install

Since config/db.php contains database connection information, I did not put it in the Git warehouse.

If you are using my demo , please complete this file as well.

Then, we open the browser and enter https://hacker.jt-gmall.com/todo/get-todo-list?key=test see the effect! (As shown below)

image

You're done!

summary

In this article, I wrote an article about my experience of Yii build a basic Todo List

After the actual operation, I found that Yii build a server-side business site, and the classic MVC mode is also relatively easy to understand.

In subsequent articles, I may Yii the advanced use of 061fb8b89bcff7.

Finally, attach the Demo address this experience.

one last thing

If you have seen this, I hope you will give a like and go~

Your likes are the greatest encouragement to the author, and can also allow more people to see this article!

If you think this article is helpful to you, please help to light up star github to encourage it!


晒兜斯
1.8k 声望534 粉丝