文章评论表设计参考
评论表
id | article_id | parent_id(string) |
---|---|---|
1 | 3 | 0 |
2 | 3 | 1 |
3 | 3 | 1_2 |
4 | 3 | 2 |
5 | 3 | 1_2_3 |
模型操作
删除 id = 1 的1 级文章及下级所有文章
Article::where('id', 1)->delete();
Article::where('parent_id', 'like', '1%')->delete();
这样和 id = 1 的所有相关文章就删除了
删除这些文章下的评论就用观察者就行了
id | article_id | parent_id(string) |
---|---|---|
1 | 3 | 0 |
2 | 3 | 1 |
3 | 3 | 1_2 |
4 | 3 | 2 |
5 | 3 | 1_2_3 |
删除 id = 1 的1 级文章及下级所有文章
Article::where('id', 1)->delete();
Article::where('parent_id', 'like', '1%')->delete();
这样和 id = 1 的所有相关文章就删除了
删除这些文章下的评论就用观察者就行了
习惯将CDN文件下载到本地,保存至静态目录中,注意保留版本号。
<!-- 引入 css -->
<link href="https://cdn.jsdelivr.net/npm/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet">
<!-- 引入 js -->
<script src="https://cdn.jsdelivr.net/npm/@wangeditor/editor@latest/dist/index.min.js"></script>
直接上 HTML
代码吧:
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">正文:</label>
<div class="layui-input-block">
<textarea name="content" id="content" style="display: none"></textarea>
<div id="toolbar-container"></div>
<div id="editor-container" style="height: 300px;border:1px solid #dddddd"></div>
</div>
</div>
</div>
</div>
注意从官方拷贝js
代码时,要注意javascript与typescript的修改。
直接上 JS
代码。
// 渲染富文本编辑器
const { createEditor, createToolbar } = window.wangEditor;
// 编辑器配置
const editorConfig = {MENU_CONF: {}};
// 图片上传
editorConfig.MENU_CONF['uploadImage'] = {
// 表单字段名称
fieldName: 'file',
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 5 * 1024 * 1024, // 5M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 5,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 小于该值就插入 base64 格式(而不上传),默认为 0
base64LimitSize: 5 * 1024, // 5kb
// 自定义参数
meta: {
from: 'editor', //标志是从编辑器上传
},
// 上传图片的配置
server: '<?=route("upload.image")?>',
};
// 默认提示文字
editorConfig.placeholder = '请输入内容'
editorConfig.onChange = (editor) => {
// 当编辑器选区、内容变化时,即触发
//console.log('content', editor.children)
//console.log('html', editor.getHtml())
document.getElementById('content').value = editor.getHtml();
}
// 工具栏配置
const toolbarConfig = {}
// 创建编辑器
const editor = createEditor({
html: document.getElementById('content').value,
selector: '#editor-container',
config: editorConfig,
mode: 'simple' // 或 'simple' 参考下文
})
// 创建工具栏
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'simple' // 或 'simple' 参考下文
})
截图如下。
laravel
自带中间件说明:Authenticate
用户身份验证和重定向
EncryptCookies cookie
的加解密处理和验证
PreventRequestsDuringMaintenance
维护模式
RedirectIfAuthenticated
注册、登录、忘记密码时如果已经登录则跳转
TrimStrings
前后空白字符清理
TrustHosts
配置了受信任主机的白名单
TrustProxies
配置可信代理
VerifyCsrfToken
处理 CSRF 验证
SubstituteBindings
路由参数模型绑定中间件
ThrottleRequests
接口请求频率限制中间件
PHP Fileinfo extension must be installed/enabled to use Intervention Image.
必须安装扩展 fileInfo
切记。
安装扩展 opcache
切记。php_fileinfo, php_mbstring, php_openssl,
删除被禁用的proc_open
函数。
备份设置:'dump_binary_path' => '/usr/bin/', //BT宝塔
'dump_binary_path' => 'D:\phpstudyV81\Extensions\MySQL5.7.26\bin', //win
权限目录:chmod -R 777 bootstrap/
chmod -R 777 storage/
chmod -R 777 public/
Linux:whereis mysql
查询mysql
路径。
参考:https://www.jianshu.com/p/c692761bbd0d
nwidart/laravel-modules
https://www.cnblogs.com/lyzg/p/6181055.html
官网例子:https://tcpdf.org/examples
github: https://github.com/tecnickcom/tcpdf
packagist: https://packagist.org/packages/tecnickcom/tcpdf
之前还看了fpdf
类库,支持中文不是很方便,需要一个中文扩展程序,所以就放弃它了。
今天tcpdf
是主角,先看一下它的特型。
TCPDF
具有以下特性:
1、支持页面页脚;
2、支持HTML
标签代码;
3、支持jpg/png/gif/svg
图形图像;
4、支持表格;
5、支持中文字符;(有些PDF
类不支持中文或者处理中文相当麻烦)
6、自动分页,自动页码,等等。
如何用Laravel
生成pdf
文档?
由于开发工作用的是Laravel
,所以首先需要引入类库,如下:
composer require tecnickcom/tcpdf
$pdf = new \TCPDF();
// 设置文档信息
$pdf->SetCreator('懒人开发网');
$pdf->SetAuthor('懒人开发网');
$pdf->SetTitle('TCPDF示例');
$pdf->SetSubject('TCPDF示例');
$pdf->SetKeywords('TCPDF, PDF, PHP');
// 设置页眉和页脚信息
$pdf->SetHeaderData('tcpdf_logo.jpg', 30, 'LanRenKaiFA.com', '学会偷懒,并懒出效率!', [0, 64, 255], [0, 64, 128]);
$pdf->setFooterData([0, 64, 0], [0, 64, 128]);
// 设置页眉和页脚字体
$pdf->setHeaderFont(['stsongstdlight', '', '10']);
$pdf->setFooterFont(['helvetica', '', '8']);
// 设置默认等宽字体
$pdf->SetDefaultMonospacedFont('courier');
// 设置间距
$pdf->SetMargins(15, 15, 15);//页面间隔
$pdf->SetHeaderMargin(5);//页眉top间隔
$pdf->SetFooterMargin(10);//页脚bottom间隔
// 设置分页
$pdf->SetAutoPageBreak(true, 25);
// set default font subsetting mode
$pdf->setFontSubsetting(true);
//设置字体 stsongstdlight支持中文
$pdf->SetFont('stsongstdlight', '', 14);
//第一页
$pdf->AddPage();
$pdf->writeHTML('<div style="text-align: center"><h1>第一页内容</h1></div>');
$pdf->writeHTML('<p>我是第一行内容</p>');
$pdf->writeHTML('<p style="color: red">我是第二行内容</p>');
$pdf->writeHTML('<p>我是第三行内容</p>');
$pdf->Ln(5);//换行符
$pdf->writeHTML('<p><a href="http://www.lanrenkaifa.com/" title="">懒人开发网</a></p>');
//第二页
$pdf->AddPage();
$pdf->writeHTML('<h1>第二页内容</h1>');
//输出PDF
$pdf->Output('t.pdf', 'I');//I输出、D下载、 F保存到服务器
https://www.jianshu.com/p/4d68aefa820e
App\Exceptions\Handler.php
中重写两个方法如下:
/**
* Convert an authentication exception into a response.
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Auth\AuthenticationException $exception
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest($this->redirectTo($exception->guards()) ?? route('login'));
}
/**
* 重定向跳转至会员中心
* @param array $guardArr
* @return string
*/
protected function redirectTo($guardArr = [])
{
if(in_array('member',$guardArr)){
return route('member.login');
}
}
其中关键点是拿到guards()
里的门卫名称,根据此来判断:$exception->guards()
得到数据类似['admin','user']
再据此判断。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>开户注册</title>
</head>
<link rel="stylesheet" href="/layui/css/layui.css" media="all">
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<style>
#captcha{
float: left;
}
#code{
float: left;
}
#text{
line-height:20px;
}
</style>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>开户注册</legend>
</fieldset>
<form class="layui-form" action="save" method="post">
<div class="layui-form-item">
<label class="layui-form-label">开户银行</label>
<div class="layui-input-block">
<input type="text" name="bank" lay-verify="required|bank" autocomplete="off" placeholder="请输入银行" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">开户账号</label>
<div class="layui-input-block">
<input type="text" name="accout" lay-verify="required|number" autocomplete="off" placeholder="请输入账号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">真实姓名</label>
<div class="layui-input-block">
<input type="text" name="name" lay-verify="required|name" lay-reqtext="用户名是必填项,岂能为空?" placeholder="请输入真实姓名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">身份证号码</label>
<div class="layui-input-block">
<input type="text" name="idcard" lay-verify="required|identity" placeholder="请输入身份证号码" autocomplete="off" class="layui-input">
</div>
</div>
<br>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">图形验证码</label>
<div class="layui-input-inline">
<input type="tel" name="captcha" lay-verify="required" placeholder="请输入验证码" autocomplete="off" class="layui-input">
</div>
<span><div id="captcha">{:captcha_img()}</div></span><span id="text"><a href="">看不清楚!点击图片换一张?</a></span>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">手机号</label>
<div class="layui-input-inline">
<input type="tel" id="phone" name="phone" lay-verify="required|phone" placeholder="请输入手机号" autocomplete="off" class="layui-input">
</div>
<input type="button" id="btn" value="免费获取验证码" class="layui-btn" onclick="settime(this)" />
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">短信验证码</label>
<div class="layui-input-inline">
<input type="tel" id="code" name="code" lay-verify="required|code" placeholder="请输入接收的验证码" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<br><br>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="submit" class="layui-btn" lay-submit="save" >立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</body>
</html>
<script src="/layui/layui.js" charset="utf-8"></script>
<script>
layui.use(['form', 'layedit', 'laydate'], function(){
var form = layui.form
,layer = layui.layer
,layedit = layui.layedit
,laydate = layui.laydate;
//日期
laydate.render({
elem: '#date'
});
laydate.render({
elem: '#date1'
});
//创建一个编辑器
var editIndex = layedit.build('LAY_demo_editor');
//自定义验证规则
form.verify({
title: function(value){
if(value.length < 5){
return '银行名称至少得5个字';
}
},
accout: function(value){
if(value.length < 5){
return '账户至少得5个字符啊';
}
},
code: function(value){
if(value.length < 3){
return '验证码格式不正确';
}
},
name: function(value) {
if(!new RegExp("^[\u4e00-\u9fa5\]+$").test(value)){
return '姓名只能是中文';
}
},
bank: function(value) {
if(!new RegExp("^[\u4e00-\u9fa5\]+$").test(value)){
return '银行只能是中文';
}
}
,pass: [
/^[\S]{6,12}$/
,'密码必须6到12位,且不能出现空格'
]
,content: function(value){
layedit.sync(editIndex);
}
});
//监听提交
form.on('submit(demo1)', function(data){
layer.alert(JSON.stringify(data.field), {
title: '最终的提交信息'
})
return false;
});
//表单取值
layui.$('#LAY-component-form-getval').on('click', function(){
var data = form.val('example');
alert(JSON.stringify(data));
});
});
</script>
<script type="text/javascript">
var countdown=60;
function settime(val) {
if (countdown == 0) {
val.removeAttribute("disabled");
val.value="免费获取验证码";
countdown = 60;
} else {
val.setAttribute("disabled", true);
val.value="重新发送(" + countdown + ")";
countdown--;
setTimeout(function() {
settime(val)
},1000)
}
}
layui.use('layer', function(){
var layer = layui.layer;
$(function (){
//获取手机号 发送验证码
$('#btn').click(function (){
let phone=$('#phone').val();
$.post('/api/send',{phone:phone},function (res){
console.log(res.msg)
if (res.code==200){
layer.msg(res.msg);
alert(res.msg)
}
})
})
})
});
</script>
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
*/
public function save(Request $request)
{
$data=$request->param();
$validate = \think\facade\Validate::rule([
'phone|手机号' => 'require|mobile',
'captcha|验证码'=>'require|captcha',
'name|真实姓名'=>'require|chs',
'bank|银行名称'=>'require|chs',
'code|短信验证码'=>'require|alphaDash',
'idcard|身份证'=>'require|idCard',
]);
//验证参数
if (!$validate->check($data)) {
die($validate->getError());
}
}
本文转自:https://blog.csdn.net/array_sum/article/details/117608907
悲观锁性能很差,我目前用的是乐观锁,但是用的是 `User::where (‘id’, $user->id)->where (‘version’, $user->version)->update ([]);`
这种方式感觉不是很优雅,看手册,发现有一个原子锁,`Cache::lock ()` 这个方案你们觉得怎么样?
$out_purse = null;
$out_lock = Cache::lock('EBank@_transfer:' . $out_purse_id);
try {
$out_purse = $out_lock->block(50, function () use ($out_purse_id, $amount) {
$var = FundPurse::where(['id' => $out_purse_id, 'status' => 1])->where('balance', '>=', $amount)->decrement('balance', $amount);
// 未修改返回修改行数为0
if (!$var) {
return false;
}
return FundPurse::find($out_purse_id);
});
optional($out_lock)->release();
} catch (LockTimeoutException $e) {
} finally {
optional($out_lock)->release();
}
if ($out_purse === null) {
abort(422, '转出钱包查询超时');
}
if ($out_purse === false) {
abort(422, '转出钱包扣款失败,余额不足或账户被禁用');
}
转自:https://learnku.com/laravel/t/61510