The class A has a constructor with some Interfaces.
The class A has a __construct where a new object $this->foo = new FooController(); will be spawned
The class B extends A but has its own __construct with only one param. calling parent::__construct() with the same params of A::__construct does not work since this changes the B::__construct
class B accesses $this->foo which was created in the constructor of class A
How create $b = new B($some_variable='foo'); without reusing all constructor variables of A::class and have the $b->foo avaliable (which was created in A::class ?
Your hints are highly appreciated.
class A extends AbstractController
{
protected FooController $foo;
protected EntityManagerInterface $manager;
protected Security $security;
protected RequestStack $requestStack;
public function __construct(EntityManagerInterface $manager, Security $security, RequestStack $requestStack)
{
$this->manager = $manager;
$this->security = $security;
$this->requestStack = $requestStack;
$this->foo = new FooController();
}
}
class B extends A
{
public function __construct($some_variable)
{
parent::__construct()
$this->foo = $some_variable;
}
}
Related
In some particular situations, we need to call the parent constructor parent::__construct(); after properties initialization like the bellow code (Symfony Command code, for more details see Symfony documentation):
<?php
class CreateUserCommand extends Command
{
// ...
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager, bool $requirePassword = false)
{
// best practices recommend to call the parent constructor first and
// then set your own properties. That wouldn't work in this case
// because configure() needs the properties set in this constructor
$this->entityManager = $entityManager;
$this->requirePassword = $requirePassword;
parent::__construct();
}
I wonder how we can deal with this case if we make the Constructor in PHP 8 property promotion style :
<?php
public function __construct(private EntityManagerInterface $entityManager, private bool
$requirePassword = false)
{
parent::__construct();
}
I have my base controller and my content controller extending it like below, and I'm getting a Call to a member function error() on null so my question is:
Do I have to call the parent constructor?
If "yes" then is it better to have a service because the parent controller has some injected dependencies that I don't want to inject myself by calling the parent constructor
use Psr\Log\LoggerInterface;
class BaseController extends AbstractController
{
/** #var LoggerInterface */
protected $logger;
/**
* BaseController constructor.
*/
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
class ContentController extends BaseController
{
private $contentRepository;
private $breadcrumbService;
public function __construct(
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
}
public function contentPage(...)
{
try {
....
} catch (\Throwable $exception) {
$this->logger->error(...);
throw $exception;
}
}
You do not need to call the constructor. But you do need to set the $logger property if you want to use it.
class ContentController extends BaseController
{
private $contentRepository;
private $breadcrumbService;
public function __construct(
LoggerInterface $logger,
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
$this->logger = $logger;
}
}
With the above you no longer need to call parent::__construct(). But it's simply good practice, painless and harmless:
Just do:
public function __construct(
LoggerInterface $logger,
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
parent::__construct($logger);
}
But if the only purpose of BaseController is to provide some logging methods, it's probably better to simply inject the logger service in ContentController and use it directly.
I am trying to inject my BaseService within antoher service where I need to call my repository that I wrote in BaseService.
I think it's pretty simple thing but it marks __construct part with :
Missing parent constructor call
I made that logic in BaseService and it works
class BaseService
{
/** #var ContainerInterface */
public $container;
public $em;
public function __construct(ContainerInterface $container, EntityManagerInterface $em)
{
$this->container = $container;
$this->em = $em;
}
/**
* #return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
*/
public function getMyDataRepository()
{
return $this->em->getRepository(MyData::class);
}
}
and my other service:
class DataService extends AbstractAdmin
{
public function __construct(BaseService $baseService)
{
$this->baseService = $baseService;
}
public function getTransactions(Card $card)
{
return $this->getMyDataRepository()
->createQueryBuilder('c')
->getQuery();
}
}
I found an answer.
I did it like this:
public $baseService;
public function __construct($code, $class, $baseControllerName, BaseService $baseService)
{
parent::__construct($code, $class, $baseControllerName);
$this->baseService = $baseService;
}
As Abstract Admin has its constructor.
You forgot to add parent constructor of AbstractAdmin on DataService.
class DataService extends AbstractAdmin
{
public function __construct(BaseService $baseService)
{
parent::__construct(AbstractAdmin dependencies goes here);
$this->baseService = $baseService;
}
public function getTransactions(Card $card)
{
return $this->getMyDataRepository()
->createQueryBuilder('c')
->getQuery();
}
I dont know which dependencies need your AbstractAdmin
I am trying to test my form extending TypeTestCase class
class ProjectTypeTest extends TypeTestCase
{
private $entityManager;
private $securityContext;
private $translator;
private $formFactory;
protected function setUp()
{
// mock any dependencies
$this->entityManager = $this->createMock("Doctrine\ORM\EntityManagerInterface");
$this->securityContext = $this->createMock("Symfony\Component\Security\Core\SecurityContextInterface");
$this->translator = $this->createMock("Symfony\Component\Translation\TranslatorInterface");
$this->formFactory = $this->createMock("AppBundle\FormTemplate\Factory\FormFactory");
}
public function testSubmitValidData()
{
$type = new ProjectType($this->entityManager,$this->securityContext, $this->translator, $this->formFactory);
$this->factory->create($type);
}
}
But, When I called $this->factory->create($type), returns :
Call to a member function create() on null
the factory property is null !
I'am using Symfony\Component\Form\Test\TypeTestCase and also I've used use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase and returns the same outcome.
Or, What am I doing bad ?.
Or, How do I test a Form ?
The problem is $this->factory is not being initialized. That should be done by FormIntegrationTestCase, which is extended by TypeTestCase and then by your test class.
Your setUp() is overriding the original setUp(), which initializes $this->factory, so you should call the parent one:
protected function setUp()
{
parent::setUp();
// mock any dependencies
$this->entityManager = $this->createMock("Doctrine\ORM\EntityManagerInterface");
$this->securityContext = $this->createMock("Symfony\Component\Security\Core\SecurityContextInterface");
$this->translator = $this->createMock("Symfony\Component\Translation\TranslatorInterface");
$this->formFactory = $this->createMock("AppBundle\FormTemplate\Factory\FormFactory");
}
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!