thinkphp中where()和whereOr()有很多种用法,组合成不同的SQL语句,有些常用的可能都了解,但对于一些特殊的,比如一个字段多种条件、一个条件查询多个字段等。
//最简单的实用方法,多条件并列查询,基本形式:['字段名1'=>['SQL运算符','值'],'字段名1'=>['SQL运算符','值'],……]
$where = ['name' => ['like', '%国%'], 'age' => ['>=', 18]];
Db::table('user')
->where($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE `name` LIKE '%国%' AND `age` >= 18 LIMIT 1//特殊用法(|和&),多个字段用同一个条件查询,并且用and或or连接
//or(|)的用法
$where = ['name|address' => ['like', '%国%']];
Db::table('user')
->where($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE ( `name` LIKE '%国%' OR `address` LIKE '%国%' ) LIMIT 1
//and(&)的用法
$where = ['name&address' => ['like', '%国%']];
Db::table('user')
->where($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE ( `name` LIKE '%国%' AND `address` LIKE '%国%' ) LIMIT 1//复杂查询,一个字段同时满足多种查询条件
$where = ['name' => [['like', '%国%'],['like', '%天%'],'or']];
Db::table('user')
->where($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE ( `name` LIKE '%国%' or `name` LIKE '%天%' ) LIMIT 1
//其中如果or不写默认为and连接,或者这里也可以写上and
$where = ['name' => [['like', '%国%'],['like', '%天%'],'and']];
Db::table('user')
->where($where)
->find();
where = ['name' => [['like', '%国%'],['like', '%天%']]];
Db::table('user')
->where($where)
->find();
//以上两种方式生成的SQL语句相同
SELECT * FROM `user` WHERE ( `name` LIKE '%国%' AND `name` LIKE '%天%' ) LIMIT 1//最简单的实用方法,多条件or查询,基本形式:['字段名1'=>['SQL运算符','值'],'字段名1'=>['SQL运算符','值'],……]
$where = ['name' => ['like', '%国%'], 'age' => ['>=', 18]];
Db::table('user')
->where($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE `name` LIKE '%国%' OR `age` >= 18 LIMIT 1//复杂用法,在whereOr中使用and,并且多个and之间用or连接(TP6写法)
$where = [//第一维代表多个条件
[//第二维之间用or链接
//第三维之间用and连接
['create_time', '<=', time()],
['status', '=', '1']
], [
['id', '>=', 10],
['update_time', '>=', time()],
]
];
Db::table('user')
->whereOr($where)
->find();
//SQL语句
SELECT * FROM `user` WHERE (create_time <= time() and status = 1) or (update_time >= time() and id >= 10)//模糊搜索后,按照搜索结果排序,比如:文章模糊搜索标题和内容,搜索结果优先显示标题
$where = ['title|content' => ['like', '%国%']];
Db::table('article')
->whereOr($where)
->order('titlelike "%国%" desc')
->find(); 这个错误一般是数据库断开后不再自动连接导致的。
需要将 config['break_reconnect'] 的值设置为 true, 才会开启断线重连。
一般在database.php 配置文件中:
//断线重连 'break_reconnect' => true,
但有些版本不在此文件中,可以从thinkphp/library/think/db/Connection.php找到,再找不到,请全局搜索!
最后,一般造成这样的原因可能是因为数据库连接次数过多,还是要优化代码,可以考虑添加缓存,减少数据库的连接。
THINK_PATH 框架系统目录 ROOT_PATH 框架应用根目录 APP_PATH 应用目录(默认为application) CONF_PATH 配置目录(默认为APP_PATH) LIB_PATH 系统类库目录(默认为 THINK_PATH.'library/') CORE_PATH 系统核心类库目录 (默认为 LIB_PATH.'think/') TRAIT_PATH 系统trait目录(默认为 LIB_PATH.'traits/') EXTEND_PATH 扩展类库目录(默认为 ROOT_PATH . 'extend/') VENDOR_PATH 第三方类库目录(默认为 ROOT_PATH . 'vendor/') RUNTIME_PATH 应用运行时目录(默认为 ROOT_PATH.'runtime/') LOG_PATH 应用日志目录 (默认为 RUNTIME_PATH.'log/') CACHE_PATH 项目模板缓存目录(默认为 RUNTIME_PATH.'cache/') TEMP_PATH 应用缓存目录(默认为 RUNTIME_PATH.'temp/')
Elasticsearch是一个基于java语言开发的分布式多用户的全文搜索引擎,在云计算中,能够达到实时搜索,稳定、可靠、快速,安装方便。
官方客户端在java、PHP、Python、.NET等其他语言都是可用的。
Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。
首先安装java环境,因为Elasticsearch是基于java运行的。参考:java的安装。
下载Elasticsearch,建议最新版,其他版本我下载后出现问题,解决非常麻烦。
GitHub下载地址:https://github.com/elastic/elasticsearch
下载成功后打开根目录样式如下:
命令行启动Elasticsearch服务:
elasticsearch.bat

浏览器访问以下地址,如果如图显示则安装成功!
http://localhost:9200/
end
此工具是为了对Elasticsearch中的数据进行可视化管理,类似于Navicat。
安装使用参考查看:Elasticsearch-Head的安装。
Elasticsearch-analysis-ik中文分词器是为了将中文搜索条件进行分词搜索使用的。
安装使用参考查看:Elasticsearch-analysis-ik的安装。
1、首先下载composer
2、cmd进入项目根目录下
3、使用composer命令进行安装:
composer require elasticsearch/elasticsearch
4、end
use Elasticsearch\ClientBuilder;
class Es extends BaseController
{
protected $client;
public $index_name;//索引值,类似于数据库名
/**
* Es constructor.
* @param string $index_name 索引名称
*/
public function __construct($index_name = "shx")
{
parent::__construct();
//给定默认值
$this->index_name = $index_name;
//自动连接Elasticsearch服务器
$this->client = ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build();
}
/**
* ELasticsearch初次使用
*/
public function index()
{
//删除原索引
//$this->delete_index();
//创建新索引
//$this->create_index();
//自定义内容,也可换成数据库内容
$docs = [];
$docs[] = ['id' => 1, 'name' => '叶凡', 'content' => '我做的ui界面强无敌。', 'age' => 23];
$docs[] = ['id' => 2, 'name' => '秦昊', 'content' => '我的php代码无懈可击。', 'age' => 24];
$docs[] = ['id' => 3, 'name' => '萧炎', 'content' => 'C的生活,快乐每一天。', 'age' => 29];
$docs[] = ['id' => 4, 'name' => '林动', 'content' => '就没有我做不出的前端页面。', 'age' => 26];
$docs[] = ['id' => 5, 'name' => '唐三', 'content' => 'php是最好的语言。', 'age' => 21];
$docs[] = ['id' => 6, 'name' => '秦宇', 'content' => '别烦我,我正在敲bug呢!', 'age' => 25];
$docs[] = ['id' => 7, 'name' => '宋书航', 'content' => '为所欲为,不行就删库跑路', 'age' => 27];
foreach ($docs as $k => $v) {
//添加文档
$this->add_doc($v['id'], $v);
}
//查看映射(整个index索引的相关信息,不包括数据)
//$res = $this->get_mapping();
//搜索结果
$search_where = [
'bool' => [
'should' => [
[
'match' => [
'content' => [
'query' => '删库 别烦我',//查询内容
'boost' => 3, // 权重大
]
]
],
[
'match' => [
'name' => [
'query' => '删库 别烦我',
'boost' => 2,
]
]
],
],
],
];
//按照搜索条件,age倒序排列,每页10条数据,查看第一页的数据
//$data = $this->search_doc($search_where, ['age' => ['order' => 'desc']], 0, 10);
//按照搜索条件,按照匹配度由高到低排序,每页2条数据,查看第一页的数据
$data = $this->search_doc($search_where, [], 0, 2);
dump($data);
}
/**
* 创建索引
* @return array|mixed
*/
public function create_index()
{
//给定索引名
$index_name = $this->index_name;
// 只能创建一次
$params = [
'index' => $index_name,
'body' => [
//定义映射中的一些设置
'settings' => [
'number_of_shards' => 5,//数据分片数,默认为5,有的时候也会用3
'number_of_replicas' => 0//数据备份数,如果只有一台机器,则设置为0
]
]
];
try {
//完成创建索引的映射
return $this->client->indices()->create($params);
} catch (Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/**
* 删除索引
* @return array
*/
public function delete_index()
{
//给定索引名
$index_name = $this->index_name;
$params = ['index' => $index_name];
//删除指定索引,包括其中的数据映射等
$response = $this->client->indices()->delete($params);
return $response;
}
/**
* 查看映射
* @return array
*/
public function get_mapping()
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
];
//查看指定索引下指定
$response = $this->client->indices()->getMapping($params);
return $response;
}
/**
* 添加文档,一次仅插入一条
* @param $id 此条数据索引id
* @param $doc 此条数据具体内容,例:['name'=>'shx']
* @return array|callable
*/
public function add_doc($id, $doc)
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id,
'body' => $doc
];
//添加一条文档
$response = $this->client->index($params);
return $response;
}
/**
* 判断文档存在
* @param $id 索引id
* @return bool
*/
public function exists_doc($id)
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
//判断文档是否存在
$response = $this->client->exists($params);
return $response;
}
/**
* 获取文档
* @param $id 索引id
* @return array|callable
*/
public function get_doc($id)
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
//获取指定id的文档内容
$response = $this->client->get($params);
return $response;
}
/**
* 更新文档,一次仅更新一次
* @param $id 需要更新的索引id
* @param $update_data 更新内容,例:['name'=>'shxtest']
* @return array|callable
*/
public function update_doc($id, $update_data)
{
//给定索引名
$index_name = $this->index_name;
// 可以灵活添加新字段,最好不要乱添加
$params = [
'index' => $index_name,
'id' => $id,
'body' => [
'doc' => $update_data
]
];
$response = $this->client->update($params);
return $response;
}
/**
* 删除文档
* @param $id 索引id
* @return array|callable
*/
public function delete_doc($id)
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
$response = $this->client->delete($params);
return $response;
}
/**
* 查询文档
* @param $search_where 检索条件
* @param array $order 排序,此处如果传空数组,则会按相似度由高到低进行排序。例如:['age' => ['order' => 'desc']]
* @param int $from 分页的页数,从0开始为第一页
* @param int $size 分页条数(每页显示多少条)
* @return array|callable
*/
public function search_doc($search_where, $order, $from = 0, $size = 2)
{
//给定索引名
$index_name = $this->index_name;
$params = [
'index' => $index_name,
'body' => [
//检索条件
'query' => $search_where,
//排序内容
'sort' => $order,
//页数
'from' => $from,
//每页条数
'size' => $size
]
];
$results = $this->client->search($params);
return $results;
}
}MySQL中的数据库就相当于Elasticsearch的index索引。
MySQL中的数据表就相当于Elasticsearch的type。
MySQL中的每行数据就相当于Elasticsearch的doument。
MySQL中的表字段就相当于Elasticsearch的Field。
MySQL中的视图就相当于Elasticsearch的Mapping。
注意:MySQL和Elasticsearch无任何直接关系,只是在此举例说明。
elasticsearch运行是基于java的,所以如果想要使用elasticsearch首先需要安装java环境。
如果安装最新版ES插件,请检查PHP版本,例如当前安装elasticsearch7.11,则PHP版本必须大于等于7.1。
//快捷查询
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'); 直接在网站运行目录的.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;
}
}
在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邮箱这边可能有一定验证机制,长时间不使用就会出现类似于注销的情况。