YII2 框架 高级模板 学习笔记
路由
- 使用
<?= \yii\helpers\Url::toRoute('') ?>
进行路由的跳转 - 需要进行参数的传递:
"<?= \yii\helpers\Url::toRoute(['send', 'uid' => 'id']) ?
- ajax请求URL传参:
"<?= \yii\helpers\Url::toRoute(['send', 'uid' => '']) ?>" + uid, // 在URL中添加uid参数
增删改查
路由配置
- 首先在
header.php
文件中的全局变量bootUrl
进行url地址的配置
表单验证常用规则
高级专题(Special Topics): 核心验证器(Core Validators) | Yii 2.0 权威指南 | Yii PHP Framework (yiiframework.com)
Yii::
是 Yii2 框架中的命名空间,表示全局命名空间。在 PHP 中,::
是一个静态操作符,称为范围解析操作符,用于访问类的静态属性、常量和静态方法,如 ClassName::staticMethod()
。
而 Yii::$app
表示访问 yii\web\Application
类型对象的静态属性 $app
。通常在 Yii2 中,我们会把应用程序实例赋值给 Yii::$app
属性,以便在后续代码中使用该应用程序实例的各种组件。
总之, Yii::$app->user->login()
是访问 Yii2 应用程序组件的一种方式,而 Yii::
和 ::
是用于访问类和静态成员的 PHP 中的操作符。
设置的树形分类弹窗
实现方式
- 弹窗视图
<--该组件的作用是让用户从一个商品分类树中选择一个分类,并将所选分类的ID和名称存储在表单中。在js中,使用`$('#treeId').val()`和`$('#treeName').val()`来获取所选分类的ID和名称。-->
<div class="form-group">
<label class="col-sm-3 control-label is-required">商品分类:</label>
<div class="col-sm-8">
<div class="input-group">
<input type="hidden" id="treeId" name="cat_id" value="">
<input type="text" id="treeName" value="" class="form-control" placeholder="请选择商品分类" readonly autocomplete="off" />
<span class="input-group-addon"><i class="fa fa-search"></i></span>
</div>
</div>
</div>
// 当用户点击商品分类选择器时,将打开一个模态框,显示一个商品分类树。
$(function() {
$("#treeName").click(function() {
var treeId = $("#treeId").val(); // 获取隐藏的输入控件`#treeId`的值,并将其存储在变量`treeId`中。
var url = ("<?= \yii\helpers\Url::toRoute('tree') ?>"); //生成一个URL,用于加载商品分类树的数据。`Url::toRoute()`方法用于生成一个相对于应用程序根目录的URL,指向名为`tree`的操作。
var options = {
title: '商品分类选择', //设置模态框的标题为“商品分类选择”
width: "380", //设置模态框的宽度为380像素。
url: url, //设置模态框的URL为上面生成的URL。
callBack: doSubmit //设置模态框关闭时要执行的回调函数为`doSubmit`。
};
$.modal.openOptions(options); //打开一个模态框,使用上面配置的选项。
});
});
- 树形视图
<!-- 该行代码用于设置当前视图页面的布局文件为/form,即使用名为form.php的布局文件来渲染该页面。 -->
<?php $this->context->layout = '/form'; ?>
<!-- 该行代码用于引入一个名为Asset的widget组件,并传入一个名为type的参数数组,其中包含一个名为ztree的元素。这个组件会自动加载ztree相关的静态资源,使树形结构选择器能够正常显示。ztree插件的初始化参数可以通过传递不同的选项对象进行配置。 -->
<?= \backend\extend\widgets\Asset::widget(['type' => ['ztree']]) ?>
<style>
body {
height: auto;
font-family: "Microsoft YaHei";
background-color: #fff !important;
}
button {
font-family: "SimSun", "Helvetica Neue", Helvetica, Arial;
}
</style>
<!-- 该行代码定义了一个隐藏的input元素,其名称为treeId,ID为treeId,并且将前端传递过来的变量$struct_id作为其值。该元素用于保存当前选中节点的编号。 -->
<input type="hidden" name='treeId' id='treeId' value='<?= $struct_id ?>'>
<!-- 该行代码定义了一个隐藏的input元素,其名称为treeName,ID为treeName,初始值为空。该元素用于保存当前选中节点的名称。 -->
<input type="hidden" name='treeName' id='treeName' value=''>
<!-- 该部分代码<div class="wrapper">包含了整个树形结构选择器的核心代码,包括了搜索按钮、展开和折叠按钮以及显示树形结构的容器。其中,类名为treeselect的DIV元素最终会被ztree插件实例化成一个树形结构的DOM元素。 -->
<div class="wrapper">
<div class="treeShowHideButton" onclick="$.tree.toggleSearch();">
<label id="btnShow" title="显示搜索" style="display:none;">︾</label>
<label id="btnHide" title="隐藏搜索">︽</label>
</div>
<div class="treeSearchInput" id="search">
<label for="keyword">关键字:</label><input type="text" class="empty" id="keyword" maxlength="50">
<button class="btn" id="btn" onclick="$.tree.searchNode()"> 搜索</button>
</div>
<div class="treeExpandCollapse">
<a href="javascript:;" onclick="$.tree.expand()">展开</a> /
<a href="javascript:;" onclick="$.tree.collapse()">折叠</a>
<?php if ($ismult == '1') : ?>
/<a href="javascript:;" onclick="$._tree.checkAllNodes(false);$.tree.zOnCheck();">取消选择</a>
<?php endif; ?>
</div>
<div id="tree" class="ztree treeselect"></div>
</div>
<?php $this->beginBlock('script'); ?>
<script>
var parent = "<?= $parent ?>";
var ismult = "<?= $ismult ?? '' ?>";
$(function() {
console.log(aUrl);
var options = {
url: aUrl,
showParentLevel: parent == '1' ? 0 : false,
ismult: (ismult == '1') ? true : false,
expandLevel: 2
};
$.tree.init(options);
});
</script>
<?php $this->endBlock(); ?>
- 控制器
/**
* 获取所有菜单,用于树形组件使用
* @return array
*/
public function getList(): array
{
$list = Classify::find()->select(['id', 'pid', 'name'])->orderBy('pid asc,id asc')->asArray()->all();
// 定义$map数组,用于存储节点的键值对信息,其中键为节点的id,值为节点的信息和其子节点信息
$map = [];
// 定义$root数组,用于存储根节点的子节点信息
$root = [];
// 加上 & 符号可以将一个变量声明为引用变量。引用变量是指向内存中同一个变量的不同名称,它们共享同一个内存地址,因此对其中一个变量的修改会影响到其他变量。
// &$item 是一个引用变量,表示将 $item 变量的引用传递给数组中的元素,而不是将 $item 变量的值复制到数组中的元素。这样做的好处是,当修改数组中的元素时,原始变量 $item 的值也会被修改,
foreach ($list as &$item) {
// 为节点添加一个空的children数组,用于存储其子节点信息。
$item['children'] = [];
// 将节点加入到$map数组中,以节点的id为键,节点信息和子节点信息为值。
$map[$item['id']] = &$item;
// 如果节点的pid为0,则将其加入到$root数组中。
if ($item['pid'] == 0) {
$root[] = &$item;
} else {
// 否则,将其加入到其父节点的children数组中。
$map[$item['pid']]['children'][] = &$item;
}
}
return $root;
}
/**
* 树形页面
* @return array|string
*/
public function actionTree()
{
if ($this->request->isPost) {
$list = $this->getList();
return $this->success('', $list);
} else { //是否显示父级名称
// * 父节点的名称
$parent = $this->request->get('parent', 0);
// * 树节点的id
$id = $this->request->get('id', 0);
// * 多选
$ismult = $this->request->get('ismult', 0);
$list = $this->getList();
return $this->render('', ['struct_id' => $id, 'parent' => $parent, 'ismult' => $ismult]);
}
}
注意:以上操作适用于添加,使用编辑跟详情的时候,调用对应的组件,应在控制器里进行对应数据的查询,再进行赋值渲染当前分类选项
/**
* 编辑页渲染
* @param array $info 获取到的对应信息
* @return string
*/
protected function editRender(array $info): string
{
$struct_id = Classify::find()->select('id')->where(['id' => $info['cat_id']])->scalar();
$struct_name = Classify::find()->select('name')->where(['id' => $info['cat_id']])->scalar();
return $this->render('', ['info' => $info, 'struct_id' => $struct_id, 'struct_name' => $struct_name]);
}
导入EXCEL
- 视图
<!-- 对上传文件的类型进行限制,只允许上传excel文件,可以在input元素中添加accept属性,并设置为".xlsx, .xls" -->
<input type="file" id="inputExcel" style="display: none" accept=".xlsx, .xls">
<a class="btn btn-warning" id="inputExcelBtn"> <i class="fa fa-file-excel-o"></i> 导入EXCEL</a>
$('#inputExcelBtn').on('click', function() {
// 打开文件选择器
$('#inputExcel').click();
});
// 监听文件选择器的 change 事件,在选择文件后立即触发上传操作
$('#inputExcel').on('change', function() {
var file = this.files[0];
var formData = new FormData();
formData.append('file', file);
// 调用上传方法,传递 formData 对象和其他参数
$.ajax({
//当前控制器的方法
url: "<?= \yii\helpers\Url::toRoute('import') ?>",
data: formData,
type: 'POST',
contentType: false,
processData: false,
dataType: 'json',
cache: false,
success: function(res) {
// 处理上传成功的回调
history.go(0);
alert(res.msg);
},
error: function(err) {
// 处理上传失败的回调
console.error('Upload error:', err);
}
});
})
2、控制器
/**
* 导入excel
*/
public function actionImport()
{
// 判断当前请求是否为POST请求,并且检查是否存在名为file的上传文件。如果条件成立,则继续执行代码;否则返回错误信息
if (Yii::$app->request->isPost && isset($_FILES['file']['tmp_name'])) {
// 生成一个唯一的文件名,使用当前时间戳和一个随机数进行MD5加密,最后加上后缀名.xlsx。
$fileName = md5(microtime(true) . mt_rand(1000, 9999)) . '.xlsx';
// 获取应用程序根目录下的@backend/web/assets目录,并将其赋值给变量$root。
$root = Yii::getAlias('@backend/web/assets/');
// 生成一个以当前日期为名称的子目录,用于存放上传的Excel文件。
$savePath = 'importExcel/' . date('Ymd') . '/';
// 将上传文件的路径设置为$root与$savePath的拼接结果。
$path = $root . $savePath;
//将路径中的斜杆替换为操作系统对应的分隔符,以确保路径在各个操作系统之间的兼容性
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
// is_dir查上传文件的目录是否存在,如果不存在则创建该目录。
if (!is_dir($path)) {
// mkdir($path, 0777, true) 该方法创建名为$path的目录,并赋予最大的权限
if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
throw new HttpException(500, '存储文件夹创建失败:' . $path);
}
}
// 组合出完整的文件路径。
$filePath = $path . $fileName;
// 将上传的Excel文件保存到服务器上指定的位置
if (move_uploaded_file($_FILES['file']['tmp_name'], $filePath)) {
// 加载Excel文件
$objPHPExcel = IOFactory::load($filePath);
// 获取第一个工作表
$worksheet = $objPHPExcel->getActiveSheet()->toArray(null, true, true, true);
// 获取A1单元格的值
// $value = $worksheet->getCell('A1')->getValue();
// 处理Excel文件内容并保存到数据库中
$transaction = Yii::$app->db->beginTransaction();
try {
foreach ($worksheet as $key => $value) {
// 跳过表头
if ($key == 1) continue;
// 跳过空行
if (empty($value['A'])) continue;
$model = new NewTestDemo();
$model->name = $value['A'];
// 当参数为false时,会禁用数据验证,直接保存数据到数据库中。而当参数为true时,会强制进行数据验证,
$model->save(false);
}
$transaction->commit();
return $this->success('文件上传成功');
} catch (\Exception $e) {
$transaction->rollBack();
return $this->error($e->getMessage());
}
} else {
return $this->error('文件上传失败');
}
} else {
return $this->error();
}
}
发送邮箱
邮箱配置
参考:www.jianshu.com/p/f6c6355f4…
//定义邮箱配置
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => 'common/mail', //根据实际情况配置
'useFileTransport' => false,
// 以下为 SMTP 发送邮件配置
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.qq.com', // 以下为 SMTP 发送邮件配置
'username' => 'U Email@qq.com', //改成自己的用户名密码
'password' => 'U password',
'port' => '465',
'encryption' => 'ssl',
],
'messageConfig' => [
'charset' => 'UTF-8',
'from' => ['U Email@qq.com' => 'U name']
],
],
-
发送邮件时,将生成的验证码插入到邮件内容中,并发送给用户。可以使用 Yii2 的邮件组件
yii\swiftmailer\Mailer
发送邮件,public function actionSend() { // 生成一个随机四位数的验证码 $code = rand(1000, 9999); // 将生成的验证码保存到缓存中,以邮箱地址作为键名,使用了文件缓存,默认的缓存文件夹路径是 @runtime/cache,可以通过文件管理器或命令行进入该目录查看相关缓存文件。 实际开发可以使用redis缓存进行配置 \Yii::$app->cache->set($userData['email'], $code, 300); // 有效时间为 300 秒(5分钟) // 将生成的验证码插入到邮件内容中,并发送给用户,使用 Yii2 的邮件组件 yii\swiftmailer\Mailer 发送邮件 $emailObj = \Yii::$app->mailer->compose(); ->setTo($email) // 收件人邮箱地址 ->setSubject('验证码') // 邮件主题 ->setTextBody("您的验证码是:{$code}") // 邮件正文 ->send(); // 发送邮件 }
-
用户输入收到的验证码后,从缓存中获取验证码并和用户输入的进行比对,以验证验证码是否正确。
$inputCode = Yii::$app->request->post('code'); $savedCode = Yii::$app->cache->get($email); if ($savedCode === false || $savedCode != $inputCode) { // 验证码不正确 } else { // 验证码正确 Yii::$app->cache->delete($email); // 验证成功后,可以删除缓存中的验证码 }
项目开发遇到的一些报错
列表图片不能正常显示
- 文件上传的配置文件在
common\helpers\UploadHelper.php
里面定义了图片视频等上传方法。 - 在上传操作的
_upload()
方法中,配置了文件路径等信息。 - 项目初始的时候,运行的是backend的web目录,而上传的文件安装在了网站的根目录里面了,@root_path表示根目录,运行的是web目录,所以图片是读取不到的,这里修改了一下路径,就能正常显示在页面中了。
转载自:https://juejin.cn/post/7232879267624861752