使用存储库时,调用成员函数createQueryBuilder()为null

时间:2019-03-30 19:47:36

标签: php symfony doctrine

我正在尝试为我的应用程序实现自定义身份验证系统。

为此,我创建了一个名为Auth的类(库),该类扩展了我的UserRepository,因此我可以直接在auth类中使用该模型。

<?php

namespace App\Lib;
use App\Repository\UserRepository;

class Auth extends UserRepository {

在我位于Auth中的身份验证函数中,我调用findByCredentials函数,该函数应该从数据库返回一些用户数据。

$user = $this->findByCredentials($email, $password);

我在控制器内部调用它以使其运行

public function index(Request $request) {
    $auth = new Auth();
    $auth->authenticate(null, 'somefakemaik@example.com', '1234');
    return $this->tpl();
}

这是我的UserRepository,它是自动生成的,到目前为止,我仅添加了一个功能。

<?php

namespace App\Repository;

use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;

class UserRepository extends ServiceEntityRepository
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, User::class);
    }

    public function findByCredentials($email, $password)
    {
        return $this->createQueryBuilder('user')
            ->andWhere('user.email = :email')
            ->andWhere('user.password = :password')
            ->setParameters([
                'email' => $email,
                'password' => $password
              ])
            ->getQuery()
            ->getResult();
}

但是我得到标题中提到的错误。

我使用cli / console生成了所有内容,并确保我的表存在于数据库中。我在Linux机器上使用MySQL v5.7。

我还配置了config / packages / doctrine.yaml

auth类的构造器

public function __construct($jwt = null, $email = null, $password = null) {
    $this->jwt = $jwt;
    $this->email = $email;
    $this->password = $password;
}

1 个答案:

答案 0 :(得分:1)

您显然正在消除依赖注入的优点。

从本质上讲,依赖注入是您不在类内部创建服务,而是在构造函数或函数调用中将其作为参数提供。 (在symfony框架中,通常不必在其他地方创建它,但是symfony将照顾为您提供它,除非您明确地想要调用函数/构造函数...这就是为什么通常不这样做的原因。 t。)

一个示例是控制器,其中Request $request(类型提示)由框架提供。从技术上讲,只要框架(更具体地说是容器)知道该类以及如何生成该类/实例,您几乎可以在其中找到任何类型提示类。容器会为您生成依赖项,并连接到自动装配或自动配置,这非常方便。 (有关symfony的文档:https://symfony.com/doc/current/service_container.html

在您的特定示例中:我建议不要扩展UserRepository,并提出两个更好的选择:

将其移动到您的UserRepository

如果您想扩展UserRepository,并且该UserRepository已经是特定项目并且对于您的项目而言是唯一的,为什么不将“扩展名”直接放在UserRepository中?我真的想不出一个很好的理由不这样做。但是...

Auth服务。

由于要进行一些重要的准备工作,以在学说的一边创建存储库,并且涉及一些类似单例的依赖项(注册表,连接等),并在某个地方挖掘,因此请采用自动装配/自动配置框架,并将UserRepository设为Auth服务的参数:

class Auth {
    /** @var UserRepository */
    private $userRepository;

    public function __construct(UserRepository $userRepository) {
        $this->userRepository = $userRepository;
    }

    public function authenticate(...) {
        /* some logic that can use $this->userRepository */
    }
}

重要说明:您将jwt,电子邮件,密码作为Auth的参数。这产生了语义上的差异:要么是对用户进行身份验证的服务,在这种情况下,绝对不要将jwt,电子邮件和密码作为构造函数的一部分,因为那样的话,它将是对一个用户的身份验证,即为其创建用户的身份验证。 ...几乎没有任何好处。否则,它应该在authenticate调用中接收这些参数-我认为这更有意义。

但是,如果您正在执行一些复杂的身份验证协议,在该协议中,您需要与在用户凭据顶部(用户身份验证即服务或其他任何东西)上需要一些其他凭据的外部服务器通信,那么我的论点是无效的,您应该学习如何在services.yaml中定义Auth的凭据。

但是我怀疑并非如此。暂时考虑一下状态可能是一个好主意。通常,找出构造服务的实际必要条件,并将这些内容添加到构造函数中。当服务必须执行某些操作时,它属于服务方法中的参数。

修复控制器

自己创建一个Auth是很不方便的,因为您将不得不重建symfony的依赖注入+自动接线+自动配置已经提供的许多功能。干净的方法是注入所需的东西。

我将在此处重复您的索引路线,并在下面添加一些评论

public function index(Request $request) {
    $auth = new Auth();
    $auth->authenticate(null, 'somefakemaik@example.com', '1234');
    return $this->tpl();
}

尽管不是严格要求,但通常将控制器的路由功能命名为[something]Action,因此命名为indexAction。其次,您显然不使用Request,因此应将其删除。第三,将UserRepositoryAuth添加到方法标题中(取决于您选择的替代方法):

// this goes into your controller

// or: public function indexAction(UserRepository $userRepository) { 
public function indexAction(Auth $auth) {
    $auth->authenticate(/** ... your parameters ... */);
    return $this->tpl(); // <-- what's this? ;o)
}