关于 Laravel 在 public/index.php 入口文件实例一个 model 对象,为何查找数据失败,连接解析器为 null?

问题由来
为了了解laravel一个model的操作数据的全过程,为加入redis缓存做准备,想跳过请求解析阶段,直接调试model(容器加载之后很方便就能实例一个model)。

我已查阅相关论坛以及社区,并没有找到有类似问题以及解答,接下来就自己对框架的了解来步步调试的过程。

代码如下:

<?php

define('LARAVEL_START', microtime(true));

require __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';

----------------------------------------------看这里,添加这两行代码
// 1. 实例一个usermodel
$user = app(Modules\User\Models\User::class);
// 2. 直接用这个model去操作数据库,得到users表的所有数据列表
var_dump($user::all());die;

---------------------------------------------美丽的分割线

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

容器解析一个usermodel是没问题的,但是在查询数据库时,就报错了
Uncaught Error: Call to a member function connection() on null in D:\xampp\htdocs\niua\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:1144 Stack trace: #0

看到提示,我打印了容器解析的 $user 对象,发现对象中 "connection":protected]=> NULL" connection这个受保护的属性为空,所以,我直接调用 model 基类的 setConnection() 函数手动设置 connection ,代码如下:

$user = app(Modules\User\Models\User::class);
$user->setConnection('mysql');
var_dump($user::all());die;

结果没什么卵用,再看源代码,发现 all() 是个 static 方法:

public static function all($columns = ['*'])
    {
        // TMD,他是直接 new static
        return (new static)->newQuery()->get(
            is_array($columns) ? $columns : func_get_args()
        );
    }

所以,我直接在 all() 方法修改:

public static function all($columns = ['*'])
    {
        // 在这里手动设置连接数据库类型
        return (new static)->setConnection('mysql')->newQuery()->get(
            is_array($columns) ? $columns : func_get_args()
        );
    }

结果报错了,根据提示,看了下框架,发现 连接解析器 resolver 这个东西我压根就没有加载:

/**
     * Set the connection resolver instance.
     * 设置连接解析器实例
     *
     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
     * @return void
     */
    public static function setConnectionResolver(Resolver $resolver)
    {
        var_dump(static::$resolver);die;// 输出为null
        static::$resolver = $resolver;
    }

想想不对啊,这个 setConnectionResolver() 函数的 Resolver 依赖为什么laravel容器的反射没给我注入进来?????

到这里,我就已经懵逼了,laravel对我来说是一个新的方向,刚学laravel没多久,求各位路过的朋友帮下忙哈,谢谢laravel中国社区平台。

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

先简单讲讲吧,容器虽然实例化了,但是整个程序的运行是靠 \App\Http\Kernel::class,而你以为容器虽然实例化了,可以实现简单的依赖注入,但是还是有很多问题的。比如有些类实例化不是注入对象,而是数组,字符串。这时候容器是没有办法解析的。而就需要通过服务提供者绑定解析。


其实最核心的还是你能调用到\App\Http\Kernel::bootstrap()这个方法就行了。这个方法启动了整个程序,包括配置,服务提供者...
你反过来想一想,你那一步连Config都没有载入,怎么得到正确的数据库配置呢?

file

file

file

file

file

file

5年前 评论
讨论数量: 6

先简单讲讲吧,容器虽然实例化了,但是整个程序的运行是靠 \App\Http\Kernel::class,而你以为容器虽然实例化了,可以实现简单的依赖注入,但是还是有很多问题的。比如有些类实例化不是注入对象,而是数组,字符串。这时候容器是没有办法解析的。而就需要通过服务提供者绑定解析。


其实最核心的还是你能调用到\App\Http\Kernel::bootstrap()这个方法就行了。这个方法启动了整个程序,包括配置,服务提供者...
你反过来想一想,你那一步连Config都没有载入,怎么得到正确的数据库配置呢?

file

file

file

file

file

file

5年前 评论

@DavidNineRoc 谢谢你,秒懂了,我把加载引导文件bootstrap的过程记错了。

5年前 评论
lmaster

推荐你看下这个专栏,非常棒的讲解,前提是你 php 基础要扎实,可以用一些 IDE 进行查看,如 phpstorm (在把 xdebug 弄好,打个断点什么的)。
你的问题不知道是不是要这么来:
常规:
请求进入框架,处理后到控制器,到模型取数据返回结果,返回响应
那你是想在那里加入 redis 控制中?或者控制器前面?

5年前 评论

@lmaster 框架中用了15-repository这个包,之前加入缓存是直接在repository上实现缓存接口,通过php特性加入缓存的操作方法

class UserRepository extends BaseRepository implements CacheInterface
{
    // 实现缓存,可缓存的方法是:all,paginate,find
    use CacheTrait, SuppQueryBuilderTrait;

    /**
     * @return string 对应的模型类
     */
    public function model()
    {
        return User::class;
    }
}

但这种做法被技术总监驳回了,他想在repository层和model层之间实现一个newmodel层(重写model,在这个层加入缓存)

5年前 评论
lmaster

@lsm301 Repository 是把一个数据存储区的数据给封装成对象的集合并提供了对这些集合的操作
你这个东西没用过,但是网上的介绍。
这个已经是一次封装了,对于操作来说。
不理解你们总监的想法,你应该把你们总监的思路说的更明白些,社区中肯定是有人能解决的

5年前 评论
lmaster

@lsm301

file
可不可以这么理解你们现在的思路

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!