hyperf 三十 修改器 三 查询时类型转换

本文详细解释了如何在Hyperf框架中使用Model::__callStatic()和Model::__call()方法进行动态调用,涉及newQuery、withGlobalScope、withCasts等函数,以及查询中的join和selectRaw操作。同时,文章指出了在实际测试中的错误和解决方案。
摘要由CSDN通过智能技术生成

教程:Hyperf

一 原理

使用Hyperf\Database\Model\Model::__callStatic()调用Model::__call()。

Model::__call()使用Model::newQuery()和传递的方法名,返回\Hyperf\Database\Model\Builder类。并使用Hyperf\Database\Model\Builder::withGlobalScope()注册一个新的全局作用域。

根据文档调用Builder::withCasts($casts),实际上是使用Hyperf\Database\Model\Concerns\HasAttributes::mergeCasts($casts)合并casts数组。

最后调用Builder::get()返回\Hyperf\Database\Model\Collection类。获取Collection类后,根据源码若是调用该类属性仅限于以下内容。

#Hyperf\Utils\Collection
protected static $proxies= ['average','avg','contains','each',
'every','filter','first','flatMap','groupBy','keyBy','map',
'max','min','partition','reject','sortBy','sortByDesc',
'sum','unique',];

可以使用Hyperf\Utils\Collection::first()、Hyperf\Utils\Collection::map()或相关函数获取结果。详见Hyperf\Utils\Collection。 

获取的结果应该是对应model类的对象或对象数组,接下来就可以使用model类的对应方法。

若使用casts数组,可调用Hyperf\Database\Model\Concerns\HasAttributes::attributesToArray()。

二 测试

2.1 测试1

#App\Controller\TestController
public function testmodel3() {
       $info = Article::from("articles as a")->select([
        'a.title',
        'content' => User::selectRaw('MAX(created_at)')->whereColumn('a.user_id', 'users.id'),
        ])->withCasts([
        'created_at' => 'date',
        ])->get();
     var_dump($info);
}

 测试结果

[ERROR] stripos() expects parameter 1 to be string, object given[286] in /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/Grammar.php
[ERROR] #0 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/Grammar.php(286): stripos()
#1 [internal function]: Hyperf\Database\Query\Grammars\Grammar->wrap()
#2 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Grammar.php(85): array_map()
#3 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/Grammar.php(366): Hyperf\Database\Grammar->columnize()
#4 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/Grammar.php(324): Hyperf\Database\Query\Grammars\Grammar->compileColumns()
#5 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/Grammar.php(73): Hyperf\Database\Query\Grammars\Grammar->compileComponents()
#6 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Grammars/MySqlGrammar.php(57): Hyperf\Database\Query\Grammars\Grammar->compileSelect()
#7 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(1748): Hyperf\Database\Query\Grammars\MySqlGrammar->compileSelect()
#8 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(2590): Hyperf\Database\Query\Builder->toSql()
#9 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(1780): Hyperf\Database\Query\Builder->runSelect()
#10 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(2746): Hyperf\Database\Query\Builder->Hyperf\Database\Query\{closure}()
#11 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(1781): Hyperf\Database\Query\Builder->onceWithColumns()
#12 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Model/Builder.php(560): Hyperf\Database\Query\Builder->get()
#13 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Model/Builder.php(545): Hyperf\Database\Model\Builder->getModels()
#14 /wj/hyperf/hyperfpro2/runtime/container/proxy/App_Controller_TestController.proxy.php(432): Hyperf\Database\Model\Builder->get()
#15 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/CoreMiddleware.php(161): App\Controller\TestController->testmodel3()
#16 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/CoreMiddleware.php(113): Hyperf\HttpServer\CoreMiddleware->handleFound()
#17 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\HttpServer\CoreMiddleware->process()
#18 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#19 /wj/hyperf/hyperfpro2/vendor/hyperf/session/src/Middleware/SessionMiddleware.php(58): Hyperf\Dispatcher\HttpRequestHandler->handle()
#20 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\Session\Middleware\SessionMiddleware->process()
#21 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#22 /wj/hyperf/hyperfpro2/vendor/hyperf/validation/src/Middleware/ValidationMiddleware.php(83): Hyperf\Dispatcher\HttpRequestHandler->handle()
#23 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\Validation\Middleware\ValidationMiddleware->process()
#24 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#25 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpDispatcher.php(40): Hyperf\Dispatcher\HttpRequestHandler->handle()
#26 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/Server.php(117): Hyperf\Dispatcher\HttpDispatcher->dispatch()
#27 {main}

 经过调试

#Hyperf\Database\Grammar
public function columnize(array $columns) {
        //测试用
        foreach ($columns as $key => $value) {
            if (is_object($value)) {
                var_dump(get_class($value));
            } else {
                var_dump($value);
            }
        }
        //测试用 end
        return implode(', ', array_map([$this, 'wrap'], $columns));
    }
string(7) "a.title"
string(29) "Hyperf\Database\Model\Builder"

 所以select()方法中,参数content值是Hyperf\Database\Model\Builder对象,所以报错。

可见对于当前版本,文档上的内容不可用。

修改后内容如下

#App\Controller\TestController
public function testmodel3() {
       $info = Article::from("articles as a")->select([
        'a.title',
        'content' => User::selectRaw('MAX(created_at)')->whereColumn('a.user_id', 'users.id')->toSql(),
        ])->withCasts([
        'created_at' => 'date',
        ])->get();
     var_dump($info);
}

 测试结果

[ERROR] SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.```deleted_at`` is null` from `articles` as `a` where `articles`.`deleted_at` i' at line 1 (SQL: select `a`.`title`, `select MAX(created_at) from ``userinfo`` where ``a```.```user_id`` = ``users```.```id`` and ``userinfo```.```deleted_at`` is null` from `articles` as `a` where `articles`.`deleted_at` is null)[1088] in /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Connection.php
[ERROR] #0 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Connection.php(1045): Hyperf\Database\Connection->runQueryCallback()
#1 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Connection.php(289): Hyperf\Database\Connection->run()
#2 /wj/hyperf/hyperfpro2/vendor/hyperf/db-connection/src/Connection.php(68): Hyperf\Database\Connection->select()
#3 /wj/hyperf/hyperfpro2/vendor/hyperf/db-connection/src/Traits/DbConnection.php(41): Hyperf\DbConnection\Connection->__call()
#4 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(2590): Hyperf\DbConnection\Connection->select()
#5 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(1780): Hyperf\Database\Query\Builder->runSelect()
#6 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(2746): Hyperf\Database\Query\Builder->Hyperf\Database\Query\{closure}()
#7 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Query/Builder.php(1781): Hyperf\Database\Query\Builder->onceWithColumns()
#8 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Model/Builder.php(560): Hyperf\Database\Query\Builder->get()
#9 /wj/hyperf/hyperfpro2/vendor/hyperf/database/src/Model/Builder.php(545): Hyperf\Database\Model\Builder->getModels()
#10 /wj/hyperf/hyperfpro2/runtime/container/proxy/App_Controller_TestController.proxy.php(432): Hyperf\Database\Model\Builder->get()
#11 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/CoreMiddleware.php(161): App\Controller\TestController->testmodel3()
#12 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/CoreMiddleware.php(113): Hyperf\HttpServer\CoreMiddleware->handleFound()
#13 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\HttpServer\CoreMiddleware->process()
#14 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#15 /wj/hyperf/hyperfpro2/vendor/hyperf/session/src/Middleware/SessionMiddleware.php(58): Hyperf\Dispatcher\HttpRequestHandler->handle()
#16 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\Session\Middleware\SessionMiddleware->process()
#17 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#18 /wj/hyperf/hyperfpro2/vendor/hyperf/validation/src/Middleware/ValidationMiddleware.php(83): Hyperf\Dispatcher\HttpRequestHandler->handle()
#19 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/AbstractRequestHandler.php(64): Hyperf\Validation\Middleware\ValidationMiddleware->process()
#20 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpRequestHandler.php(26): Hyperf\Dispatcher\AbstractRequestHandler->handleRequest()
#21 /wj/hyperf/hyperfpro2/vendor/hyperf/dispatcher/src/HttpDispatcher.php(40): Hyperf\Dispatcher\HttpRequestHandler->handle()
#22 /wj/hyperf/hyperfpro2/vendor/hyperf/http-server/src/Server.php(117): Hyperf\Dispatcher\HttpDispatcher->dispatch()
#23 {main}

 调试内容

string(7) "a.title"
string(109) "select MAX(created_at) from `userinfo` where `a`.`user_id` = `users`.`id` and `userinfo`.`deleted_at` is null"

 从报错中可见两个错误。

  1. 字符串拼接中查询userinfo的sql作为查询字段,运行肯定要报错。
  2. article表重命名后,其查询字段“where `articles`.`deleted_at`”,应为“ `a`.`deleted_at`”,但是并没有改变,肯定运行不通。

所以join表肯定不能按照文档上的内容写,可能是本地的版本不适用。

2.2 测试2

#App\Controller\TestController
public function testmodel3() { 
    $info = Article::select([
            'articles.title',
            'articles.created_at',
        ])->withCasts([
            'created_at' => 'date:Y-m-d',
        ])->get();
        var_dump(get_class($info), get_class($info->first()), $info->first()->attributesToArray());
}

测试结果

string(32) "Hyperf\Database\Model\Collection"
string(18) "App1\Model\Article"
array(2) {
  ["title"]=>
  string(5) "test1"
  ["created_at"]=>
  string(10) "2024-01-13"
}

2.3 测试3

#App\Controller\TestController
public function testmodel3() { 
    $info = Article::select([
            'articles.title',
            'articles.created_at',
        ])->join('userinfo', 'user_id', 'userinfo.id')->withCasts([
            'created_at' => 'String',
        ])->get();
        var_dump(get_class($info), get_class($info->first()), $info->first()->created_at);
}

测试结果

string(32) "Hyperf\Database\Model\Collection"
string(18) "App1\Model\Article"
string(19) "2024-01-13 10:05:51"

三 源码

#Hyperf\Database\Model\Model

use Hyperf\Database\Query\Builder as QueryBuilder;

public static function __callStatic($method, $parameters) {
        return (new static())->{$method}(...$parameters);
    }
public function __call($method, $parameters) {
        if (in_array($method, ['increment', 'decrement'])) {
            return $this->{$method}(...$parameters);
        }

        return call([$this->newQuery(), $method], $parameters);
    }
public function newQuery() {
        return $this->registerGlobalScopes($this->newQueryWithoutScopes());
    }
public function newQueryWithoutScopes() {
        return $this->newModelQuery()->with($this->with)->withCount($this->withCount);
    }
public function newModelQuery() {
        return $this->newModelBuilder($this->newBaseQueryBuilder())->setModel($this);
    }
 protected function newBaseQueryBuilder() {
        $connection = $this->getConnection();

        return new QueryBuilder($connection, $connection->getQueryGrammar(), $connection->getPostProcessor());
    }

public function newModelBuilder($query) {
        return new Builder($query);
    }
#Hyperf\Database\Model\Builder 
public function __construct(QueryBuilder $query) {
        $this->query = $query;
    }
public function __call($method, $parameters) {
        if ($method === 'macro') {
            $this->localMacros[$parameters[0]] = $parameters[1];

            return;
        }

        if (isset($this->localMacros[$method])) {
            array_unshift($parameters, $this);

            return $this->localMacros[$method](...$parameters);
        }

        if (isset(static::$macros[$method])) {
            if (static::$macros[$method] instanceof Closure) {
                return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters);
            }

            return call_user_func_array(static::$macros[$method], $parameters);
        }

        if (isset($this->model) && method_exists($this->model, $scope = 'scope' . ucfirst($method))) {
            return $this->callScope([$this->model, $scope], $parameters);
        }

        if (in_array($method, $this->passthru)) {
            return $this->toBase()->{$method}(...$parameters);
        }

        call([$this->query, $method], $parameters);

        return $this;
    }
#Hyperf\Database\Query\Builder
public function __construct(
        ConnectionInterface $connection,
        Grammar $grammar = null,
        Processor $processor = null
    ) {
        $this->connection = $connection;
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
        $this->processor = $processor ?: $connection->getPostProcessor();
    }
public function select($columns = ['*']) {
        $this->columns = is_array($columns) ? $columns : func_get_args();

        return $this;
    }
public function selectRaw($expression, array $bindings = []) {
        $this->addSelect(new Expression($expression));

        if ($bindings) {
            $this->addBinding($bindings, 'select');
        }

        return $this;
    }
public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) {
        $join = new JoinClause($this, $type, $table);

        // If the first "column" of the join is really a Closure instance the developer
        // is trying to build a join with a complex "on" clause containing more than
        // one condition, so we'll add the join and call a Closure with the query.
        if ($first instanceof Closure) {
            call_user_func($first, $join);

            $this->joins[] = $join;

            $this->addBinding($join->getBindings(), 'join');
        }

        // If the column is simply a string, we can assume the join simply has a basic
        // "on" clause with a single condition. So we will just build the join with
        // this simple join clauses attached to it. There is not a join callback.
        else {
            $method = $where ? 'where' : 'on';

            $this->joins[] = $join->{$method}($first, $operator, $second);

            $this->addBinding($join->getBindings(), 'join');
        }

        return $this;
    }
public function toSql() {
        return $this->grammar->compileSelect($this);
    }
public function get($columns = ['*']): Collection {
        return collect($this->onceWithColumns(Arr::wrap($columns), function () {
            return $this->processor->processSelect($this, $this->runSelect());
        }));
    }
protected function onceWithColumns($columns, $callback) {
        $original = $this->columns;

        if (is_null($original)) {
            $this->columns = $columns;
        }

        $result = $callback();

        $this->columns = $original;

        return $result;
    }
#vendor\hyperf\utils\src\Functions.php 
function collect($value = null)
    {
        return new Collection($value);
    }
#Hyperf\Utils\Collection
protected static $proxies
        = [
            'average',
            'avg',
            'contains',
            'each',
            'every',
            'filter',
            'first',
            'flatMap',
            'groupBy',
            'keyBy',
            'map',
            'max',
            'min',
            'partition',
            'reject',
            'sortBy',
            'sortByDesc',
            'sum',
            'unique',
        ];
public function __get(string $key)
    {
        if (! in_array($key, static::$proxies)) {
            throw new Exception("Property [{$key}] does not exist on this collection instance.");
        }
        return new HigherOrderCollectionProxy($this, $key);
    }
public function __construct($items = [])
    {
        $this->items = $this->getArrayableItems($items);
    }
public function all(): array
    {
        return $this->items;
    }
public function first(callable $callback = null, $default = null)
    {
        return Arr::first($this->items, $callback, $default);
    }
public function get($key, $default = null)
    {
        if ($this->offsetExists($key)) {
            return $this->items[$key];
        }
        return value($default);
    }
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在 Hyperf 中,可以使用 `Hyperf\DbConnection\Db::query()` 方法来执行协程 MySQL 查询。该方法返回一个协程对象,使用 `await` 关键字来等待查询结果。以下是一个示例: ``` use Hyperf\DbConnection\Db; // 在协程中执行 MySQL 查询 $results = await Db::query('SELECT * FROM `users` WHERE `status` = ?', [1]); ``` 在上面的示例中,`Db::query()` 方法接受两个参数:SQL 查询语句和查询参数。查询参数可以是一个数组,其中的值将被自动转义和引用,以避免 SQL 注入攻击。 需要注意的是,执行 MySQL 查询需要在 `config/autoload/databases.php` 中配置数据库连接信息。例如,以下是一个 MySQL 数据库连接的示例配置: ``` return [ 'default' => [ 'driver' => env('DB_DRIVER', 'mysql'), 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', 3306), 'database' => env('DB_DATABASE', 'test'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'charset' => env('DB_CHARSET', 'utf8mb4'), 'collation' => env('DB_COLLATION', 'utf8mb4_general_ci'), 'pool' => [ 'min_connections' => 1, 'max_connections' => 10, 'wait_timeout' => 3.0, ], ], ]; ``` ### 回答2: Hyperf协程是一种基于PHP协程的轻量级并发框架,可以利用高效的协程进行MySQL查询。在Hyperf中使用协程进行MySQL查询有以下几个步骤: 1. 配置MySQL连接:在Hyperf的配置文件中,设置MySQL的连接参数,包括主机名、端口号、用户名、密码、数据库名等。 2. 使用协程方式连接MySQL:Hyperf提供了`Hyperf\Database\Pool\Pool::get()`方法来获取MySQL连接池的实例,然后使用协程的方式连接MySQL数据库。 3. 执行查询语句:使用协程的方式执行MySQL查询语句,可以使用`HYPERF_COMMAND`宏定义来包裹查询语句,以实现协程查询的功能。 4. 获取查询结果:使用协程的方式获取查询的结果,可以使用`$connection->fetchAll()`方法来获取查询结果集。 5. 释放连接:在查询完毕后,需要手动释放连接,使用`$connection->release()`方法将连接放回连接池,以便其他协程可以复用连接。 通过上述步骤,可以在Hyperf中实现使用协程进行MySQL查询的功能。由于协程的特性,可以提高查询的性能和响应速度,同减少资源的消耗。 ### 回答3: Hyperf是一个基于Swoole的高性能框架,可以使用协程来进行MySQL查询。 在Hyperf中,使用协程进行MySQL查询非常简单。首先,我们需要确保项目中安装了`hyperf/database`组件。 接下来,我们可以在控制或服务类中使用依赖注入来引入数据库连接池对象Pool,然后调用query方法进行查询。这样我们就可以在协程中执行MySQL查询操作。 下面是一个简单的示例: ``` use Hyperf\Database\Pool\Pool; use Hyperf\Database\ConnectionInterface; class SomeService { /** * @var ConnectionInterface */ private $connection; public function __construct(Pool $pool) { $this->connection = $pool->getConnection(); } public function query() { $result = $this->connection->select('SELECT * FROM users'); return $result; } } ``` 在这个例子中,我们通过依赖注入引入了数据库连接池对象Pool,并获取了一个数据库连接对象ConnectionInterface。 然后,我们调用了ConnectionInterface对象的select方法,传入了一个简单的查询语句,这里是选择所有的用户数据。查询结果会以数组的形式返回给我们。 需要注意的是,在Hyperf中使用协程进行MySQL查询,我们无需手动创建和释放连接,连接池会自动处理连接的获取和归还。 通过以上例子,我们可以看到Hyperf框架非常方便地支持了协程MySQL查询,使得我们可以在高性能的基础上,更加便捷地操作数据库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lsswear

感谢大佬打赏 q(≧▽≦q)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值