Hyperf 学习日志 II

本文学习Hyperf 的基础功能

基础功能

路由

默认情况下路由由 nikic/fast-route 提供支持,并由 hyperf/http-server 组件负责接入到 Hyperf 中,RPC 路由由对应的 hyperf/rpc-server 组件负责。

HTTP 路由

通过闭包定义路由

构建一个最基本的路由只需一个 URI 和一个 闭包(Closure),我们直接通过代码来演示一下:

1
2
3
4
5
6
<?php
use Hyperf\HttpServer\Router\Router;

Router::get('/hello-hyperf', function () {
return 'Hello Hyperf.';
});
定义标准路由

所谓标准路由指的是由 控制器(Controller)操作(Action) 来处理的路由,如果您使用 请求处理器(Request Handler) 模式也是类似的,我们通过代码来演示一下:

1
2
3
4
5
6
7
<?php
use Hyperf\HttpServer\Router\Router;

// 下面三种方式的任意一种都可以达到同样的效果
Router::get('/hello-hyperf', 'App\Controller\IndexController::hello');
Router::get('/hello-hyperf', 'App\Controller\IndexController@hello');
Router::get('/hello-hyperf', [App\Controller\IndexController::class, 'hello']);
可用的路由方法
1
2
3
4
5
6
7
8
9
10
11
12
use Hyperf\HttpServer\Router\Router;

// 注册与方法名一致的 HTTP METHOD 的路由
Router::get($uri, $callback);
Router::post($uri, $callback);
Router::put($uri, $callback);
Router::patch($uri, $callback);
Router::delete($uri, $callback);
Router::head($uri, $callback);

// 注册任意 HTTP METHOD 的路由
Router::addRoute($httpMethod, $uri, $callback);
路由组的定义方式

实际路由为 gourp/route, 即 /user/index, /user/store, /user/update, /user/delete

1
2
3
4
5
6
Router::addGroup('/user/',function (){
Router::get('index','App\Controller\UserController@index');
Router::post('store','App\Controller\UserController@store');
Router::get('update','App\Controller\UserController@update');
Router::post('delete','App\Controller\UserController@delete');
});

注解路由

Hyperf 学习日志 注解

中间件

定义全局中间件

全局中间件只可通过配置文件的方式来配置,配置文件位于 config/autoload/middlewares.php ,配置如下:

1
2
3
4
5
6
7
8
<?php
return [
// http 对应 config/autoload/server.php 内每个 server 的 name 属性对应的值,该配置仅应用在该 Server 中
'http' => [
// 数组内配置您的全局中间件,顺序根据该数组的顺序
YourMiddleware::class
],
];

只需将您的全局中间件配置在该文件及对应的 Server Name 内,即该 Server 下的所有请求都会应用配置的全局中间件。

定义局部中间件

通过配置文件定义

在使用配置文件定义路由时,推荐通过配置文件来定义对应的中间件,局部中间件的配置将在路由配置上完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Router\Router;

// 每个路由定义方法都可接收一个 $options 参数
Router::get('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::post('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::put('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::patch('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::delete('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::head('/', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);
Router::addRoute(['GET', 'POST', 'HEAD'], '/index', [\App\Controller\IndexController::class, 'index'], ['middleware' => [ForMiddleware::class]]);

// 该 Group 下的所有路由都将应用配置的中间件
Router::addGroup(
'/v2', function () {
Router::get('/index', [\App\Controller\IndexController::class, 'index']);
},
['middleware' => [ForMiddleware::class]]
);
通过注解定义
  • 定义单个中间件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;

/**
* @AutoController()
* @Middleware(FooMiddleware::class)
*/
class IndexController
{
public function index()
{
return 'Hello Hyperf.';
}
}
  • 定义多个中间件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

use App\Middleware\BarMiddleware;
use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;

/**
* @AutoController()
* @Middlewares({
* @Middleware(FooMiddleware::class),
* @Middleware(BarMiddleware::class)
* })
*/
class IndexController
{
public function index()
{
return 'Hello Hyperf.';
}
}
  • 定义方法级别的中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

use App\Middleware\BarMiddleware;
use App\Middleware\FooMiddleware;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;

/**
* @AutoController()
* @Middlewares({
* @Middleware(FooMiddleware::class)
* })
*/
class IndexController
{

/**
* @Middlewares({
* @Middleware(BarMiddleware::class)
* })
*/
public function index()
{
return 'Hello Hyperf.';
}
}

生成中间件

1
php ./bin/hyperf.php gen:middleware Auth/FooMiddleware

常用中间件

跨域中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

declare(strict_types=1);

namespace App\Middleware;

use Hyperf\Utils\Context;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class CorsMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = Context::get(ResponseInterface::class);
$response = $response->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Credentials', 'true')
// Headers 可以根据实际情况进行改写。
->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization');

Context::set(ResponseInterface::class, $response);

if ($request->getMethod() == 'OPTIONS') {
return $response;
}

return $handler->handle($request);
}
}

实际上,跨域配置也可以直接挂在 Nginx 上。

1
2
3
4
5
6
7
8
9
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization';

if ($request_method = 'OPTIONS') {
return 204;
}
}

控制器

编写控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

declare(strict_types=1);

namespace App\Controller;

use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;

class IndexController
{
// 在参数上通过定义 RequestInterface 和 ResponseInterface 来获取相关对象,对象会被依赖注入容器自动注入
public function index(RequestInterface $request, ResponseInterface $response)
{
$target = $request->input('target', 'World');
return 'Hello ' . $target;
}
}

请求

安装

1
composer require hyperf/http-message

获取请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 获取请求路径
$uri = $request->path();

# 获取请求的 URL
// 没有查询参数
$url = $request->url();

// 带上查询参数
$url = $request->fullUrl();

# 获取请求方法
$method = $request->getMethod();
if ($request->isMethod('post')) {
// ...
}

# 获取所有输入
$all = $request->all();

# 获取指定输入值
// 存在则返回,不存在则返回 null
$name = $request->input('name');
// 存在则返回,不存在则返回默认值 Hyperf
$name = $request->input('name', 'Hyperf');

# 从查询字符串获取输入
// 存在则返回,不存在则返回 null
$name = $request->query('name');
// 存在则返回,不存在则返回默认值 Hyperf
$name = $request->query('name', 'Hyperf');
// 不传递参数则以关联数组的形式返回所有 Query 参数
$name = $request->query();

# 获取 JSON 输入信息
// 存在则返回,不存在则返回 null
$name = $request->input('user.name');
// 存在则返回,不存在则返回默认值 Hyperf
$name = $request->input('user.name', 'Hyperf');
// 以数组形式返回所有 Json 数据
$name = $request->all();

# 确定是否存在输入值
// 仅判断单个值
if ($request->has('name')) {
// ...
}
// 同时判断多个值
if ($request->has(['name', 'email'])) {
// ...
}

大致上和Laravel一样…

响应

Hyperf 里可通过 Hyperf\HttpServer\Contract\ResponseInterface 接口类来注入 Response 代理对象对响应进行处理,默认返回 Hyperf\HttpServer\Response 对象,该对象可直接调用所有 Psr\Http\Message\ResponseInterface 的方法。

返回JSON格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Controller;

use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;

class IndexController
{
public function json(ResponseInterface $response): Psr7ResponseInterface
{
$data = [
'key' => 'value'
];
return $response->json($data);
}
}

返回 Xml 格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Controller;

use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;

class IndexController
{
public function xml(ResponseInterface $response): Psr7ResponseInterface
{
$data = [
'key' => 'value'
];
return $response->xml($data);
}
}

返回 Raw 格式

Hyperf\HttpServer\Contract\ResponseInterface 提供了 raw($data) 方法用于快速返回 raw 格式,并设置 Content-Typeplain/text$data 接受一个字符串或为一个实现了 __toString() 方法的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\Controller;

use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;

class IndexController
{
public function raw(ResponseInterface $response): Psr7ResponseInterface
{
return $response->raw('Hello Hyperf.');
}
}

重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\Controller;

use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;

class IndexController
{
public function redirect(ResponseInterface $response): Psr7ResponseInterface
{
// redirect() 方法返回的是一个 Psr\Http\Message\ResponseInterface 对象,需再 return 回去
return $response->redirect('/anotherUrl');
}
}

异常处理

注册异常处理器

目前仅支持配置文件的形式注册 异常处理器(ExceptionHandler),配置文件位于 config/autoload/exceptions.php,将您的自定义异常处理器配置在对应的 server 下即可:

1
2
3
4
5
6
7
8
9
10
11
12
<?php

// config/autoload/exceptions.php
return [
'handler' => [
// 这里的 http 对应 config/autoload/server.php 内的 server 所对应的 name 值
'http' => [
// 这里配置完整的类命名空间地址已完成对该异常处理器的注册
\App\Exception\Handler\FooExceptionHandler::class,
],
],
];

定义异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace App\Exception\Handler;

use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use App\Exception\TestException;
use Throwable;

class TestExceptionHandler extends ExceptionHandler
{
/**
* @param Throwable $throwable
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function handle(Throwable $throwable, ResponseInterface $response)
{
// 判断被捕获到的异常是希望被捕获的异常
if ($throwable instanceof TestException) {
// 格式化输出
$data = json_encode([
'code' => $throwable->getCode(),
'message' => $throwable->getMessage(),
], JSON_UNESCAPED_UNICODE);

// 阻止异常冒泡
$this->stopPropagation();

return $response->withStatus(500)->withBody(new SwooleStream($data));
}

// 交给下一个异常处理器
return $response;
}

public function isValid(Throwable $throwable): bool
{
return true;
}
}

定义异常类

1
2
3
4
5
6
7
8
9
10
<?php

namespace App\Exception;

use Hyperf\Server\Exception\ServerException;

class TestException extends ServerException
{

}

触发异常

1
2
3
4
public function exception()
{
throw new TestException('Test Exception...', 200);
}

缓存

hyperf/cache 提供了基于 Aspect 实现的切面缓存,也提供了实现 Psr\SimpleCache\CacheInterface 的缓存类。

安装

1
composer require hyperf/cache

使用

SimpleCache方式

如果您只想使用实现 Psr\SimpleCache\CacheInterface 缓存类,比如重写 EasyWeChat 缓存模块,可以很方便的从 Container 中获取相应对象。

1
$cache = $container->get(Psr\SimpleCache\CacheInterface::class);
注解方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Services;

use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

class UserService
{
/**
* @Cacheable(prefix="user", ttl=9000, listener="user-update")
*/
public function user($id)
{
$user = User::query()->where('id',$id)->first();

if($user){
return $user->toArray();
}

return null;
}
}
清理注解缓存
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @Inject
* @var EventDispatcherInterface
*/
protected $dispatcher;

public function flushCache($userId)
{
$this->dispatcher->dispatch(new DeleteListenerEvent('user-update', [$userId]));

return true;
}

注解缓存介绍

Cacheable

例如以下配置,缓存前缀为 user, 超时时间为 7200, 删除事件名为 USER_CACHE。生成对应缓存 KEYc:user:1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

/**
* @Cacheable(prefix="user", ttl=7200, listener="USER_CACHE")
*/
public function user(int $id): array
{
$user = User::query()->find($id);

return [
'user' => $user->toArray(),
'uuid' => $this->unique(),
];
}

当设置 value 后,框架会根据设置的规则,进行缓存 KEY 键命名。如下实例,当 $user->id = 1 时,缓存 KEYc:userBook:_1

1
2
3
4
5
6
7
8
9
10
11
12
13
use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

/**
* @Cacheable(prefix="userBook", ttl=6666, value="_#{user.id}")
*/
public function userBook(User $user): array
{
return [
'book' => $user->book->toArray(),
'uuid' => $this->unique(),
];
}
CachePut

CachePut 不同于 Cacheable,它每次调用都会执行函数体,然后再对缓存进行重写。所以当我们想更新缓存时,可以调用相关方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use App\Models\User;
use Hyperf\Cache\Annotation\CachePut;

/**
* @CachePut(prefix="user", ttl=3601)
*/
public function updateUser(int $id)
{
$user = User::query()->find($id);
$user->name = 'HyperfDoc';
$user->save();

return [
'user' => $user->toArray(),
'uuid' => $this->unique(),
];
}
CacheEvict
1
2
3
4
5
6
7
8
9
use Hyperf\Cache\Annotation\CacheEvict;

/**
* @CacheEvict(prefix="userBook", value="_#{id}")
*/
public function updateUserBook(int $id)
{
return true;
}

日志

安装

1
composer require hyperf/logger

配置

hyperf-skeleton 项目内默认提供了一些日志配置,默认情况下,日志的配置文件为 config/autoload/logger.php ,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

return [
'default' => [
'handler' => [
'class' => \Monolog\Handler\StreamHandler::class,
'constructor' => [
'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
'level' => \Monolog\Logger::DEBUG,
],
],
'formatter' => [
'class' => \Monolog\Formatter\LineFormatter::class,
'constructor' => [
'format' => null,
'dateFormat' => null,
'allowInlineLineBreaks' => true,
]
],
],
];

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

declare(strict_types=1);

namespace App\Service;

use Psr\Container\ContainerInterface;
use Hyperf\Logger\LoggerFactory;

class DemoService
{

/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;

public function __construct(LoggerFactory $loggerFactory)
{
// 第一个参数对应日志的 name, 第二个参数对应 config/autoload/logger.php 内的 key
$this->logger = $loggerFactory->get('log', 'default');
}

public function method()
{
// Do somthing.
$this->logger->info("Your log message.");
}
}

不同环境下输出不同格式的日志

上面这么多的使用, 都还只在 monolog 中的 Logger 这里打转, 这里来看看 HandlerFormatter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// config/autoload/logger.php
$appEnv = env('APP_ENV', 'dev');
if ($appEnv == 'dev') {
$formatter = [
'class' => \Monolog\Formatter\LineFormatter::class,
'constructor' => [
'format' => "||%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n",
'allowInlineLineBreaks' => true,
'includeStacktraces' => true,
],
];
} else {
$formatter = [
'class' => \Monolog\Formatter\JsonFormatter::class,
'constructor' => [],
];
}

return [
'default' => [
'handler' => [
'class' => \Monolog\Handler\StreamHandler::class,
'constructor' => [
'stream' => 'php://stdout',
'level' => \Monolog\Logger::INFO,
],
],
'formatter' => $formatter,
],
]
  • 默认配置了名为 defaultHandler, 并包含了此 Handler 及其 Formatter 的信息
  • 获取 Logger 时, 如果没有指定 Handler, 底层会自动把 default 这一 Handler 绑定到 Logger
  • dev(开发)环境: 日志使用 php://stdout 输出到 标准输出(stdout), 并且 Formatter 中设置 allowInlineLineBreaks, 方便查看多行日志
  • 非 dev 环境: 日志使用 JsonFormatter, 会被格式为 json, 方便投递到第三方日志服务

分页器

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Paginator\Paginator;

/**
* @AutoController()
*/
class UserController
{
public function index(RequestInterface $request)
{
$currentPage = $request->input('page', 1);
$perPage = 2;
$users = [
['id' => 1, 'name' => 'Tom'],
['id' => 2, 'name' => 'Sam'],
['id' => 3, 'name' => 'Tim'],
['id' => 4, 'name' => 'Joe'],
];
return new Paginator($users, $perPage, $currentPage);
}
}

分页器方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php

// 获取当前页数
$currentPage = $paginator->currentPage();

// 获取当前页的条数
$count = $paginator->count();

// 获取当前页中第一条数据的编号
$firstItem = $paginator->firstItem();

// 获取当前页中最后一条数据的编号
$lastItem = $paginator->lastItem();

// 获取是否还有更多的分页
<?php
if ($paginator->hasMorePages()) {
// ...
}

// 获取对应分页的 URL
// 下一页的 URL
$nextPageUrl = $paginator->nextPageUrl();
// 上一页的 URL
$previousPageUrl = $paginator->previousPageUrl();
// 获取指定 $page 页数的 URL
$url = $paginator->url($page);

// 是否处于第一页
$onFirstPage = $paginator->onFirstPage();

// 是否有更多分页
$hasMorePages = $paginator->hasMorePages();

// 每页的数据条数
$perPage = $paginator->perPage();

// 数据总数
$total = $paginator->total();

命令行

安装

1
composer require hyperf/command

查看命令列表

1
php bin/hyperf.php 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Available commands:
help Displays help for a command
info Dump the server info.
list Lists commands
migrate
start Start hyperf servers.
db
db:seed
demo
demo:command Hyperf Demo Command
di
di:init-proxy
gen
gen:amqp-consumer Create a new amqp consumer class
gen:amqp-producer Create a new amqp producer class
gen:aspect Create a new aspect class
gen:command Create a new command class
gen:controller Create a new controller class
gen:job Create a new job class
gen:listener Create a new listener class
gen:middleware Create a new middleware class
gen:migration
gen:model
gen:process Create a new process class
gen:request Create a new form request class
gen:seeder Create a new seeder class
migrate
migrate:fresh
migrate:install
migrate:refresh
migrate:reset
migrate:rollback
migrate:status
vendor
vendor:publish Publish any publishable configs from vendor packages.

自定义命令

生成命令

如果你有安装 hyperf/devtool 组件的话,可以通过 gen:command 命令来生成一个自定义命令:

1
php bin/hyperf.php gen:command TestCommand
定义命令

定义该命令类所对应的命令有两种形式,

  • 一种是通过 $name 属性定义,
  • 另一种是通过构造函数传参来定义,

我们通过代码示例来演示一下,假设我们希望定义该命令类的命令为 test:hello

$name 属性定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

declare(strict_types=1);

namespace App\Command;

use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Command\Annotation\Command;

/**
* @Command
*/
class TestCommand extends HyperfCommand
{
/**
* 执行的命令行
*
* @var string
*/
protected $name = 'test:hello';
}
构造函数传参定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

declare(strict_types=1);

namespace App\Command;

use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Command\Annotation\Command;

/**
* @Command
*/
class TestCommand extends HyperfCommand
{
public function __construct()
{
parent::__construct('test:hello');
}
}

数据库模型

快速开始

Hyperf框架

1
composer require hyperf/db-connection

其他框架

1
composer require hyperf/database

读写分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'read' => [
'host' => ['192.168.1.1'],
],
'write' => [
'host' => ['196.168.1.2'],
],
'sticky' => true,
'database' => env('DB_DATABASE', 'hyperf'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
],
],
];

注意在以上的例子中,配置数组中增加了三个键,分别是 readwritestickyreadwrite 的键都包含一个键为 host 的数组。而 readwrite 的其他数据库都在键为 mysql 的数组中。

如果你想重写主数组中的配置,只需要修改 readwrite 数组即可。所以,这个例子中: 192.168.1.1 将作为 「读」 连接主机,而 192.168.1.2 将作为 「写」 连接主机。这两个连接会共享 mysql 数组的各项配置,如数据库的凭据(用户名 / 密码),前缀,字符编码等。

多库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php

return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'hyperf'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
],
],
'test'=>[
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST2', 'localhost'),
'database' => env('DB_DATABASE', 'hyperf'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
],
],
];

使用时,只需要规定 connectiontest,就可以使用 test 中的配置,如下。

1
2
3
4
5
6
7
8
9
<?php

use Hyperf\DbConnection\Db;
// default
Db::select('SELECT * FROM user;');
Db::connection('default')->select('SELECT * FROM user;');

// test
Db::connection('test')->select('SELECT * FROM user;');

模型中修改 connection 字段,即可使用对应配置,例如一下 Model 使用 test 配置。

1
2
3
4
5
6
/**
* The connection name for the model.
*
* @var string
*/
protected $connection = 'test';

执行原生SQL语句

Query 查询类

1
2
3
4
5
6
7
8
9
<?php

use Hyperf\DbConnection\Db;

$users = Db::select('SELECT * FROM `user` WHERE gender = ?',[1]); // 返回array

foreach($users as $user){
echo $user->name;
}

Execute 执行类

1
2
3
4
5
6
7
8
9
10
11
<?php

use Hyperf\DbConnection\Db;

$inserted = Db::insert('INSERT INTO user (id, name) VALUES (?, ?)', [1, 'Hyperf']); // 返回是否成功 bool

$affected = Db::update('UPDATE user set name = ? WHERE id = ?', ['John', 1]); // 返回受影响的行数 int

$affected = Db::delete('DELETE FROM user WHERE id = ?', [1]); // 返回受影响的行数 int

$result = Db::statement("CALL pro_test(?, '?')", [1, 'your words']); // 返回 bool CALL pro_test(?,?) 为存储过程,属性为 MODIFIES SQL DATA

自动管理数据库事务

1
2
3
4
5
6
7
8
9
<?php

use Hyperf\DbConnection\Db;

Db::transaction(function () {
Db::table('user')->update(['votes' => 1]);

Db::table('posts')->delete();
});

手动管理数据库事务

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

use Hyperf\DbConnection\Db;

Db::beginTransaction();
try {

// Do something...

Db::commit();
} catch(\Throwable $ex) {
Db::rollBack();
}

查询构造器

Hyperf 查询构造器

参考

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2023 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :