Yii – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Sun, 17 May 2026 06:30:10 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 phpstorm 中提示:@property-read https://www.shuijingwanwq.com/2025/08/27/9286/ https://www.shuijingwanwq.com/2025/08/27/9286/#respond Wed, 27 Aug 2025 02:51:49 +0000 mailer]]> https://www.shuijingwanwq.com/?p=9286 Post Views: 67 1、在 phpstorm 中提示:@property-read 。如图1
在 phpstorm 中提示:@property-read

图1



$setting = ConventionEmailSetting::findSettingByConventionId($params['convention_id']);
if($setting && $setting->enable_custom_server == ConventionEmailSetting::ENABLE_CUSTOM_SERVER_YES){
	Yii::$app->mailer->transport = [
		'class' => 'Swift_SmtpTransport',
		'host' => $setting->host,
		'username' => $setting->username,
		'password' => $setting->password,
		'port' => $setting->port ?: '25',
		'encryption' => $setting->encryption ?: 'tls',
	];
}


2、在 common/config/main-local.php 中
<pre class="wp-block-syntaxhighlighter-code">

<?php

return [
    'components' => [
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'viewPath' => '@common/mail',
            // send all mails to a file by default. You have to set
            // 'useFileTransport' to false and configure a transport
            // for the mailer to send real emails.
            'useFileTransport' => false,
            'transport' => [
                'class' => 'Swift_SmtpTransport',
                'host' => 'smtpdm.aliyun.com',
                'username' => '',
                'password' => '',
                'port' => '25',
                'encryption' => 'tls',
            ]
        ],
    ],
];


</pre>
3、在 Yii2 中,yii\swiftmailer\Mailer 组件中对 transport 并不是通过公开属性设置的,而是通过 setTransport() 方法设置的。而 Yii::$app->mailer->transport = […] 属于 直接设置属性方式,PHPStorm 无法静态分析出你其实是通过 magic setter 间接调用了 setter 方法。这就是它提示 @property-read $transport 的原因。调整如下,不再报错。如图2
在 Yii2 中,yii\swiftmailer\Mailer  组件中对 transport 并不是通过公开属性设置的,而是通过 setTransport() 方法设置的。而 Yii::$app->mailer->transport = [...] 属于 直接设置属性方式,PHPStorm 无法静态分析出你其实是通过 magic setter 间接调用了 setter 方法。这就是它提示 @property-read $transport 的原因。调整如下,不再报错

图2



Yii::$app->mailer->setTransport([
	'class' => 'Swift_SmtpTransport',
	'host' => $setting->host,
	'username' => $setting->username,
	'password' => $setting->password,
	'port' => $setting->port ?: '25',
	'encryption' => $setting->encryption ?: 'tls',
]);


]]>
https://www.shuijingwanwq.com/2025/08/27/9286/feed/ 0
行业资讯页面缓存未及时更新的排查分析 https://www.shuijingwanwq.com/2025/08/26/9281/ https://www.shuijingwanwq.com/2025/08/26/9281/#respond Tue, 26 Aug 2025 01:46:16 +0000 https://www.shuijingwanwq.com/?p=9281 Post Views: 163 1、行业资讯页面缓存未及时更新 。当后台添加了新的文章后,前台的列表页面未及时更新,总是需要等上 5 分钟后才更新,感觉页面缓存的依赖未生效。后台的最新的两篇文章未同步在前台更新。如图1
行业资讯页面缓存未及时更新 。当后台添加了新的文章后,前台的列表页面未及时更新,总是需要等上 5 分钟后才更新,感觉页面缓存的依赖未生效。后台的最新的两篇文章未同步在前台更新。

图1

2、但是同样的程序在本地环境是正常可用的。仔细分析差异,发现生产环境的后台的文章的更新时间要较之北京时间 早 8 小时。如果 updated_at 存的是 UTC 时间(即比北京时间晚 8 小时),而你用的是北京时间做判断(比如看页面没更新),会出现: 数据实际已更新(北京时间 15:30), 但 updated_at 是 2025-06-05 07:30:25(UTC), 所以缓存判断依赖的 MAX(updated_at) 没变化,页面继续用旧缓存。


public function behaviors()
    {
        $request = Yii::$app->request;
        $alias = $request->get('alias');
        $id = $request->get('id');
        $categoryId = (int)$request->get('c', 0);
        $keyword = trim($request->get('k'));
        $tag = $request->get('tag');
        $page = (int)$request->get('page', 1);

        $detect = new MobileDetect();
        $isMobile = $detect->isMobile() ? PageElementType::DEVICE_TYPE_MOBILE : PageElementType::DEVICE_TYPE_PC;

        $behaviors = [];

        // view 页面缓存
        if (!empty($alias)) {
            $dependency = new DbDependency([
                'sql' => 'SELECT updated_at FROM articles WHERE alias = :alias LIMIT 1',
                'params' => [':alias' => $alias],
            ]);
        } elseif (!empty($id)) {
            $dependency = new DbDependency([
                'sql' => 'SELECT updated_at FROM articles WHERE id = :id LIMIT 1',
                'params' => [':id' => $id],
            ]);
        } else {
            $dependency = null;
        }

        $behaviors['pageCacheView'] = [
            'class' => PageCache::class,
            'only' => ['view'],
            'duration' => 300,
            'variations' => [$id, $alias, $isMobile],
            'dependency' => $dependency,
            'cache' => 'fileCache',
        ];

        // index 页面缓存,仅当无关键词和标签时启用
        if ($keyword === '' && empty($tag)) {

            $category = null;
            if (!empty($alias) && UrlHelper::isValidAlias($alias)) {
                $category = Yii::$app->db->cache(fn() => ArticleCategory::find()
                    ->where(['alias' => $alias, 'status' => CommonArticleCategory::STATUS_NORMAL])
                    ->limit(1)
                    ->one(), 300);
            } elseif (!empty($categoryId)) {
                $category = Yii::$app->db->cache(fn() => ArticleCategory::find()
                    ->where(['id' => $categoryId, 'status' => CommonArticleCategory::STATUS_NORMAL])
                    ->limit(1)
                    ->one(), 300);
            }

            if ($category !== null) {
                // 有分类筛选,仅依赖该分类的文章更新时间
                $indexDependency = new DbDependency([
                    'sql' => 'SELECT MAX(updated_at) FROM articles WHERE status = 1 AND category_id = :categoryId',
                    'params' => [':categoryId' => $category->id],
                ]);
            } else {
                // 无分类筛选,才依赖全表(不建议再进一步切分,否则复杂度会上升)
                $indexDependency = new DbDependency([
                    'sql' => 'SELECT MAX(updated_at) FROM articles WHERE status = 1',
                ]);
            }

            $behaviors['pageCacheIndex'] = [
                'class' => PageCache::class,
                'only' => ['index'],
                'duration' => 300,
                'variations' => [
                    $categoryId,
                    $alias,
                    $page,
                    $isMobile,
                ],
                'dependency' => $indexDependency,
                'cache' => 'fileCache',
            ];
        }

        return $behaviors;
    }


3、查看 php.ini 中的时区设置,其与 date.timezone = Asia/Shanghai 是等效的。决定修改为 date.timezone = Asia/Shanghai


date.timezone = PRC


4、设置 MySQL 连接时区,确保你的 PHP 连接数据库时设置了时区为 +08:00。在 Yii2 中可以这样做:


'components' => [
    'db' => [
        // ... 其他配置
        'on afterOpen' => function($event) {
            $event->sender->createCommand("SET time_zone = '+08:00'")->execute();
        },
    ],
],


5、编辑一篇文章后,其更新时间已经是北京时区。如图2
编辑一篇文章后,其更新时间已经是北京时区

图2

]]>
https://www.shuijingwanwq.com/2025/08/26/9281/feed/ 0
在 Yii2 的 GridView::widget 中,’data-method’ => ‘post’ ,请求方式是 GET 的排查分析 https://www.shuijingwanwq.com/2025/08/22/9278/ https://www.shuijingwanwq.com/2025/08/22/9278/#respond Fri, 22 Aug 2025 01:37:38 +0000 endBody()]]> https://www.shuijingwanwq.com/?p=9278 Post Views: 65

1、在 Yii2 的 GridView::widget 中,’data-method’ => ‘post’ ,请求方式是 GET。



 $dataProvider,
	'tableOptions' => ['class' => 'table table-centered table-nowrap table-striped mb-0'],
	'rowOptions' => function ($model, $key, $index, $grid) {
		return ['id' => "tr_{$key}"];
	},
	'options' => [
		'id' => 'grid',
		'class' => 'grid-view',
		'style' => "position: relative; overflow: auto; width: 100%;"
	],
	'filterModel' => $searchModel,
	'columns' => [
		['class' => 'yii\grid\SerialColumn'],
	
		
		[
			'class' => 'yii\grid\ActionColumn',
			'template' => '{messages} {abort}',
			'buttons' => [
				'abort' => function ($url, $model, $key) {
					if ($model->state == PhoneMessageTask::STATE_WAIT || $model->state == PhoneMessageTask::STATE_WAITING_FOR_SENDING) {
						return Html::a('', [
							'abort',
							'id' => $model->id
						], [
							'class' => 'action-icon remark-btn',
							'title' => '中止',
							'data-confirm' => '确定要中止该任务吗?',
							'data-method' => 'post',
						]);
					}
					return null;
				},
			],
		],//end ActionColumn
	],
	'emptyText' => '暂无信息', //没有数据时显示的信息
	'emptyTextOptions' => ['style' => 'text-align:center;'], //没有数据时显示信息的样式设置
	'showOnEmpty' => true, //没有数据时是否显示表格
	'summary' => '共{totalCount}条信息,共{pageCount}页,当前{begin}-{end}条',
	'pager' => [
		'options' => ['class' => 'hidden']
	]
]);
?>


2、后来发现原因在于 yii.js 未加载。但是布局文件中是存在 AppAsset::register($this);


<?php
 
namespace management\assets;
 
use yii\web\AssetBundle;
 
/**
 * Main management application asset bundle.
 */
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
    ];
    public $depends = [
        'yii\web\YiiAsset',
        // 'yii\bootstrap4\BootstrapAsset',
    ];
}
 

3、最后发现原因在于布局文件中不存在

<?php $this->endBody() ?>
]]>
https://www.shuijingwanwq.com/2025/08/22/9278/feed/ 0
在 Yii2 中,支持生成基于别名的链接,别名只能包含小写字母、数字、中划线、@ 和下划线。 https://www.shuijingwanwq.com/2025/07/21/9238/ https://www.shuijingwanwq.com/2025/07/21/9238/#respond Mon, 21 Jul 2025 01:39:51 +0000 https://www.shuijingwanwq.com/?p=9238 Post Views: 91

1、表结构设计为 允许别名为 null,且默认为 null,添加唯一索引。


-- `official-website-management-system`.articles definition

CREATE TABLE `articles` (
  `id` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
  `alias` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx-alias` (`alias`),
  KEY `idx-category_id` (`category_id`),
  KEY `idx-status` (`status`),
  KEY `idx-updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


2、在后台表单提交编辑时,自动将空字符串转换为 null。在模型的 rules 中设置转换规则与验证


    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['alias', 'filter', 'filter' => function ($value) {
                return $value === '' ? null : $value;
            }],
            ['alias', 'match', 'pattern' => '/^[a-z0-9\-@_]+$/', 'message' => '别名只能包含小写字母、数字、中划线、@ 和下划线。'],
        ];
    }


3、在前端的视图文件中,生成链接的实现如下

frontend/views/article/index.php

<a class="item" href="<?= ArticleUrlHelper::detailUrl($article) ?>">



frontend/helpers/ArticleUrlHelper.php



    public static function detailUrl($article): string
    {
        if (!empty($article['alias']) && UrlHelper::isValidAlias($article['alias'])) {
            return Url::to(['article/view', 'alias' => $article['alias']]);
        }
        return Url::to(['article/view', 'id' => $article['id']]);
    }


frontend/helpers/UrlHelper.php


    public static function isValidAlias(string $alias): bool
    {
        return preg_match('/^[a-z0-9\-@_]+$/', $alias) === 1;
    }


4、在 frontend/config/main.php 中配置 urlManager 如下



        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => true,
            'normalizer' => [
                'class' => yii\web\UrlNormalizer::class,
                'collapseSlashes' => true,
                'normalizeTrailingSlash' => true,
            ],
            'rules' => [
                '' => 'page/index',
                // 资讯详情
                'industry/detail-.html' => 'article/view',
                'industry/.html' => 'article/view',
            ],
        ],


5、当别名为:@dd87-_s 时,生成的链接为:/industry/%40dd87-_s.html 。如图1

当别名为:@dd87-_s 时,生成的链接为:/industry/%40dd87-_s.html

图1

6、原因是,Url::to() 自动对参数进行了 URL 编码, alias 是 @dd87-_s,其中 @ 是特殊字符,会被编码为 %40。决定暂不做处理。

]]>
https://www.shuijingwanwq.com/2025/07/21/9238/feed/ 0
在 Yii2 中,报错:Call to undefined method Closure::evaluateDependency() https://www.shuijingwanwq.com/2025/07/17/9225/ https://www.shuijingwanwq.com/2025/07/17/9225/#respond Thu, 17 Jul 2025 02:43:05 +0000 https://www.shuijingwanwq.com/?p=9225 Post Views: 98 1、在 Yii2 中,报错:Call to undefined method Closure::evaluateDependency()。如图1
在 Yii2 中,报错:Call to undefined method Closure::evaluateDependency()

图1

2、给 view 方法所对应的详情页面添加了页面缓存。代码实现如下


        // 新增 fileCache 组件,用于页面缓存等大数据内容
        'fileCache' => [
            'class' => 'yii\caching\FileCache',
            'cachePath' => '@runtime/cache/pages', // 可自定义路径
            'directoryLevel' => 2, // 默认即可,便于分目录管理文件
        ],




use yii\caching\DbDependency;
use yii\filters\PageCache;

    public function behaviors()
    {
        return [
            'pageCache' => [
                'class' => PageCache::class,
                'only' => ['view'], // 仅缓存 view 动作
                'duration' => 300,  // 缓存 5 分钟
                'variations' => [
                    Yii::$app->request->get('id'),
                    Yii::$app->request->get('alias'),
                ],
                'dependency' => function () {
                    $alias = Yii::$app->request->get('alias');
                    $id = Yii::$app->request->get('id');

                    if (!empty($alias)) {
                        return new DbDependency([
                            'sql' => 'SELECT updated_at FROM use_cases WHERE alias = :alias LIMIT 1',
                            'params' => [':alias' => $alias],
                        ]);
                    } elseif (!empty($id)) {
                        return new DbDependency([
                            'sql' => 'SELECT updated_at FROM use_cases WHERE id = :id LIMIT 1',
                            'params' => [':id' => $id],
                        ]);
                    }

                    return null; // 不缓存
                },
                'cache' => 'fileCache', // 指定使用 file 缓存组件
            ],
        ];
    }


3、当前 Yii2 的版本是 2.0.50。不直接传闭包给 dependency,而是在行为外先生成依赖实例


	public function behaviors()
    {
        $alias = Yii::$app->request->get('alias');
        $id = Yii::$app->request->get('id');

        if (!empty($alias)) {
            $dependency = new DbDependency([
                'sql' => 'SELECT updated_at FROM use_cases WHERE alias = :alias LIMIT 1',
                'params' => [':alias' => $alias],
            ]);
        } elseif (!empty($id)) {
            $dependency = new DbDependency([
                'sql' => 'SELECT updated_at FROM use_cases WHERE id = :id LIMIT 1',
                'params' => [':id' => $id],
            ]);
        } else {
            // 不缓存或默认缓存依赖
            $dependency = null;
        }

        return [
            'pageCache' => [
                'class' => PageCache::class,
                'only' => ['view'],
                'duration' => 300,
                'variations' => [
                    $id,
                    $alias,
                ],
                'dependency' => $dependency,
                'cache' => 'fileCache',
            ],
        ];
    }


4、不再报错,查看 Debug Log,Valid page content is found in the cache. 。由此确认页面缓存生效,符合预期。如图2
不再报错,查看 Debug Log,Valid page content is found in the cache. 。由此确认页面缓存生效,符合预期

图2

5、查看 frontend/runtime/cache/pages 目录,确定已经生成了对应的页面缓存。如图3
查看 frontend/runtime/cache/pages 目录,确定已经生成了对应的页面缓存

图3

]]>
https://www.shuijingwanwq.com/2025/07/17/9225/feed/ 0
在 Yii2 中,如何实现 case/ 的路由 https://www.shuijingwanwq.com/2025/07/11/9218/ https://www.shuijingwanwq.com/2025/07/11/9218/#respond Fri, 11 Jul 2025 01:53:41 +0000 https://www.shuijingwanwq.com/?p=9218 Post Views: 91

1、在 Yii2 中,如何实现 case/ 的路由。如果配置 ‘case/’ => ‘use-case/index’, 是无效的,打开 /case/ 会 302 重定向至 /case. 如图1

在 Yii2 中,如何实现 case/ 的路由。如果配置 'case/' => 'use-case/index', 是无效的,打开 /case/ 会 302 重定向至 /case.

图1

2、调整配置如下,’suffix’ => ‘/’, 响应 200 ,且打开 /case,也会自动跳转至 /case/ 符合预期。如图2

调整配置如下,'suffix' => '/', 响应 200 ,且打开 /case,也会自动跳转至 /case/ 符合预期。

图2


        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => true,
            'normalizer' => [
                'class' => yii\web\UrlNormalizer::class,
                'collapseSlashes' => true,
                'normalizeTrailingSlash' => true,
            ],
            'rules' => [
                // 精彩案例
                [
                    'pattern' => 'case',
                    'route' => 'use-case/index',
                    'suffix' => '/',
                ],
            ],
        ],


3、 生成的链接为:精彩案例 。符合预期。如图3

<?= Html::a('精彩案例', ['use-case/index'], ['target' => '_blank']) ?> 生成的链接为:<a href="/case/" target="_blank">精彩案例</a> 。符合预期。

图3


4、/case/list-.html 当点击第 1 页时的链接是这样的。希望是 /case/


        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => 9,
                'pageSizeParam' => false,
                'pageParam' => 'page',
            ],
            'sort' => [
                'defaultOrder' => ['id' => SORT_DESC],
            ],
        ]);

             $dataProvider->pagination,
                'options' => ['class' => 'page'], // 
                'linkContainerOptions' => ['tag' => 'li'],
                'activePageCssClass' => 'active',
                'prevPageCssClass' => 'prev',
                'nextPageCssClass' => 'next',
                'disabledPageCssClass' => 'disabled',
                'nextPageLabel' => '下一页',
                'prevPageLabel' => '上一页',
            ]) ?>



5、自定义 Pagination 类 Yii 默认从第 0 页开始分页,第一页是 page=0,需要重写分页类来处理第一页单独的 URL。


params === null ? Yii::$app->getRequest()->getQueryParams() : $this->params;

        // 移除 page 参数
        unset($params[$this->pageParam]);

        if ($page > 0) {
            // 页码从0开始,所以需要 +1
            $params[$this->pageParam] = $page + 1;

            // 第二页及以上:/case/list-2.html
            return Yii::$app->getUrlManager()->createUrl(array_merge(
                ['use-case/index'],
                $params
            ));
        }

        // 第1页:/case/
        return Yii::$app->getUrlManager()->createUrl(array_merge(
            ['use-case/index'],
            $params
        ));
    }
}




//        $dataProvider = new ActiveDataProvider([
//            'query' => $query,
//            'pagination' => [
//                'pageSize' => 9,
//                'pageSizeParam' => false,
//                'pageParam' => 'page',
//            ],
//            'sort' => [
//                'defaultOrder' => ['id' => SORT_DESC],
//            ],
//        ]);

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => new CleanPagination([
                'totalCount' => $query->count(),
                'pageSize' => 9,
                'pageSizeParam' => false,
                'pageParam' => 'page',
            ]),
            'sort' => [
                'defaultOrder' => ['id' => SORT_DESC],
            ],
        ]);


]]>
https://www.shuijingwanwq.com/2025/07/11/9218/feed/ 0
当网址为 /case 或者 /case/ 时,Yii getAlias 生成的图片地址不一样,进而导致 404 https://www.shuijingwanwq.com/2025/07/09/9204/ https://www.shuijingwanwq.com/2025/07/09/9204/#respond Wed, 09 Jul 2025 03:12:09 +0000 https://www.shuijingwanwq.com/?p=9204 Post Views: 60

1、视图中代码实现如下

当网址为 http://official.local/case 时,生成的 ,图片地址为 http://official.local/static/images/header/top.png,响应 200

当网址为 http://official.local/case/ 时,生成的 ,图片地址为 http://official.local/case/images/top.png ,响应 404。怎样解决?如图1

当网址为 http://official.local/case/ 时,生成的 <img class="top-icon" src="./images/top.png"> ,图片地址为 http://official.local/case/images/top.png ,响应 404。怎样解决?

图1

2、查看 http://official.local/case/ 的页面源代码,生成的 ,图片地址为 http://official.local/case/images/top.png ,响应 404。如图2

查看 http://official.local/case/ 的页面源代码,生成的 <img class="top-icon" src="./images/top.png"> ,图片地址为 http://official.local/case/images/top.png ,响应 404

图2

3、查看 Debug,原来已经报错,且响应 404 yii\base\InvalidRouteException: Unable to resolve the request “case/”. ,如图3

查看 Debug,原来已经报错,且响应 404 yii\base\InvalidRouteException: Unable to resolve the request "case/".

图3


yii\base\InvalidRouteException: Unable to resolve the request "case/". in C:\wwwroot\official-website-management-system\src\vendor\yiisoft\yii2\base\Module.php:561
Stack trace:
#0 C:\wwwroot\official-website-management-system\src\vendor\yiisoft\yii2\web\Application.php(103): yii\base\Module->runAction('case/', Array)
#1 C:\wwwroot\official-website-management-system\src\vendor\yiisoft\yii2\base\Application.php(384): yii\web\Application->handleRequest(Object(yii\web\Request))
#2 C:\wwwroot\official-website-management-system\src\frontend\web\index.php(18): yii\base\Application->run()
#3 {main}

Next yii\web\NotFoundHttpException: Page not found. in C:\wwwroot\official-website-management-system\src\vendor\yiisoft\yii2\web\Application.php:115
Stack trace:
#0 C:\wwwroot\official-website-management-system\src\vendor\yiisoft\yii2\base\Application.php(384): yii\web\Application->handleRequest(Object(yii\web\Request))
#1 C:\wwwroot\official-website-management-system\src\frontend\web\index.php(18): yii\base\Application->run()
#2 {main}


4、最终实现了一个小的帮助类方法


    public static function url($path): string
    {
        $cdn = Yii::$app->params['cdnDomainHttps'] ?? '';
        return rtrim($cdn, '/') . '/' . ltrim($path, '/');
    }




]]>
https://www.shuijingwanwq.com/2025/07/09/9204/feed/ 0
在 Yii2 中,查询缓存未生效的排查分析 https://www.shuijingwanwq.com/2025/07/07/9198/ https://www.shuijingwanwq.com/2025/07/07/9198/#respond Mon, 07 Jul 2025 01:58:44 +0000 https://www.shuijingwanwq.com/?p=9198 Post Views: 127 1、代码实现如下


    /**
     * 获取热门标签
     *
     * @param int $limit 获取数量,默认10个
     * @return UseCaseTag[]
     * @throws \Throwable
     */
    public static function getHotTags(int $limit = 30): array
    {
        Yii::info(
            [
                '获取热门标签',
                self::getDb()->enableQueryCache
            ]
        );
        return self::getDb()->cache(function ($db) use ($limit) {
            return self::find()
                ->where(['status' => self::STATUS_NORMAL])
                ->orderBy(['case_count' => SORT_DESC])
                ->limit($limit)
                ->all();
        }, 300); // 缓存 300 秒
    }


2、已经确认 self::getDb()->enableQueryCache 的值为 true,但是每一次执行到此方法,仍然会执行对应的 SQL,如图1
已经确认 self::getDb()->enableQueryCache 的值为 true,但是每一次执行到此方法,仍然会执行对应的 SQL

图1



SELECT * FROM `use_case_tags` WHERE `status`=1 ORDER BY `case_count` DESC LIMIT 30


3、在 Debug Bar 中,有 3 条日志。决定分析一下,是否表明缓存已经生效,只不过日志中仍然会有 SQL 语句罢了


yii\db\Command::query	SELECT * FROM use_case_tags WHERE status=1 ORDER BY case_count DESC LIMIT 30

yii\redis\Connection::executeCommand	Executing Redis Command: GET

yii\db\Command::query	Query result served from cache


4、在 Yii2 的 Debug Bar 中,即使 查询缓存已命中并返回缓存结果,仍然可能看到类似这样的日志条目。这是因为:Yii2 在执行 ->cache() 时,为了判断是否已经存在缓存,会构建并准备 SQL 命令对象。然后才检查。决定清除掉 Redis 中的所有 key,然后再次查看 Debug Bar,确定第 1 次是写入缓存,第 2 次是读取缓存,缓存是起效的。如图2、图3
在 Yii2 的 Debug Bar 中,即使 查询缓存已命中并返回缓存结果,仍然可能看到类似这样的日志条目

图2

决定清除掉 Redis 中的所有 key,然后再次查看 Debug Bar,确定第 1 次是写入缓存,第 2 次是读取缓存,缓存是起效的

图3



Saved query result in cache

Query result served from cache


]]>
https://www.shuijingwanwq.com/2025/07/07/9198/feed/ 0
在 Yii2 中执行数据库迁移时报错:Exception ‘yii\di\NotInstantiableException’ with message ‘Failed to instantiate component or class “m220810_154914_create_table_access_tokens”.’ https://www.shuijingwanwq.com/2025/07/04/9189/ https://www.shuijingwanwq.com/2025/07/04/9189/#respond Fri, 04 Jul 2025 03:36:08 +0000 https://www.shuijingwanwq.com/?p=9189 Post Views: 93

1、在 Yii2 中执行数据库迁移时报错:Exception ‘yii\di\NotInstantiableException’ with message ‘Failed to instantiate component or class “m220810_154914_create_table_access_tokens”.’。如图1

在 Yii2 中执行数据库迁移时报错:Exception 'yii\di\NotInstantiableException' with message 'Failed to instantiate component or class "m220810_154914_create_table_access_tokens".'

图1


PS C:\wwwroot\object\src> ./yii migrate
Yii Migration Tool (based on Yii v2.0.49.4)

Total 1 new migration to be applied:
        m220810_154914_create_table_access_tokens

Apply the above migration? (yes|no) [no]:yes
*** applying m220810_154914_create_table_access_tokens
Exception 'yii\di\NotInstantiableException' with message 'Failed to instantiate component or class "m220810_154914_create_table_access_tokens".'

in C:\wwwroot\object\src\vendor\yiisoft\yii2\di\Container.php:509

Caused by: Exception 'ReflectionException' with message 'Class m220810_154914_create_table_access_tokens does not exist'

in C:\wwwroot\object\src\vendor\yiisoft\yii2\di\Container.php:507

Stack trace:
#0 C:\wwwroot\object\src\vendor\yiisoft\yii2\di\Container.php(507): ReflectionClass->__construct('m220810_154914_...')
#1 C:\wwwroot\object\src\vendor\yiisoft\yii2\di\Container.php(385): yii\di\Container->getDependencies('m220810_154914_...')
#2 C:\wwwroot\object\src\vendor\yiisoft\yii2\di\Container.php(170): yii\di\Container->build('m220810_154914_...', Array, Array)
#3 C:\wwwroot\object\src\vendor\yiisoft\yii2\BaseYii.php(365): yii\di\Container->get('m220810_154914_...', Array, Array)
#4 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\controllers\MigrateController.php(202): yii\BaseYii::createObject(Array)
#5 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\controllers\BaseMigrateController.php(757): yii\console\controllers\MigrateController->createMigration('m220810_154914_...')
#6 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\controllers\BaseMigrateController.php(216): yii\console\controllers\BaseMigrateController->migrateUp('m220810_154914_...')
#7 [internal function]: yii\console\controllers\BaseMigrateController->actionUp(0)
#8 C:\wwwroot\object\src\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#9 C:\wwwroot\object\src\vendor\yiisoft\yii2\base\Controller.php(178): yii\base\InlineAction->runWithParams(Array)
#10 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\Controller.php(180): yii\base\Controller->runAction('', Array)
#11 C:\wwwroot\object\src\vendor\yiisoft\yii2\base\Module.php(552): yii\console\Controller->runAction('', Array)
#12 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\Application.php(180): yii\base\Module->runAction('migrate', Array)
#13 C:\wwwroot\object\src\vendor\yiisoft\yii2\console\Application.php(147): yii\console\Application->runAction('migrate', Array)
#14 C:\wwwroot\object\src\vendor\yiisoft\yii2\base\Application.php(384): yii\console\Application->handleRequest(Object(yii\console\Request))
#15 C:\wwwroot\object\src\yii(23): yii\base\Application->run()
#16 {main}


2、这个迁移文件是从别的项目中复制过来的。文件内容如下


createTable('access_tokens', [
            'id' => $this->string(20),
            'user_id' => $this->string(20),
            'access_token' => $this->string(200)->unique(),
            'ip' => $this->string(128)->defaultValue(''),

            'created_at' => $this->dateTime(),
            'updated_at' => $this->dateTime(),
        ], 'row_format=DYNAMIC');
        $this->addPrimaryKey('pk-id', 'access_tokens', 'id');
        $this->createIndex('idx-uid', 'access_tokens', 'user_id');

    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        echo "m220810_154914_create_table_access_tokens cannot be reverted.\n";

        return false;
    }

    /*
    // Use up()/down() to run migration code without a transaction.
    public function up()
    {

    }

    public function down()
    {
        echo "m220810_154914_create_table_access_tokens cannot be reverted.\n";

        return false;
    }
    */
}



3、暂时没有找到具体的原因,决定新建一个迁移文件,然后复制文件中的迁移实现。执行命令:./yii migrate/create create_access_tokens_table。执行迁移,未报错。后续才找到原因,删除掉 namespace console\migrations; 后就可以正常执行迁移。


PS C:\wwwroot\object\src> ./yii migrate/create create_access_tokens_table
Yii Migration Tool (based on Yii v2.0.49.4)

Create new migration 'C:\wwwroot\object\src\console/migrations\m250515_031604_create_access_tokens_table.php'? (yes|no) [no]:yes
New migration created successfully.
PS C:\wwwroot\object\src> ./yii migrate
Yii Migration Tool (based on Yii v2.0.49.4)

Total 1 new migration to be applied:
        m250515_031604_create_access_tokens_table

Apply the above migration? (yes|no) [no]:yes
*** applying m250515_031604_create_access_tokens_table
    > create table access_tokens ... done (time: 0.028s)
    > add primary key pk-id on access_tokens (id) ... done (time: 0.044s)
    > create index idx-uid on access_tokens (user_id) ... done (time: 0.038s)
*** applied m250515_031604_create_access_tokens_table (time: 0.122s)


1 migration was applied.

Migrated up successfully.


]]>
https://www.shuijingwanwq.com/2025/07/04/9189/feed/ 0
在 Yii2 中,一个 update 接口,如果在执行 save() 之前,表中的记录已经被删除,则仍然会返回 ture,是否需要避免此种情况 https://www.shuijingwanwq.com/2025/06/20/9159/ https://www.shuijingwanwq.com/2025/06/20/9159/#respond Fri, 20 Jun 2025 11:39:55 +0000 save(false)]]> https://www.shuijingwanwq.com/?p=9159 Post Views: 81 1、在 Yii2 中,一个 update 接口,如果在执行 save() 之前,表中的记录已经被删除,则仍然会返回 ture,是否需要避免此种情况。如图1
在 Yii2 中,一个 update 接口,如果在执行 save() 之前,表中的记录已经被删除,则仍然会返回 ture,是否需要避免此种情况

图1

2、代码实现如下


$animation = InvitationPageElementAnimation::find()->where([
	'id' => $id,
])->with(['invitation'])->limit(1)->one();


$executeRes = Yii::$app->db->createCommand('DELETE FROM `invitation_page_element_animations` WHERE id=:id')
	->bindValue(':id', $id)
	->execute();
Yii::info(
	[
		$executeRes
	],
	'$executeRes'
);
try {
	$res = $animation->save(false);
	Yii::info(
		[
			$res
		],
		'$res'
	);
	if (!$res) {
		return [
			'code' => 10400,
			'message' => '设置动画属性失败',
		];
	}

	return [
		'code' => 10000,
		'message' => '设置动画属性成功',
		'data' => $animation,
	];
} catch (Throwable $e) {
	Yii::error([
		'exception' => get_class($e),
		'message' => $e->getMessage(),
		'file' => $e->getFile(),
		'line' => $e->getLine(),
		'trace' => $e->getTraceAsString(),
	], 'application.model.save.exception');

	return [
		'code' => 10500,
		'message' => '数据库操作失败,请联系管理员',
	];
}


3、决定暂时不调整。]]>
https://www.shuijingwanwq.com/2025/06/20/9159/feed/ 0