2025-02-15:已告别 Yii2,文章内容不再维护。


gii CLI

php yii help gii/mode

php yii gii/model --generateLabelsFromComments=1 --overwrite=1 --standardizeCapitals=1 --ns='app\models\gii' --tableName="*"

# 多数据库
php yii gii/model --generateLabelsFromComments=1 --overwrite=1 --standardizeCapitals=1 --db="hub_db" --ns='app\models\hub\gii' --tableName="*"

连接数据库时设置时区

  'class' => 'yii\db\Connection',
  'dsn' => 'mysql:host=mysql;port=3306;dbname=hub',
  'username' => 'root',
  'password' => 'root',
  'charset' => 'utf8',

  // 关闭日志记录,防止被 logs 平台拿走
  'enableLogging' => YII_DEBUG ? true : false,
  'enableProfiling' => YII_DEBUG ? true : false,

  // 设置时区
  'on afterOpen' => static function ($event) {
    // set 'Asia/Bangkok' timezone
    $event->sender->createCommand("SET time_zone='+08:00';")->execute();
  },

ActiveRecord one

yii\db\ActiveRecord::findOne()yii\db\ActiveQuery::one() 都不会添加 LIMIT 1 到 生成的 SQL 语句中。如果你的查询会返回很多行的数据, 你明确的应该加上 limit(1) 来提高性能,比如 Customer::find()->limit(1)->one()

DB where

查询构建器 | yiiframework YII where 条件 | csdn

  • 字符串格式,例如:'status=1'
  • 哈希格式,例如: ['status' => 1, 'type' => 2]
  • 操作符格式,例如:['like', 'name', 'test']
  • 对象格式,例如:new LikeCondition('name', 'LIKE', 'test')

简单条件

// SQL: (type = 1) AND (status = 2).
$cond = ['type' => 1, 'status' => 2]

// SQL: (id IN (1, 2, 3)) AND (status = 2)
$cond = ['id' => [1, 2, 3], 'status' => 2]

// SQL: status IS NULL
$cond = ['status' => null]

AND OR

// SQL: `id=1 AND id=2`
$cond = ['and', 'id=1', 'id=2']

// SQL: `type=1 AND (id=1 OR id=2)`
$cond = ['and', 'type=1', ['or', 'id=1', 'id=2']]

// SQL: `type=1 AND (id=1 OR id=2)`
// 此写法 '=' 可以换成其他操作符,例:in like != >= 等
$cond = [
    'and',
    ['=', 'type', 1],
    [
        'or',
        ['=', 'id', '1'],
        ['=', 'id', '2'],
    ]
]

NOT

// SQL: `NOT (attribute IS NULL)`
$cond = ['not', ['attribute' => null]]

BETWEEN

// not between 用法相同
// SQL: `id BETWEEN 1 AND 10`
$cond = ['between', 'id', 1, 10]

IN

// not in 用法相同
// SQL: `id IN (1, 2, 3)`
$cond = ['between', 'id', 1, 10]
$cond = ['id' => [1, 2, 3]]

// IN 条件也适用于多字段
// SQL: (`id`, `name`) IN ((1, 'foo'), (2, 'bar'))
$cond = ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]]

// 也适用于内嵌 SQL 语句
$cond = ['in', 'user_id', (new Query())->select('id')->from('users')->where(['active' => 1])]

LIKE

// SQL: `name LIKE '%tester%'`
$cond = ['like', 'name', 'tester']

// SQL: `name LIKE '%test%' AND name LIKE '%sample%'`
$cond = ['like', 'name', ['test', 'sample']]

// SQL: `name LIKE '%tester'`
$cond = ['like', 'name', '%tester', false]

EXIST

// not exists用法类似
// SQL: EXISTS (SELECT "id" FROM "users" WHERE "active"=1)
$cond = ['exists', (new Query())->select('id')->from('users')->where(['active' => 1])]

Yii2 Vue 跨域问题

解决 Yii2 + Vue 前后台分离时跨域的问题 | shiqidu 跨源资源共享(CORS) | mozilla

旧的解决办法

在控制器的 behaviors 方法中增加下面的代码:

public function behaviors()
{
    $behaviors = parent::behaviors();
    if (YII_ENV_DEV) {
        // add CORS filter
        $behaviors['corsFilter'] = [
            'class' => Cors::class,
            'cors' => [
                'Origin' => ['http://localhost:8080'],
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'],
                'Access-Control-Allow-Credentials' => true,
                'Access-Control-Max-Age' => 86400,
                'Access-Control-Expose-Headers' => [],
            ],
        ];
    }

    return $behaviors;
}

上边配置的解释:

  • OriginAccess-Control-Allow-Origin 表示:响应头指定了该响应的资源是否被允许与给定的 origin 共享。
  • Access-Control-Request-Method 用于通知服务器在真正的请求中会采用哪种 HTTP 方法。因为预检请求所使用的方法总是 OPTIONS ,与实际请求所使用的方法不一样,所以这个请求头是必要的。
  • Access-Control-Request-Headers 用于通知服务器在真正的请求中会采用哪些请求头。
  • Access-Control-Allow-Credentials 表示是否可以将对请求的响应暴露给页面。返回 true 则可以,其他值均不可以。
  • Access-Control-Max-Age 表示返回结果(即 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 提供的信息) 可以被缓存多久。
  • Access-Control-Expose-Headers 列出了哪些首部可以作为响应的一部分暴露给外部。

注意:

如果设置 Origin['*'],即所有的前端跨域请求可以接受,同时把 Access-Control-Allow-Credentials 设置为 true,Yii 会直接报错:Allowing credentials for wildcard origins is insecure. Please specify more restrictive origins or set ‘credentials’ to false in your CORS configuration.

告诉你使用通配符的凭证是不安全的,让你设置更严格的 Origin 或者把 Access-Control-Allow-Credentials 设置为 false

也就是说不能 Access-Control-Allow-Credentialstrue 并且 Access-Control-Allow-Origin*

因为 Access-Control-Allow-Credentials 的意思就是允许跨域请求在请求头中携带凭证,比如 cookie,做身份识别,但是你又把 Access-Control-Allow-Origin 设置为 *,这是说不通的,是相悖的。

具体可参考:Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’ | mozilla

新的问题

上面的理论上解决了跨域问题,但是新版 Chrome 根据 Cookie 的 SameSite 属性,仍然会阻止 Cookie 的发送 network show filtered out request cookies

参考:

在开发环境根治跨域问题,使用 webpack-dev-server 代理。

前端:abc.test 后端:abc-api.test 代理:abc.test/web-api -> abc-api.test

  proxy: {
    '/web-api/': {
      target: 'http://abc-api.test/',
      pathRewrite: { '^/web-api': '' },
      changeOrigin: true, // 默认情况下,代理时会保留主机头的来源,您可以将 changeOrigin 设置为true 来覆盖此行为。
      secure: false, // 接受在 HTTPS 上运行带有无效证书的后端服务器。
    },
  }

参考:dev-server devserverproxy | webpack

– EOF –