//快捷查询 Db::table('think_user') ->where('name|title','like','thinkphp%') ->where('create_time&update_time','>',0) ->find(); //sql: SELECT * FROM `think_user` WHERE ( `name` LIKE 'thinkphp%' OR `title` LIKE 'thinkphp%' ) AND ( `create_time` > 0 AND `update_time` > 0 ) LIMIT 1
->whereOr([//第一维代表多个条件 [//第二维之间用or链接 //第三维之间用and连接 ['create_time', '<=', time()], ['status', '=', '1'] ],[ ['id', '>=', 10], ['update_time', '>=', time()], ] ]) //拼凑出来的结果: //where (create_time <= time() and status = 1) //or (update_time >= time() and id >= 10)
适用于多对多,并且有中间表的,例如,不同用户关联不同的兴趣标签,关联数据单独存在一个表中:
建表:
article表(字段:id,……)
label表(字段:id,name,……)
article_label表(字段:id,article_id,label_id,createtime,……)
article模型中:
//方法名随意,调用时使用此方法名 public function Labels(){ //参数1:关联模型 //参数2:中间表表名,不带表前缀 //参数3:中间表 关联 关联模型的外键 //参数4:中间表 关联 当前模型的外键 return $this->belongsToMany('LabelModel', 'article_label','label_id','article_id'); }
控制器中使用:
//查询 $data=ArticleModel::with('Labels')->select(); //使用,因为查询结果为对象,所以调用方式如下: //打印第一条文章数据下第一个标签的名字 dump($data[0]->Labels[0]->name); //接口调用:直接用json_encode()处理后自动变成三维数组 echo json_encode($data); //删除 $data = ArticleModel::find(1);//查询文章信息 $data->Labels()->detach();//删除article_label表中当前文章的信息 //添加 $label_ids=[1,2,3,4,5,6,7,8,9];//需要绑定的label表主键id //参数1是需要绑定的多个label_id,参数2是其他需要补充的字段 $data->Labels()->attach($label_ids,['createtime'=> time()]);//保存
一般用于一条记录对应多条记录,例如某个文章的评论:
建表:
article表(字段:id,……)
comment表(字段:id,article_id,content,……)
article模型中:
//方法名字随意,调用时方法名作为参数使用 public function comments(){ //参数1:关联模型名 //参数2:关联模型外键 //参数3:当前模型表主键 return $this->hasMany('CommentModel','article_id','id'); }
控制器中使用:
$data=ArticleModel::with('comments')->select(); //使用,因为查询结果为对象,所以调用方式如下: //打印第一条文章数据下第一条评论数据的内容 dump($data[0]->comments[0]->content); //接口调用:直接用json_encode()处理后自动变成三维数组 echo json_encode($data);
一般用于多级分类存在同一个表中:
建表:
classify表(字段:id,name,level,pid,……)
classify模型中:
//方法名字随意,调用时方法名作为参数使用 public function secondClassify(){ //参数1:当前模型名 //参数2:上级id(原关联模型外键) //参数3:当前模型表主键 return $this->hasMany('ClassifyModel','pid','id'); }
控制器中使用:
//查询一级分类,自动补全二级 $data=ClassifyModel::with('secondClassify')->where('level',1)->select(); //使用,因为查询结果为对象,所以调用方式如下: //打印第一条一级分类数据下第一个二级分类的名称 dump($data[0]->secondClassify[0]->name); //接口调用:直接用json_encode()处理后自动变成三维数组 echo json_encode($data);
一般用于主记录查询子记录(子表有关联记录在主表),例如一个用户信息只对应一个用户:
建表:
user表(字段:id,message_id,……)
message表(字段:id,user_id,……)
user模型中:
//方法名字随意,调用时方法名作为参数使用 public function user(){ //参数1:关联模型名 //参数2:关联模型外键 //参数3:当前模型主键 return $this->hasOne('UserModel','message_id','id'); }
控制器中使用:
$data=UserModel::with('user')->select(); //使用,因为查询结果为对象,所以调用方式如下: //打印当前用户信息对应的用户生日 dump($data[0]->user->birthday); //接口调用:直接用json_encode()处理后自动变成三维数组 echo json_encode($data);
一般用于主记录查询子记录(子表有一条记录属于主表),例如一个用户只有一条用户信息:
建表:
user表(字段:id,message_id,……)
message表(字段:id,user_id,……)
classify模型中:
//方法名字随意,调用时方法名作为参数使用 public function messages(){ //参数1:关联模型名 //参数2:关联模型外键 //参数3:当前模型主键 return $this->belongsTo('MessageModel','message_id','id'); }
控制器中使用:
$data=UserModel::with('messages')->select(); //使用,因为查询结果为对象,所以调用方式如下: //打印第一条用户数据下子信息的金额 dump($data[0]->messages->money); //接口调用:直接用json_encode()处理后自动变成三维数组 echo json_encode($data);
//取单列:获取所有标签名,输出结构:[id=>name] LabelModel::column('name','id'); //取多列:获取所有标签名和创建时间,输出结构:[id=>[id,name,createtime]] LabelModel::where('status',1)->column('name,createtime','id');
cache()可以用于select、find、value和column方法,以及其衍生方法,使用cache方法后,在缓存有效期之内不会再次进行数据库查询操作,而是直接获取缓存中的数据,关于数据缓存的类型和设置可以参考缓存部分。
1. 简单的存储
//查询news表中id=10的新闻存储于cache中 //写true默认读取配置的中缓存时间 db('news')->cache(true)->find(10); //你也可以自定义时间,60秒表示: db('news')->cache(true,60)->find(10);
2. 指定缓存标识
//等同于使用缓存时的键,默认读取配置的中缓存时间: db('news')->cache('key')->find(15); //全局读取这条数据: $data = \think\Cahce::get('key');
3.支持设置缓存标签:
//缓存键:key,缓存时间:60秒,标签为:tagName db('news')->cache('key',60,'tagName')->find(15); //全局读取带标签的缓存: Db::name('news')->cache('news_list',60,'shx')->select(); $data = \think\Cache::tag('shx')->get('news_list');
QueryList是一套用于内容采集的PHP工具,它使用更加现代化的开发思想,语法简洁、优雅,可扩展性强。相比传统的使用晦涩的正则表达式来做采集,QueryList使用了更加强大而优雅的CSS选择器来做采集,大大降低了PHP做采集的门槛,同时也让采集代码易读易维护。
QueryList是直接获取的页面上的元素内容,而不是直接获取api中的内容。
网址:https://querylist.cc/,新版环境要求php>=7.1
composer安装命令:
composer require jaeger/querylis
如果上面的composer命令太慢,请使用国内镜像:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
说明:当时使用时是在tp5中测试使用的,所以下面的内容都是基于tp5进行举例的。
$ql=QueryList::get($src);
$data=$ql->find('div')->attr("id");//获取页面上一个div的id值
QueryList有个find()方法,用于采集单个元素,它通过jQuery选择器选择DOM元素,用法同jQuery的find()方法。
$ql=QueryList::get('https://www.iqshw.com/'); //获取第一张图片的链接地址,下面四种方法完全等价 $data[]=$ql->find('div')->attr('src'); $data[]=$ql->find('div')->src; $data[]=$ql->find('div:eq(0)')->src;//等推获取第n张:img:eq(n-1) $data[]=$ql->find('div')->eq(0)->src;//等推获取第n张:eq(n-1) //获取其他属性值 $data[]=$ql->find('div')->alt; //获取其它自定义属性值,只能获取一种 //获取所有属性值 $data[]=$ql->find('div')->attr("*"); //获取元素下的HTML内容 $data[]=$ql->find('#id')->html(); $data[]=$ql->find('#id .class')->html(); //获取其它自定义属性值,只能获取一种 //获取元素下的纯文本内容 $data[]=$ql->find('.class')->text();
获取多个元素的单个属性值(QueryList中凡是涉及到集合的地方返回的都是Collection集合对象,这个对象有个all()方法,用于把当前对象转成数组,所以你会发现下面很多写法都是$data->all() )
$ql=QueryList::get('https://www.iqshw.com/'); //获取所有图片的src属性值 $data[]=$ql->find('img')->map(function ($item){ return $item->src; })->all(); //等价与下面这句话 $data[]=$ql->find('img')->attrs('src')->all(); //获取元素中所有的html内容和text内容 $data[]=$ql->find('#id')->htmls()->all(); $data[]=$ql->find('.class')->texts()->all();
列表采集才是QueryList的核心功能,这里主要涉及到两个函数的用法:rules()和range()
$rules = [ '规则名1' => ['选择器1','元素属性'], '规则名2' => ['选择器2','元素属性'], // ... ]; //代码示例: $ql=QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules=[ //采集标题 'title'=>['.postTitle2','text'], //采集内容 'text'=>['.c_b_p_desc','text'], //采集时间 'time'=>['.dayTitle','text'] ]; $data=$ql->rules($rules)->query()->getData()->all(); //等同于上面 $data=$ql->rules($rules)->queryData();
用range()函数来配合rules()进行循环采集列表内容,range()函数的作用是选择一个元素作为多个数据之间的“切片”。
$ql=QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules=[ //采集标题 'title'=>['.postTitle2','text'], //采集内容 'text'=>['.c_b_p_desc','text'], //采集时间 'time'=>['.dayTitle','text'] ]; $range='.day';//切片选择器 $data=$ql->rules($rules)->range($range)->query()->getData()->all();
利用remove()来过滤不需要的内容:
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //移除选中html中的a标签 $data = $ql->find('.c_b_p_desc:eq(0)')->remove('a')->html(); //只保留a标签中的html内容 $data = $ql->find('.day:eq(0)')->find('a')->remove()->html();
列表采集时过滤不需要的元素,使用规则:
$rules = [ '规则名1' => ['选择器1','元素属性','内容过滤选择器'], '规则名2' => ['选择器2','元素属性','内容过滤选择器'], // ... ];
内容过滤选择器参数不光可以定义要移除的内容还可以定义要保留的内容,多个值之间用空格隔开, 有如下2条规则:
内容移除规则:选择器名前面添加减号(-),表示移除该标签以及标签内容。
内容保留规则:选择器名前面没有减号(-)(此时选择器只能为HTML标签名,不支持其他选择器), 当要采集的[元素属性] 值为text时表示需要保留的HTML标签以及内容,为html时表示要过滤掉的 HTML标签但保留内容。
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], //采集内容 'text' => ['.c_b_p_desc', 'html','-a'], //采集时间 'time' => ['.dayTitle', 'text'] ]; $range = '.day';//切片选择器 $data = $ql->rules($rules)->range($range)->query()->getData()->all();
获取到数据后进行二次处理:
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], //采集内容 'text' => ['.c_b_p_desc', 'html'], //采集时间 'time' => ['.dayTitle', 'text'] ]; $range = '.day';//切片选择器 $data = $ql->rules($rules)->range($range)->query()->getData(function($item){ $qls = QueryList::html($item['text']); $qls->find('a')->remove(); $item['content'] = $qls->find('')->html(); return $item; })->all();
$ql = QueryList::get('https://www.iqshw.com/'); //定义采集规则 $rules = [ //采集标题 'title' => ['a', 'text'], ]; $range = 'li';//切片选择器 //乱码处理,设置输出编码 $data = $ql->rules($rules)->range($range) ->encoding('UTF-8','GB2312') ->query()->getData()->all(); //如果设置输出参数无法解决乱码,那就使用removeHead()方法移除html头部 $data = $ql->rules($rules)->range($range) ->removeHead()->query()->getData()->all(); //或者 $data = $ql->rules($rules)->range($range) ->encoding('UTF-8','GB2312') ->removeHead()->query()->getData()->all(); //手动转码 $html=$text=iconv("GB2312","UTF-8",file_get_contents('https://www.iqshw.com/')); $data = QueryList::html($html)->rules([ 'title' => ['.news-comm-wrap a ', 'text'], ])->range($range)->query()->getData()->all();
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], ]; $range = '.day';//切片选择器 //使用flatten()方法将多维集合转为一维的 $data = $ql->rules($rules)->range($range)->query()->getData()->flatten()->all();
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], ]; $range = '.day';//切片选择器 //take()方法返回给定数量项目的新集合,对最初的采集结果data进行处理: $data = $ql->rules($rules)->range($range)->query()->getData()->take(2)->all();
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], ]; $range = '.day';//切片选择器 //使用reverse()来倒转集合中的项目 $data = $ql->rules($rules)->range($range)->query()->getData()->reverse()->all();
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], ]; $range = '.day';//切片选择器 //filter()方法用于按条件过滤数据,只保留满足条件的数据。 //只能过滤,不能修改添加原数据,如果需要修改添加则用getData $data = $ql->rules($rules)->range($range)->query()->getData() ->filter(function ($item){ return $item['title']!='PHP使用引用实现无限极分类'; })->all();
$ql = QueryList::get('https://www.cnblogs.com/yulongcode/'); //定义采集规则 $rules = [ //采集标题 'title' => ['.postTitle2', 'text'], ]; $range = '.day';//切片选择器 //map() 方法遍历集合并将每一个值传入给定的回调。 //该回调可以任意修改项目并返回,从而形成新的被修改过项目的集合。 $data = $ql->rules($rules)->range($range)->query()->getData() ->map(function ($item){ $item['time']=time(); return $item; })->all();
attr()方法除了获取DOM元素的值外,还有第二个参数,用于设置元素属性值
text()方法是获取元素的纯文本内容,加参数表示设置元素内容
html()方法是获取元素的HTML内容,加参数表示设置元素HTML内容
以上三个用法基本和jQuery相同,添加完后再获取,才会出现在获取的数据中。
append()追加元素
replaceWith()替换元素
removeAttr()移除元素属性
parent()用户获取当前元素的父级元素
next()和prev()用于获取当前元素临近的下一个元素和上一个元素
更多使用说明请查看使用文档。
直接在网站运行目录的.htaccess文件中输入以下内容:
Apache:
<IfModule mod_rewrite.c> RewriteEngine on RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L] </IfModule>
Nginx:
location / { if (!-e $request_filename){ rewrite ^(.*)$ /index.php?s=$1 last; break; } }
/** * 判断两个时间之间相差的月份 * @param $time1 * @param $time2 * @return float|int */ function get_month_interval($time1, $time2) { if($time1>$time2){ $t=$time1; $time1=$time2; $time2=$t; } $y1=date('Y',$time1); $y2=date('Y',$time2); $m1=date('m',$time1); $m2=date('m',$time2); $months=($y2-$y1)*12+($m2-$m1); return $months; }
在vendor\topthink下执行composer命令,默认安装最新发送电子邮件的插件包:
composer require phpmailer/phpmailer
安装图例:
引入方式:
use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception;
使用方法:
$mail = new PHPMailer(true);// Passing `true` enables exceptions try { //服务器配置 $mail->CharSet = "UTF-8";//设定邮件编码 $mail->SMTPDebug = 0;// 调试模式输出 $mail->isSMTP();// 使用SMTP $mail->Host = 'smtp.qq.com';// SMTP服务器 $mail->SMTPAuth = true;// 允许 SMTP 认证 $mail->Username = '*******@qq.com';// SMTP 用户名 即邮箱的用户名 $mail->Password = '**********';// SMTP 密码 部分邮箱是授权码(例如163邮箱) $mail->SMTPSecure = 'ssl';// 允许 TLS 或者ssl协议 $mail->Port = 465;// 服务器端口 25 或者465 具体要看邮箱服务器支持 $mail->setFrom('******@qq.com', 'shx');//发件人 $mail->addAddress($email, 'Joe');// 收件人 //$mail->addAddress('ellen@example.com');// 可添加多个收件人 //$mail->addReplyTo('xxxx@163.com', 'info');//回复的时候回复给哪个邮箱 建议和发件人一致 //$mail->addCC('cc@example.com');//抄送 //$mail->addBCC('bcc@example.com');//密送 //发送附件 // $mail->addAttachment('../xy.zip');// 添加附件 // $mail->addAttachment('../thumb-1.jpg', 'new.jpg');// 发送附件并且重命名 //Content $mail->isHTML(true);// 是否以HTML文档格式发送 发送后客户端可直接显示对应HTML内容 $mail->Subject = '我是邮件标题'; $mail->Body = '我是邮件内容'; $mail->AltBody = '如果邮件客户端不支持HTML则显示此内容'; $mail->send(); return '邮件发送成功'; } catch (Exception $e) { return '邮件发送失败: ' . $mail->ErrorInfo; }
本人用的是QQ邮箱的相关配置,所以这里申请方式就以QQ邮箱为例。
登入QQ邮箱。
进入设置,点击账户,向下翻,开启POP3/SMTP服务。
记录一下授权码,就是代码中的SMTP密码,无法复看,一旦忘记就只能重新申请!
SMTP Error: Could not authenticate.错误怎么解决?
网上大多数说的解决方案是:替换某个函数或者开启php.ini的某些功能。我个人曾多次出现此情况,前面的两个修改方案从未成功解决过错误,也可能我的问题和他们的报错原因不一样。本人多次入坑,强烈建议直接重新申请一次授权码就可以了,简单快捷!
这个错误我发现只要我配置好以后隔段时间如果不去使用它,可能就会产生这个错误,我的代码和配置从没动过,但是隔一段时间仍旧失效,所以我怀疑QQ邮箱这边可能有一定验证机制,长时间不使用就会出现类似于注销的情况。