interface UserRepositoryInterface {
public function getUser($userType, $login): Builder;
}
class UserRepository implements UserRepositoryInterface {
public function getUser('clientA', 'user-login', EmailValidatorInterface $emailValidator, PhoneValidatorInterface $phoneValidator): Builder {}
}
Method UserRepository#getUser throws an error because he is not abiding by the contract UserRepositoryInterface#getUser. How can I remove an error, while using method injection?
There are several ways how to solve this kind of error. I 'm not a big fan of overriding the signature of an interface provided method. I prefer DI over the constructor of a class.
Dependency Injection with the constructor
This one makes more sence in my eyes, because it is cleaner. Before overriding interface method signatures you 'll always have to check if the additional parameters are used in more than that one method. If this is the case, just inject the dependency over the constructor.
<?php
declare(strict_types=1);
namespace Marcel;
class UserRepository implements UserRepositoryInterface
{
protected EmailValidatorInterface $emailValidator;
protected PhoneValidatorInterface $phoneValidator;
public function __construct(
EmailValidatorInterface $emailValidator,
PhoneValidatorInterface $phoneValidator
) {
$this->emailValidator = $emailValidator;
$this->phoneValidator = $phoneValidator;
}
public function getUser(string $userType, string $login): Builder
{
// $this->emailValidator and $this->phoneValidator are available
}
}
As you can see this one is exact what the interface implements. No additional parameters. Just clean and simple dependency injection. This type of injection needs a simple factory or something else, that initializes the dependencies.
Extending an interface implemented method
If you can not use dependency injection - for whatever reason - you can extend the method signature from the interface by providing a default value for each additional parameter.
<?php
declare(strict_types=1);
namespace Marcel;
class UserRepository implements UserRepositoryInterface
{
public function getUser(
string $userType,
string $login,
?EmailValidatorInterface $emailValidator = null,
?PhoneValidatorInterface $phoneValidator = null
): Builder
{
// $emailValidator and $phoneValidator are available
// just validate them against null
}
}
This is ugly af but it will work. What happens, when you check if a class implements the UserRepositoryInterface interface? It just secures, that there is a method getUser, which takes two parameters. The interface knows nothing about any other parameters. This is inconsistency in its purest form.
In most DI systems you’d solve this in the constructor instead.
interface UserRepositoryInterface {
public function getUser($userType, $login): Builder;
}
class UserRepository implements UserRepositoryInterface {
public function __construct(
public readonly EmailValidatorInterface $emailValidator,
public readonly PhoneValidatorInterface $phoneValidator
){}
public function getUser('clientA', 'user-login'): Builder {}
}
Related
Currently I am working on Shopware 6 extension, which is based on Symfony. What I don’t understand, is how to implement abstract classes and dependency injection.
So I want to be able to refactor the code, and to use those methods often, but in another context (with another repository)
<?php
declare(strict_types=1);
namespace WShop\Service;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Uuid\Uuid;
/**
* Service for writing Products
*/
class ProductService
{
private EntityRepository $productRepository;
private MediaImageService $mediaImageService;
private EntityRepository $productMediaRepository;
public function __construct(
EntityRepository $productRepository,
MediaImageService $mediaImageService,
EntityRepository $productMediaRepository
)
{
$this->productRepository = $productRepository;
$this->mediaImageService = $mediaImageService;
$this->productMediaRepository = $productMediaRepository;
}
private function createProduct(array $data, Context $context = null): void
{
$context = $context ?? Context::createDefaultContext();
$this->productRepository->create([
$data
], $context);
}
public function updateProduct(array $data): void
{
$this->productRepository->update([$data], Context::createDefaultContext());
}
public function getExistingProductId(string $productNumber): ?string
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productNumber', $productNumber));
return $this->productRepository->searchIds($criteria,
Context::createDefaultContext())->firstId();
}
}
As you can see, there are dependency injection inside construct (Product Repository). Now my question is, how am I able to create abstract class, that is storing those methods, but the child classes is going to kind of "rewrite" parent construct with repository that is needed? For example, I want to use getDataId (Now it is called getExistingProductId, but it is going to be refactored and renamed in abstract class) method on product repository, but for the next class I want to use the same method on categors repository?
Service.xml aka Dependency Injector
<service id="wshop_product_service" class="WShop\Service\ProductService">
<argument type="service" id="product.repository"/>
<argument id="wshop_media_image_service" type="service"/>
<argument type="service" id="product_media.repository"/>
</service>
I am kind of new into OOP. Please provide good example and code explanation. Thanks!
If I understood you correctly, you just want the first argument to be interchangeable and the 3 methods in your example should be implemented in the abstract. Here's one idea for that.
The abstract:
abstract class AbstractEntityService
{
protected EntityRepository $repository;
public function __construct(EntityRepository $repository)
{
$this->repository = $repository;
}
public function create(array $data, ?Context $context = null): void
{
$context = $context ?? Context::createDefaultContext();
$this->repository->create([
$data
], $context);
}
public function update(array $data): void
{
$this->repository->update([$data], Context::createDefaultContext());
}
abstract public function getDataId(array $params): ?string;
protected function searchId(Criteria $criteria): ?string
{
return $this->repository->searchIds(
$criteria,
Context::createDefaultContext()
)->firstId();
}
}
You take the repository in the constructor and implement all your general methods regarding the generic repositories in the abstract. The getDataId method you want to implement in the extending class, since you use a specific criteria for each one (presumably). So you just force the implementation in the extending class by defining an abstract signature.
Your service class:
class ProductService extends AbstractEntityService
{
private MediaImageService $mediaImageService;
private EntityRepository $productMediaRepository;
public function __construct(
EntityRepository $productRepository,
MediaImageService $mediaImageService,
EntityRepository $productMediaRepository
) {
parent::__construct($productRepository);
$this->mediaImageService = $mediaImageService;
$this->productMediaRepository = $productMediaRepository;
}
public function getDataId(array $params): ?string
{
if (!isset($params['productNumber'])) {
return null;
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productNumber', $params['productNumber']));
return $this->searchId($criteria);
}
// your other methods using the injected services
}
In the extending class you pass only the repository to the parent constructor since the other injected services are used only in this specific instance. You implement getDataId where you create your specific criteria and call the protected (since it should only be used by extensions) searchId method with the criteria.
I have a Task class that extends an abstract class, TaskBase.
// class Task
class Task extends TaskBase {
/**
* #var Processor
*/
private $processor
public function __construct(Processor $processor)
{
$this->processor = $processor;
}
public function process() : ProcessResult
{
return $this->processor->execute();
}
}
// abstract class TaskBase
abstract class TaskBase implements CommanTask {
protected function getKey(): string
{
}
}
This TaskBase implements an interface CommanTask, which contains the following method.
interface CommanTask {
public function process(): ProcessResult;
}
Now I need a new task class TaskMultiple, and it's process() method needs to return an array of ProcessResult instead of one ProcessResult.
How can I extend abstract class TaskBase for this new TaskMultiple class?
If you implement an interface, you need to actually comply with the contract laid out by the interface.
The interface you propose says that process() returns a ProcessResult. If you could change that in an implementing class to returning an array, then consumers of the class wouldn't be able to trust the contract specified by the interface.
They would try to use the result of TaskMultiple::process(), and since they interface says it would return a ProcessResult, a fatal error would soon happen when it tried to treat it as such (e.g. by accessing a method for that class), and it wasn't that.
Your solutions are:
If you are on PHP 8, you could use union types:
interface Task {
public function process(): ProcessResult|iterable;
}
It would work, but it's ugly. Now consumers of the any Task implementing service would need to check on the result of process() to see if it's a single ProcessResult, or a collection (presumably of ProcessResults).
If you are on PHP < 8, you could make it work simply by removing the type hint:
interface Task {
public function process()
}
Now consumers would only know that there is a process() method, but you'd have no type information at all. Uglier still.
Better, simply create different interfaces like Task and MultipleTask:
interface Task {
public function process(): ProcessResult;
}
interface MultipleTask {
public function process(): iterable;
}
Since your BaseTask includes nothing to help implement the class (only includes an abstract method, making it already very similar to an interface), move the implements to each of the concrete task classes:
class ExampleTask extends BaseTask implements Task
{ /** implementation **/ }
class ExampleMultipleTask extends BaseTask implements MultipleTask
{ /** implementation **/ }
How to get translator in model?
Inside view we can get translator using this code
$this->translate('Text')
Inside controller we can get translator using this code
$translator=$this->getServiceLocator()->get('translator');
$translator->translate("Text") ;
But how to get translator in model?
I'd tried so many ways to get service locator in models
2 of those
1)Using MVC events
$e=new MvcEvent();
$sm=$e->getApplication()->getServiceManager();
$this->translator = $sm->get('translator');
if i pring $sm it is showing null. but it works fine in Model.php onBootstrap
2)Created one model which implements ServiceLocatorAwareInterface
SomeModel.php
<?php
namespace Web\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class SomeModel implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $locator)
{
$this->services = $locator;
}
public function getServiceLocator()
{
return $this->services;
}
}
and used that inside my model
$sl = new SomeModel();
$sm=$sl->getServiceManager();
var_dump($sm); exit;
$this->translator = $sm->get('translator');
this is also printing null.
If you don't need the servicemanager instance in your model, simply inject translator instance to it.
For example:
// Your model's constructor
class MyModel {
// Use the trait if your php version >= 5.4.0
use \Zend\I18n\Translator\TranslatorAwareTrait;
public function __construct( $translator )
{
$this->setTranslator( $translator );
}
public function modelMethodWhichNeedsToUseTranslator()
{
// ...
$text = $this->getTranslator()->translate('lorem ipsum');
// ...
}
}
When you creating your model first time on service or controller level
class someClass implements ServiceLocatorAwareInterface {
public function theMethodWhichCreatesYourModelInstance()
{
// ...
$sm = $this->getServiceLocator();
$model = new \Namespace\MyModel( $sm->get('translator') )
// ...
}
}
If you need to instantiate your model (new MyModel();) on multiple methods/classes, consider to writing a factory for it.
Here is a nice article about Dependency Injection and PHP by Ralph Schindler for more detailed comments about this approach.
For your Model class to be ServiceLocatorAware, you not only need to implement the interface, you also need to make your model a service of the service manager, and fetch the model from there.
Add your model to the service manager, since it doesn't appear to need any constructor params, it's invokable, so you can add it to the invokables array in service manager config. You can do that by using the getServiceConfig() method in your Module class...
class Module
{
public function getServiceConfig()
{
return array(
'invokables' => array(
'SomeModel' => 'Fully\Qualified\ClassName\To\SomeModel',
),
);
}
}
Then, instead of calling the new keyword to create your model instance, you fetch it from the service manager, for instance, by calling the getServiceLocator() method in a controller action...
public function fooAction()
{
$sm = $this->getServiceLocator();
$model = $sm->get('SomeModel');
}
When your model is fetched from the service manager, a service initializer will look to see if it implements the ServiceLocatorAwareInterface and automatically call setServiceLocator() if it does, passing it an instance of the service manager.
I like and use the Yii framework, particularly its "components", which are lazily-instantiated and you can swap them in or out in your configuration file. Kind of like a dependency injection-lite.
I try to keep the business logic of my code completely independent of the Framework, in case I ever want to repurpose that code, or even change frameworks.
Let's say I have a class in my service layer called AccountService, which implements IAccountService and has a one-argument constructor.
interface IAccountService
{
function getUserById($id);
}
class AccountService implements IAccountService
{
private $_userRepository;
public function __construct(IUserRepository $userRepository) {
$this->_userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
Great. So far, it's totally framework-free. Now I'd like to expose this as a Yii component, so it can be lazily-instantiated and easily used by Yii controllers and other Yii components.
But Yii components (which implement IApplicationComponent) must have exactly zero constructor arguments, while my class requires one!
Any ideas?
Here's what I've got. I'm not really happy with any of them; they both look over-engineered and I'm detecting a distinct smell from them.
Option 1 - compose: I create a class called "AccountServiceComponent" which implements Yii's IApplicationComponent. It cannot extend my AccountService class, because of the constructor, but it could instantiate one as a private member and wrap all of its methods, like so:
class AccountServiceComponent implements IApplicationComponent, IAccountservice
{
private $_accountService;
public __construct() {
$this->_accountService = new AccountService(new UserRepository());
}
public getUserById($id) {
return $this->_accountService->getUserById($id);
}
}
Cons: I'll have to wrap every method like that, which is tedious and could lead to "baklava code." Especially considering that there'll be multiple service classes, each with multiple methods.
Option 2 - mixin: (Or behavior or trait or whatever it's called these days.)
Yii (having been written prior to PHP 5.4) offers "behaviors" in the form of a class which implements IBehavior. I could create a behavior class which extends my service, and attach it to a component:
class AccountServicesBehavior extends AccountService implements IBehavior
{
// Implement the few required methods here
}
class AccountServiceComponent implements IApplicationComponent
{
public function __construct() {
$accountService = new AccountService(new UserRepository());
$this->attachBehavior($accountService);
}
Cons: My component no longer officially implements IAccountService. Also seems to be getting excessive with the layering.
Option 3 - optional constructor parameters:
I could just make the constructor parameter to my service class optional, and then extend it into a component:
class AccountService implements IAccountService
{
public $userRepository;
public function __construct(IUserRepository $userRepository = null) {
$this->userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
class AccountServiceComponent extends AccountService implements IApplicationComponent
{
}
Cons: The optional constructor parameter means this class coudld now be instantiated without supplying it with everything it needs.
...so, any other options I'm missing? Or am I just going to have to choose the one that disturbs me the least?
Option 3 but with an object as the optional argument sounds best imo:
public function __construct(IUserRepository $userRepository = new UserRepository()) {
$this->userRepository = $userRepository;
}
My Dispatcher is "choosing" correct Controller; then creating Controller's instance (DependencyInjectionContainer is passed to Controller constructor); then calling some Controller's method...
class UserController extends Controller
{
public function __construct(DependencyInjectionContainer $injection) {
$this->container = $injection;
}
public function detailsAction() {
...
}
}
DependencyInjectionContainer contains DB adapter object, Config object etc.
Now let's see what detailsAction() method contains...
public function detailsAction() {
$model = new UserModel();
$model->getDetails(12345);
}
As you see I'm creating new instance of UserModel and calling getDetails methods.
Model's getDetails() method should connect to db to get information about user. To connect to DB UserModel should be able to access DB adapter.
What is the right way to pass DependencyInjectionContainer to the UserModel?
I think that this way is wrong...
public function detailsAction() {
$model = new UserModel($this->container);
$model->getDetails(12345);
}
Instead of injecting the entire DI Container into your classes, you should inject only the dependencies you need.
Your UserController requires a DB Adapter (let's call this interface IDBAdapter). In C# this might look like this:
public class UserController
{
private readonly IDBAdapter db;
public UserController(IDBAdapter db)
{
if (db == null)
{
throw new ArgumentNullException("db");
}
this.db = db;
}
public void DetailsAction()
{
var model = new UserModel(this.db);
model.GetDetails(12345);
}
}
In this case we are injectiing the dependency into the UserModel. In most cases, however, I would tend to consider it a DI smell if the UserController only takes a dependency to pass it on, so a better approach might be for the UserController to take a dependency on an Abstract Factory like this one:
public interface IUserModelFactory
{
UserModel Create();
}
In this variation, the UserController might look like this:
public class UserController
{
private readonly IUserModelFactory factory;
public UserController(IUserModelFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory");
}
this.factory = factory;
}
public void DetailsAction()
{
var model = this.factory.Create();
model.GetDetails(12345);
}
}
and you could define a concrete UserModelFactory that takes a dependency on IDBAdapter:
public class UserModelFactory : IUserModelFactory
{
private readonly IDBAdapter db;
public UserModelFactory(IDBAdapter db)
{
if (db == null)
{
throw new ArgumentNullException("db");
}
this.db = db;
}
public UserModel Create()
{
return new UserModel(this.db);
}
}
This gives you better separation of concerns.
If you need more than one dependency, you just inject them through the constructor. When you start to get too many, it's a sign that you are violating the Single Responsibility Principle, and it's time to refactor to Aggregate Services.
I'd use a singleton object for all config parameters :
You set it up in your bootstrap, then choose to use it directly or pass it as parameter in your objects.
The idea being to have one method all around to retrieve your config data.
You may then provide an abstract class for db manipulation which uses your config. singleton.
DependancyInjection can still be used to override your default data.
The above link in the comment (possible 'duplicate') concludes on using constructor injection : this is close to your current method.
However if I try to figure how your model works, I guess you will have many other model classes other than "userModel". Thus an abstract class using a config singleton might be a good solution : all your next model classes will just extend this abstract class, and you don't have to worry about your config.
On the other hand, your solution is good to me as long as your dependanceInjectionContainer changes often.