这一章主要讲解PHP操作数据库的应用。需要具备一些数据库前置知识,至少掌握mysql和redis的crud操作。就算不懂数据库的知识也无所谓,数据库语言都非常简单,从字面意义就能明白是在做什么。
PHP连接数据库的方式有2种:
-
MySQLi extension ("i" 意为 improved)
-
PDO (PHP Data Objects)
两者的区别在于MySQLi只支持mysql,而PDO支持12种数据库。
为了方便,我们只学习PDO的使用即可。因为我们不能只会连接MySQL。这样会让我们投入的时间性价比更高。
PDO和go的go-sql-driver,Java的JDBC作用是一样的,连使用方法几乎都非常一致。做法都是创建一个连接实例,通过连接实例执行sql。
查看PDO是否受支持
PHP7之后,php自动具备了PDO,可以使用phpinfo函数查看是否开启了PDO。
利用上一章学习到的路由和控制器知识,写一个RESTfulAPI来查看php环境。
创建路由
Route::get('phpinfo', 'EnvironmentController');
创建控制器
class EnvironmentController extends Controller { public function __invoke() { // 注意需要连接一个空字符串,因为phpinfo没有toString return phpinfo() . ''; } }
然后在浏览器访问http://homestead.test/api/v1/phpinfo
。你应该会看到非常多的数据,可以直接搜索PDO关键字,看到如下内容就表示当前运行环境的PDO是支持的。
连接数据库
创建一个PDO实例,传入3个构造参数。第1个是数据库连接,第2个是用户名,第3个是用户密码。
function connDB($dbname = 'test') { $servername = "localhost"; $username = "homestead"; $password = "secret"; try { $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); } catch (PDOException $e) { return $e->getMessage(); } }
创建数据库
使用创建出来的PDO实例conn来调用query方法,传入要执行的sql作为参数。
query方法返回bool值,成功为true,失败为false。
失败时还可以通过conn访问errorCode和errorInfo来获取错误信息。
$sql = "CREATE DATABASE test"; if ($conn->query($sql) == true) { return "数据库创建成功"; } else { return [ 'errorCode' => $conn->errorCode(), 'errorInfo:' => $conn->errorInfo() ]; }
创建表
创建表和创建数据库的操作一样,只是执行的sql不同。
操作数据(CRUD)
插入insert、查询select、修改update、删除delete四种操作都可以使用exec
方法来执行,但是这个方法不返回任何值。只能通过try/catch
来捕获异常来验证是否成功。
$conn->exec($sql);
设置错误模式
设置错误模式,用于抛出异常。如果不设置,sql执行失败也不会抛出异常。
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
事务
开启事务$conn->beginTransaction();
提交事务$conn->commit();
回滚事务$conn->rollback();
开启事务后开始执行sql语句,执行完毕提交事务。开启异常捕获,如果出现异常,执行回滚操作。
预处理语句/SQL模板
预处理语句就是定义模板,预留?
标记。例如:
insert into user (name, age, email) VALUES(?, ?, ?)
使用方法:
1.使用conn的prepare方法创建模板并返回一个对象stmt。其中字符串模板的填充字段用:前缀来修饰。
2.调用stmt的bindParam方法来绑定字段和值。接受2个参数,第1个参数是字段,第2个参数是值的变量名。
3.绑定后,执行stmt的方法execute来执行模板sql。
4.之后再次修改值时,直接修改变量的值,再次执行stmt的方法execute即可。
try { $stmt = $conn->prepare("insert into user (name, age, email, gender) VALUES(:name, :age, :email, :gender)"); $name = "张三"; $age = 134; $email = "@@@.@"; $gender = "男"; $stmt->bindParam(':name', $name); $stmt->bindParam(':age', $age); $stmt->bindParam(':email', $email); $stmt->bindParam(':gender', $gender); $stmt->execute(); // 再次插入行 $name = "李四"; $age = 22; $email = "lisi@gmail.com"; $stmt->execute(); return "插入成功"; } catch (PDOException $e) { return $e->getMessage(); }
二、使用框架操作Mysql
这部分其实没什么可讲的东西,框架做的事,无非就是在PDO或mysqli基础上进行封装,简化我们的操作而已。如果你是一直读过来的同学,应该发现了一个问题。从学框架应用开始,讲的内容越来越含糊了。其实,这些框架的API我并不是死记硬背的。大多数情况下,我都是先读一遍官方文档,然后针对常用的API写一些Demo测试。之后就是实战了。在实战中,如果发现有些需求处理的不够优雅,就会在去官方文档和社区寻找对应的更优解决方案。对我来讲,学习框架的各种API是一件吃力不讨好的事。
Laravel 能使用原生 SQL、流畅的查询构造器 和 Eloquent ORM 在各种数据库后台与数据库进行非常简单的交互。
运行原生查询
使用DBuse Illuminate\Support\Facades\DB;
.
执行查询DB::select('select * from users where active = ?', [1]);
,返回值始终是数组。
执行插入DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);
执行更新$affected = DB::update('update users set votes = 100 where name = ?', ['John']);
,返回更新的行数。
执行删除$deleted = DB::delete('delete from users');
,返回删除的行数。
运行普通语句DB::statement('drop table users');
无返回值。
事务
使用db的transaction方法,参数是个函数,在里面执行sql。
DB::transaction(function () { DB::table('users')->update(['votes' => 1]); // 这里写sql DB::table('posts')->delete(); });
看到这里,你应该发现,我们用框架做的事,和前面学的PDO做的事基本上是一样的,区别只是写法不同。
那为什么还要再学一遍呢?因为我们要使用框架,框架帮助我们处理了很多异常问题和边界问题,简化我们的操作。
这里我不想再讲下去了,因为我没必要把官方文档的API挨个演示一遍。至于更高级的操作,如构造器和eloquent ORM,如果感兴趣,建议你去中文社区进行学习这一部分的内容。https://learnku.com/docs/laravel/5.8/database/3925
三、使用框架操作Redis
其实在我们目前的项目中,关于redis的使用很有限。这里不说redis集群的使用方式,只说下redis的单机应用。
这里假设你已经在本机安装好redis。
关于redis的可视化管理工具,推荐 Redis Desktop Manager。
首先在项目中安装predis包
composer require predis/predis
安装predis的过程中可能会遇到一个异常信息:
the requested PHP extension fileinfo is missing from your system
意思是没有开启fileinfo扩展,需要在你PHP环境的php.ini文件中修改一行配置,如果没有这行配置那么就添加上。
extension=php_fileinfo.dll
如果你的redis默认配置没有修改,那么项目中的配置也都不需要修改。
如果你的redis不是本机的,或者修改了端口等,需要在项目中的config/database.php和.env文件中分别修改配置。
database.php相关配置
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'predis'),
'prefix' => Str::slug(env('APP_NAME', 'laravel'), '_').'_database_',
],
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
],
.evn相关配置。
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
predis的相关操作还是非常简单的,基本上都和redis的原生操作一致。
存值Redis::set('key', 'value');
取值Redis::get('key');
检测某值是否存在Redis::exists('user_key')
设置过期时间Redis::setex('str', 10, 'bar');
删除Redis::del('key');
redis的操作多达十几种,这里就不再继续罗列,更多操作可查阅:https://packagist.org/packages/predis/predis
四、总结
这一章学习了在原生PHP和框架中操作mysql和redis的方式