laravel-doctrine / orm

A drop-in Doctrine ORM 2 implementation for Laravel 5+ and Lumen

Home Page:http://laraveldoctrine.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[QUESTION] Custom repository for Auth\DoctrineUserProvider

thsvkr opened this issue · comments

Package version, Laravel version

  • laravel-doctrine/orm 1.7.1
  • laravel/framework 8.13

Expected behaviour

class DoctrineUserProvider implements UserProvider
{    
    /**
     * Returns repository for the entity.
     * @return EntityRepository
     */
    protected function getRepository()
    {
        return new config('auth.custom_repository') //for example
    }
}

Actual behaviour

"Argument 2 passed to App\\Users\\Repositories\\DoctrineUserRepository::__construct() must be an instance of Doctrine\\ORM\\EntityRepository, instance of Doctrine\\ORM\\Mapping\\ClassMetadata given, called in /home/vagrant/code/boox/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php on line 68",

class UserProvider implements UserProvider
{    
    /**
     * Returns repository for the entity.
     * @return EntityRepository
     */
    protected function getRepository()
    {
        return $this->em->getRepository($this->entity);
    }
}

Steps to reproduce the behaviour

Use composite repository

class DoctrineUserRepository implements UserRepository
{
    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $em;
    /**
     * @var EntityRepository
     */
    private EntityRepository $er;

    /**
     * UserRepository constructor.
     * @param EntityManagerInterface $em
     * @param EntityRepository $er
     */
    public function __construct(EntityManagerInterface $em, EntityRepository $er)
    {
        $this->em = $em;
        $this->er = $er;
    }
}

User entity

/**
 * Class User
 * @package App\Users\Entities
 * @ORM\Entity(repositoryClass="App\Users\Repositories\UserRepository")
 * @ORM\Table(name="users")
 * @ORM\HasLifecycleCallbacks
 */
class User implements AuthenticatableContract, JWTSubject
{
    use AuthenticatableTrait;`
    //
}

in config/auth.php

    'providers' => [
        'users' => [
            'driver' => 'doctrine',
            'model' => \App\Users\Entities\User::class,
        ]
    ],

Not sure what you question is, the stacktrace points to errors at your end?

As you see from the stacktrace, the DefaultRepositoryFactory creates repositories by passing $metadata as the second argument. Looks like you have created a repository which does not accept metadata as second argument?

EDIT: Pressed the wrong button, sorry.

If you would like to create repositories in a different way, then use the configuration option doctrine.managers.default.repository_factory which should point to a classname which implements Doctrine\ORM\Repository\RepositoryFactory.

Looks like you have created a repository which does not accept metadata as second argument?

Yes, my composite repository does not accept metadata as second argument, but accept Doctrine\Orm\EntityRepository which in turn already accepts metadata

Composite repository binded to interface in App\Providers\AppServiceProvider

namespace App\Providers;

use App\Users\Entities\User;
use App\Users\Interfaces\UserRepository;
use App\Users\Repositories\DoctrineUserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     * @return void
     */
    public function register()
    {
        $this->app->when(DoctrineUserRepository::class)
                  ->needs(EntityRepository::class)
                  ->give(
                      fn() => new EntityRepository(
                          $this->app->make(
                              EntityManagerInterface::class
                          ),
                          new ClassMetadata(User::class)
                      )
                  );

        $this->app->bind(
            UserRepository::class,
            DoctrineUserRepository::class
        );

        //
    }

    //
}

The authentication controller uses Auth::attempt($credentials), but Auth keeps using Doctrine\Orm\EntityRepository instead of binded to App\Users\Interfaces\UserRepository

I think that replacing the doctrine.managers.default.repository_factory will not work for me, because I cannot instantiate the Doctrine\ORM\EntityRepository for App\Users\Repositories\DoctrineUserRepository repository

I used this as a reference, but it uses its own authentication

Full stacktrace
{
  "message": "Argument 2 passed to App\\Users\\Repositories\\DoctrineUserRepository::__construct() must be an instance of Doctrine\\ORM\\EntityRepository, instance of Doctrine\\ORM\\Mapping\\ClassMetadata given, called in /home/vagrant/code/boox/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php on line 68",
  "exception": "TypeError",
  "file": "/home/vagrant/code/boox/app/Users/Repositories/DoctrineUserRepository.php",
  "line": 32,
  "trace": [
      {
          "file": "/home/vagrant/code/boox/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php",
          "line": 68,
          "function": "__construct",
          "class": "App\\Users\\Repositories\\DoctrineUserRepository",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php",
          "line": 51,
          "function": "createRepository",
          "class": "Doctrine\\ORM\\Repository\\DefaultRepositoryFactory",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php",
          "line": 739,
          "function": "getRepository",
          "class": "Doctrine\\ORM\\Repository\\DefaultRepositoryFactory",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel-doctrine/orm/src/Auth/DoctrineUserProvider.php",
          "line": 130,
          "function": "getRepository",
          "class": "Doctrine\\ORM\\EntityManager",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel-doctrine/orm/src/Auth/DoctrineUserProvider.php",
          "line": 106,
          "function": "getRepository",
          "class": "LaravelDoctrine\\ORM\\Auth\\DoctrineUserProvider",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/tymon/jwt-auth/src/JWTGuard.php",
          "line": 124,
          "function": "retrieveByCredentials",
          "class": "LaravelDoctrine\\ORM\\Auth\\DoctrineUserProvider",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php",
          "line": 307,
          "function": "attempt",
          "class": "Tymon\\JWTAuth\\JWTGuard",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php",
          "line": 261,
          "function": "__call",
          "class": "Illuminate\\Auth\\AuthManager",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/app/Auth/Http/Controllers/AuthController.php",
          "line": 22,
          "function": "__callStatic",
          "class": "Illuminate\\Support\\Facades\\Facade",
          "type": "::"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Controller.php",
          "line": 54,
          "function": "login",
          "class": "App\\Auth\\Http\\Controllers\\AuthController",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
          "line": 45,
          "function": "callAction",
          "class": "Illuminate\\Routing\\Controller",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 255,
          "function": "dispatch",
          "class": "Illuminate\\Routing\\ControllerDispatcher",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 197,
          "function": "runController",
          "class": "Illuminate\\Routing\\Route",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 691,
          "function": "run",
          "class": "Illuminate\\Routing\\Route",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "function": "Illuminate\\Routing\\{closure}",
          "class": "Illuminate\\Routing\\Router",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
          "line": 41,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 693,
          "function": "then",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 668,
          "function": "runRouteWithinStack",
          "class": "Illuminate\\Routing\\Router",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 634,
          "function": "runRoute",
          "class": "Illuminate\\Routing\\Router",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 623,
          "function": "dispatchToRoute",
          "class": "Illuminate\\Routing\\Router",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 166,
          "function": "dispatch",
          "class": "Illuminate\\Routing\\Router",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "function": "Illuminate\\Foundation\\Http\\{closure}",
          "class": "Illuminate\\Foundation\\Http\\Kernel",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
          "line": 27,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
          "line": 87,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/fruitcake/laravel-cors/src/HandleCors.php",
          "line": 57,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Fruitcake\\Cors\\HandleCors",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/fideloper/proxy/src/TrustProxies.php",
          "line": 57,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "function": "handle",
          "class": "Fideloper\\Proxy\\TrustProxies",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "function": "Illuminate\\Pipeline\\{closure}",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 141,
          "function": "then",
          "class": "Illuminate\\Pipeline\\Pipeline",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 110,
          "function": "sendRequestThroughRouter",
          "class": "Illuminate\\Foundation\\Http\\Kernel",
          "type": "->"
      },
      {
          "file": "/home/vagrant/code/boox/public/index.php",
          "line": 52,
          "function": "handle",
          "class": "Illuminate\\Foundation\\Http\\Kernel",
          "type": "->"
      }
  ]
}

I see a solution in creating a separate custom repository factory next to the default one and using it in LaravelDoctrine\ORM\Auth\DoctrineUserProvider` optionally based on the configuration, but I'm not sure how good this design is

class DoctrineUserRepository implements UserRepository
{
    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $em;
    /**
     * @var EntityRepository
     */
    private EntityRepository $er;

    public function __construct(EntityManagerInterface $em, \Doctrine\ORM\Mapping\ClassMetadata $class)
    {
        $this->em = $em;
        $this->er = new EntityRepository($em, $class->name);
    }
}

Have you tried something like this?

Oh it works thanks

Not new EntityRepository($em, $class->name), but new EntityRepository($em, $class) in __construct because
Argument 2 passed to Doctrine\\ORM\\EntityRepository::__construct() must be an instance of Doctrine\\ORM\\Mapping\\ClassMetadata, string given