Symfony 6 fetching objects without a controller - php

In previous versions of symfony, you could fetch objects like this
`
public function someMethod()
{
$method = $this->getDoctrine()->getRepository(Method::class)->findOneBy(array('id' => 1));
return $method;
}
`
This was easy because it meant that you could easily make global variables in the twig.yaml file and have dynamic content all around your page.
Now in symfony as far as i know, an argument of ManagerRegistry has to be passed as a argument all the time. Am I being too close minded or is there a work around for this problem?
I tried extending classes and have it pass down that way but it gave me the same workaround errors.

In a controller you can do either this :
class MyController extends AbstractController {
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
}
or
class MyController extends AbstractController {
{ ... }
public function someMethod(EntityManagerInterface $em) {
$em->getRepository(SomeClass::class)
}
}
I took a controller as an example but you can do this into your services. If you work with multiple entity manager, you can use the ManagerRegistry that extends AbstractManagerRegistry and use the method getManager($name)
There is no real workaround for this as you always need to inject it. Depending on what you want to achieve, may be there is solution that can help.

you can also directly inject the repository:
public function someMethod(EntityRepository $repository) {
$entities = $repository->findAll();
}
and of course you can inject it in the construct as well:
class MyController extends AbstractController {
private EntityRepository $repository;
public function __construct(EntityRepository $repository) {
$this->repository = $repository;
}
}
if you are on php 8 you can write:
class MyController extends AbstractController {
public function __construct(private EntityRepository $repository) {}
}

Related

Symfony 4 access parameters inside of repository

I have a repository class called EmailRepository
class EmailRepository extends EntityRepository implements ContainerAwareInterface { ... }
I need to get a parameter injected into this repository class but I dont know how...
This is what I currently have inside of the repository, which is being called from my controller:
Controller:
$em->getRepository(Email::class)->getEmailApi();
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface {
protected $container;
public function setContainer(ContainerInterface $container = null) {
$this->container = $container;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
echo $this->container->getParameter('email_api');
}
}
I always get this error:
Call to a member function getParameter() on null
The parameter is not null, it does have a value. I know it's telling me that $this->container is null. How do I fix this?
If I run this inside of my controller, it works fine and returns Google
echo $this->getParameter('email_api');
Inject container not a good idea. Try this
services.yaml
App\Repository\EmailRepository:
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository
{
protected $emailApi;
public function __construct(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Or via setter injection
services.yaml
App\Repository\EmailRepository:
calls:
- method: setEmailApi
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface
{
protected $emailApi;
public function setEmailApi(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Your original code is not going to work because there is nothing calling EmailRepository::setContainer. Furthermore, using ContainerAware and injecting the full container is discouraged.
Fortunately, the Doctrine bundle has a new base repository class that the entity manager can use to pull the repository from container and allow you to inject additional dependencies as needed. Something like:
namespace App\Repository;
use App\Entity\Email;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class EmailRepository extends ServiceEntityRepository // Different class to extend from
{
private $emailApi;
public function __construct(RegistryInterface $registry, ParameterBagInterface $parameterBag)
{
parent::__construct($registry, Email::class);
$this->emailApi = $parameterBag->get('email_api');
}
So in this case we inject all the parameters and then store the ones we need.
Even injecting the parameter bag is a bit frowned upon. Better to inject individual parameters though this takes just a bit more configuration as we need to use services.yaml to explicitly inject the needed parameters:
public function __construct(RegistryInterface $registry, string $emailApi)
{
parent::__construct($registry, Email::class);
$this->emailApi = $emailApi;
}
#services.yaml
App\Repository\EmailRepository:
$emailApi: 'email_api_value'

How to autowire Common Controller Dependencies in Symfony using an Abstract class

I have created an abstract BaseController class that extends AbstractController.
This is so that all the Common Dependencies don't have to be injected in each Controller class that I have (e.g. EntityManager and RequestStack).
However, I have some Controller classes where I would like to inject additional services in the constructor, but this is causing problems.
// src/Controller/BaseController.php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RequestStack;
abstract class BaseController extends AbstractController
{
protected $em;
protected $request;
public function __construct(EntityManagerInterface $em, RequestStack $request)
{
$this->em = $em;
$this->request = $request->getCurrentRequest();
}
}
I can then just extend my Controller classes and call for example $this->em in any of the methods.
However, let's say that I wanted to do the following:
// src/Controller/DashboardController.php
namespace App\Controller;
use Symfony\Component\Translation\TranslatorInterface;
class DashboardController extends BaseController
{
public function __construct(TranslatorInterface $translator)
{
parent::__construct();
$this->translator = $translator;
}
public function index()
{
// use $this->translator()
}
}
This would cause an error as the constructor of the BaseController is expecting two arguments to be passed.
I've tried adding the following to my services.yaml but to no avail:
App\Controller\BaseController:
arguments: ['#doctrine.orm.entity_manager', '#request_stack']
What would be the best way to autowire these arguments, and would this be a good practice?

[Laravel]: How do I dependency inject into an abstract class extended by other classes(jobs)

I have a Laravel project where I have created an abstract class that several of my jobs will use as they all need to use the same method to find some data to proceed.
In Laravel, the way jobs work is that the constructor takes any values that you trigger the job with and in a handler method, dependencies can be injected, like so:
class SomeJob extends Job implements ShouldQueue
{
public function __construct(array $someData, int $someMoreData)
{
$this->someData = $someData;
$this->someMoreData = $someMoreData;
}
public function handle()
{
// Do something...
}
}
\Queue::pushOn(Queue::getDefaultQueue(), new SomeJob([1, 2, 3], 4));
This means that I cannot just pass dependencies into the abstract class from the extending class' constructor. The only way around it, that I can see, is to have a property on the abstract class and then set it in the handler method of the extended class.
abstract class SomeAbstractClass extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $configOne;
protected $configTwo;
protected $userRepository;
public function __construct()
{
$this->configOne = config('someConfig.valueOne');
$this->configTwo = config('someConfig.valueTwo');
}
public function doSomethingWithUserRepository()
{
return $this->userRepository->doSomething();
}
}
class SomeClass extends SomeAbstractClass
{
public function __construct(array $someData, int $someMoreData)
{
parent::__construct();
$this->someData = $someData;
$this->someMoreData = $someMoreData;
}
public function handle(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
}
This works as intended, but it does not seem like the correct way to do it. It seems a bit hacky even if it works. Is there a way to get around this? This must he a pretty common problem, also outside of Laravel.
Because the defined constructor is used to deliver data in the jobs in Laravel, so in this case, you have to treat handle() as the "constructor" method.
So consider this example:
<?php
abstract class SomeAbstractClass extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $configOne;
protected $configTwo;
protected $userRepository;
public function __construct()
{
$this->configOne = config('someConfig.valueOne');
$this->configTwo = config('someConfig.valueTwo');
}
public function handle(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
protected function doSomethingWithUserRepository()
{
return $this->userRepository->doSomething();
}
}
class SomeClass extends SomeAbstractClass
{
public function __construct(array $someData, int $someMoreData)
{
parent::__construct();
$this->someData = $someData;
$this->someMoreData = $someMoreData;
}
public function handle(UserRepository $userRepository)
{
parent::handle($userRepository);
// you can do whatever you liiike
$this->doSomethingWithUserRepository();
}
}

DI, ServiceProvider, abstract parent and Laravel 5.3

I have some problem and little misunderstanding Laravel SP (ServiceProvider). I have abstract class Repository and her Interface:
abstract class Repository implements RepositoryInterface {
private $model;
private $parser;
public function __construct() {
$this->model = new $this->model_name();
} }
interface RepositoryInterface {
public function create(array $attributes);
public function update($id, array $attributes);
public function delete($id);
public function all();
public function find($id);
public function filter(array $parameters, $query=null);
public function query(array $parameters, $query=null); }
and some child UserRepository for example:
class UserRepository extends Repository implements UserRepositoryInterface {
protected $model_name = "App\Models\User";
public function __construct() {
parent::__construct();
}
public function activation($user_id) {
return "user";
}
public function deactivation($user_id) {
return "user";
} }
and simple ModelParser class:
class ModelParser {
protected $parameters;
protected $model;
public function __construct($model) {
$this->model = $model;
} }
This work fine, but I would pass ModelParser as DI in my construct of abstract Repository with parameter $model. I dont have idea. How should I do it ?
I use it like this:
class UserController extends Controller {
private $repository;
public function __construct(UserRepository $repository) {
$this->repository = $repository;
} }
Well it's kinda complicated since your ModelParser requires a $model as it's parameter. And because this $model may vary depends on its repository, it will be too complicated if we're trying to resolve it using Laravel service container binding.
There's an easier approach, we can make the ModelParser class's constructor receive an optional $model parameter. Then we can add an additional method to set this $model property like so:
namespace App\Models;
class ModelParser
{
protected $parameters;
protected $model;
// Make $model parameter optional by providing default value.
public function __construct($model = null) {
$this->model = $model;
}
// Add setter method for $model.
public function setModel($model)
{
$this->model = $model;
return $this;
}
}
And now you can inject the ModelParser into your abstract Repository class. Laravel will easily resolve this ModelParser parameter
namespace App\Models;
use App\Models\ModelParser;
use App\Models\RepositoryInterface;
abstract class Repository implements RepositoryInterface
{
private $model;
private $parser;
// Pass ModelParser instance to your constructor!
public function __construct(ModelParser $parser)
{
$this->model = new $this->model_name();
// Set the parser's model property.
$this->parser = $parser->setModel($this->model);
}
// Rest of your code.
}
And if you're extending the abstract Repository class, you still have to pass this ModelParser to the constructor like so:
namespace App\Models;
use App\Models\ModelParser;
use App\Models\UserRepositoryInterface;
class UserRepository extends Repository implements UserRepositoryInterface
{
protected $model_name = "App\Models\User";
public function __construct(ModelParser $parser)
{
parent::__construct($parser);
}
}
Actually, if you're not planning to pass another parameter or perform something else during the class instantiation, you can simply remove the __construct() method from UserRepository and rely on its parent (the abstract Repository).
Hope this help!

Using Doctrine's EntityManager in a repository design

I'm trying to implement a repository pattern in my zend framework 2 application. I have made a service
<?php
class UserService {
private $userRepository;
public function __construct(IUserRepository $repo) {
$this -> userRepository = $repo;
}
public function createUser($params) {
$this -> userRepository -> create($params);
}
public function findAllUsers() {
return $this -> userRepository -> getAllUsers();
}
}
which has a repository that implements an interface
class UserRepository implements IUserRepository {
public function getAllUsers() {
//return all users
}
public function getUserById($id) {
}
public function getOneUser($params){
}
public function getUsers($params){
}
public function create($params){
}
public function update($params){
}
public function delete($params){
}
}
<?php
interface IUserRepository {
public function getAllUsers();
public function getUserById($id);
public function getOneUser($params);
public function getUsers($params);
public function create($params);
public function update($params);
public function delete($params);
}
In my module.php I make use of dependency injection to determine which repository I inject into a controller
public function getControllerConfig() {
return array('factories' => array(
'My\Controller\Accounts' => function(){
return new AccountsController(new UserRepository());
},
),
);
}
In my controller I pass the repository to my service
class AccountsController extends AbstractActionController {
private $service;
public function __construct(IUserRepository $repo) {
$this->service = new UserService($repo);
}
public function indexAction() {
$all_users = $this->service->findAllUsers();
return new ViewModel(array('users' => $all_users));
}
}
My problem is that I'm using Doctrine as Orm and I want to use the entitymanager in my repositories but I don't know how to do that, any ideas and feedback are appreciated
There are several ways to do this, of course. The typical way you'd do this kind of thing in a ZF2/D2 project would be to start with DoctrineORMModule.
That module exposes Doctrine's EntityManager via the ZF2 Service Manager in a variety of handy ways (you can $sm->get('doctrine.entitymanager.orm_default') to explicitly get the EM instance).
Once you can get your entitymanager from the SM, you write a factory for your repository, and inject the EM.
That said, there's a cleaner way. Doctrine has built-in support for repositories, and you can extend the default implementation.
Your repository would then look like this:
<?php
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository implements IUserRepository {
public function getAllUsers() {
return $this->findAll();
}
// ...
}
Just remember to add the repository class to the User Entity's metadata. For example, with an annotation:
/**
* #ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
class User
{
}

Categories