Is it bad practice to inject several arguments to the constructor? - php

I'm developing a quite complex logistics management system which will keep growing into several other ERP related modules. Therefore, I am trying to have as much of the SRP and Open/Close Principles in place for ease of extension and domain based management.
Therefore, I decided to use Laravel and the following pattern (not sure if this has a name or not):
I will use the PRODUCT object for my example.
An object/entity/domain has a Class
class ProductService {}
This class has a Service Provider which is included in the providers array and is also autoloaded:
ProductServiceServiceProvider
The service provider instantiate (makes) the ProductRepository which is an interface.
The interface currently has a MySQL (and some Eloquent) called EloquentProductRepository implementation(s) and a ProductRepositoryServiceProvider binds the implementation which is also loaded and in the providers array.
Now a product has many different attributes and relationships with other domains and because the other domains (or entities) need to be fully detached and again abiding with the above principle (SRP etc..) I decided to also have the same structure for them as i do for the product...I know some might think that this is too much but we need to have the system very extendable and to be honest I like to be organised and have a uniform pattern (it doesn't take that much more time and saves me a lot later).
My question is this. The ProductService which handles all the business logic of the Product and makes the "Product" what it is will have several dependencies injected on creation of it's instance through the constructor.
This is what it has at the moment:
namespace Ecommerce\Services\Product;
use Ecommerce\Repositories\Product\ProductRepository;
use Ecommerce\Services\ShopEntity\ShopEntityDescriptionService;
use Content\Services\Entity\EntitySeoService;
use Content\Services\Entity\EntitySlugService;
use Ecommerce\Services\Tax\TaxService;
use Ecommerce\Services\Product\ProductAttributeService;
use Ecommerce\Services\Product\ProductCustomAttributeService;
use Ecommerce\Services\Product\ProductVolumeDiscountService;
use Ecommerce\Services\Product\ProductWeightAttributeService;
use Ecommerce\Services\Product\ProductDimensionAttributeService;
/**
* Class ProductService
* #package Ecommerce\Services\Product
*/
class ProductService {
/**
* #var ProductRepository
*/
protected $productRepo;
/**
* #var ShopEntityDescriptionService
*/
protected $entityDescription;
/**
* #var EntitySeoService
*/
protected $entitySeo;
/**
* #var EntitySlugService
*/
protected $entitySlug;
/**
* #var TaxService
*/
protected $tax;
/**
* #var ProductAttributeService
*/
protected $attribute;
/**
* #var ProductCustomAttributeService
*/
protected $customAttribute;
/**
* #var ProductVolumeDiscountService
*/
protected $volumeDiscount;
/**
* #var ProductDimensionAttributeService
*/
protected $dimension;
/**
* #var ProductWeightAttributeService
*/
protected $weight;
/**
* #var int
*/
protected $entityType = 3;
public function __construct(ProductRepository $productRepo, ShopEntityDescriptionService $entityDescription, EntitySeoService $entitySeo, EntitySlugService $entitySlug, TaxService $tax, ProductAttributeService $attribute, ProductCustomAttributeService $customAttribute, ProductVolumeDiscountService $volumeDiscount, ProductDimensionAttributeService $dimension, ProductWeightAttributeService $weight)
{
$this->productRepo = $productRepo;
$this->entityDescription = $entityDescription;
$this->entitySeo = $entitySeo;
$this->entitySlug = $entitySlug;
$this->tax = $tax;
$this->attribute = $attribute;
$this->customAttribute = $customAttribute;
$this->volumeDiscount = $volumeDiscount;
$this->dimension = $dimension;
$this->weight = $weight;
}
`
Is it bad practice to have as much arguments passed to the constructor in PHP (please ignore the long names of the services as these might change when the ERP namespaces have been decided upon)?
As answered by Ben below, in this case it is not. My question was not related to OOP but more to performance etc.. The reason being is that this particular class ProductService is what web deves would do with a controller, i.e. they would probably (and against principles) add all DB relationships in one ProductController which handles repository services (db etc..) and attaches relationships and then it suddenly becomes your business logic.
In my application (and I see most applications this way), the web layer is just another layer. MVC takes care of the web layer and sometimes other Apis too but I will not have any logic except related to views and JS frameworks in my MVC. All of this is in my software.
In conclusion: I know that this is a very SOLID design, the dependencies are injected and they really are dependencies (i.e. a product must have tax and a product does have weight etc..) and they can easily be swapped with other classes thanks to the interfaces and ServiceProviders. Now thanks to the answers, I also know that it is Okay to inject so many dependencies in constructor.
I will eventually write an article about the design patterns which I use and why I use them in different scenarios so follow me if you're interested in such.
Thanks everyone

Generally, no, It's not a bad practice, in most cases. But in your case, as said in the comments by #zerkms, it looks like your class is depending on a lot of dependencies, and you should look into it, and think on how to minimize the dependencies, but if you're actually using them and they should be there, I don't see a problem at all.
However, you should be using a Dependency Injection Container (DIC).
An dependency injection container, is basically a tool which creates the class by the namespace you provide, and it creates the instance including all the dependencies. You can also share objects, so it won't create a new instance of it while creating the dependencies.
I suggest you to ue Auryn DIC
Usage:
$provider = new Provider();
$class = $provider->make("My\\App\MyClass");
What happens here is this:
namespace My\App;
use Dependencies\DependencyOne,
Dependencies\DependencyTwo,
Dependencies\DependencyThree;
class MyClass {
public function __construct(DependencyOne $one, Dependency $two, DependencyThree $three) {
// .....
}
}
Basically, the Provider#make(namespace) creates an instance of the given namespace, and creates the needed instances of it's consturctor's parameters and all parameter's constructors parameters and so on.

Related

Getter for a object from the JSON-Field

I have 2 entities:
class Opponent
{
...
...
...
}
class Process
{
/**
* #var array
*
* #ORM\Column(name="answers_in_related_questionnaires", type="json", nullable=true)
*/
private $answersInRelatedQuestionnaires = [];
.
.
.
}
I have in the field answersInRelatedQuestionnaires amongst other things the object opponent
"opponent": {
"id":1088,
"name":"Inora Life Versicherung"
}
I want to write a getter in the entity process, that gets not only the both values id and name from opponent, but the whole entity Opponent. Something like this:
private function getOpponent() : Opponent
{
$id = $this->answersInRelatedQuestionnaires['opponent']['id'];
return $entityManager->getRepository(Opponent::class)->find($id)
}
I have read, that using of the entity manager within the entity is not a good idea. Which solutions for my issue are there? Can I use the Process repository in the Process entity?
You should not inject entity manager in an entity, it's a very bad practice and violates the separation of concerns between classes. BUT if you really want you indeed can inject entity manager in your entity.
GOOD PRACTICE:
Create a Model/Process class and include there any functionality that concerns your model. Doctrine entities are not model classes. In Model/Process you can inject the entity manager and any other service, you need.
EDIT: By creating a Model/Process class I mean creating a class named Process inside Model directory in your /src folder. Your path of your class will be: /src/Model/Process. Of course, the name of the directory or the class can by anything, but this is a typical convention. Your Model class should be responsible for all your business logic, such as validation of your model etc. This will indeed make your code structure more complicated but will be a savor in the long run for large scale projects. You will also need a Model/ProcessManager to properly populate Process model in different cases (e.g. when loaded from Database, user form etc.) Of course, in the end it's all a matter of trade-off between complexity and sustainability.
An interesting approach about models in Symfony, mostly applicable in large scale projects, can be found here.
ALTERNATIVES:
If you access the opponent attribute only after an entity has been loaded you can use Doctrine PostLoad LifecycleCallback to properly set opponent attribute. This is not a bad practice:
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Product
{
// ...
private $opponentObject;
/**
* #ORM\PostLoad
*/
public function onPostLoad(LifecycleEventArgs $args){
$em = $args->getEntityManager();
$id = $this->answersInRelatedQuestionnaires['opponent']['id'];
$this->opponentObject = $em->getRepository(Opponent::class)->find($id);
}
public function getOpponent() {
return $this->opponent;
}
}
Finally if you really really want to inject the entity manager into your entity you can achieve that with dependency injection via autowiring:
use Doctrine\ORM\EntityManagerInterface;
class Process
{
private $em;
public function __contruct(EntityManagerInterface $em)
{
$this->em = $em;
}
....
}

Injecting dependancies - Doctrine: Multiple repositories vs single entity manager

I have a service class that has dependencies on multiple entity repositories, for example 4.
I could inject each repository and end up with many dependencies, or I could inject the entity manager as a single dependency; relying on EntityManager->getRepo('xyz').
Separate dependencies has the benefit of code hinting.
Single dependency means less verbose at construct.
Possibly easier mocking and less setup?
What is considered a better practice?
In this case EntityManager is something like Service Locator. When service depends on EntityManager, it also formally depends on all its API and all related objects (repositories, metatada, etc). Better inject only what you really need:
explicit injection of specific repositories makes your service easier to read and test.
Also, prefer interface over class if possible (ObjectRepository instead of EntityRepository, ObjectManager instead of EntityManager).
I assume, that you must use only one Doctrine Entity Manager in your service dependencies.
But if you want to have code hinting in your IDE, you can do it with phpdoc annotation like this
class SomeServiceWithDoctrineDependency
{
/** #var YourFirstObjectRepository */
protected $firstRepo;
/** #var YourSecondObjectRepository */
protected $secondRepo;
/** #var YourThirdObjectRepository */
protected $thirdRepo;
public function __construct(EntityManagerInterface $entityManager)
{
$this->firstRepo = $entityManager->getRepository('First:Repo');
$this->secondRepo = $entityManager->getRepository('Second:Repo');
$this->thirdRepo = $entityManager->getRepository('Third:Repo');
}
public function getCodeHint()
{
// You get hint here for find method
// $this->thirdRepo()->find()...
}
}

PHP - How should the controller communicate data with the model layer in a proper MVC pattern

I have been researching so many times, asking experts on stackoverflow for the best practice, but I still could not find the solution, I might have got told the correct answer, but I didn't get it.
I always wanted to create a 'proper' MVC pattern to work on my projects, I have been researching for example this http://www.phpro.org/tutorials/Model-View-Controller-MVC.html but I got told that this is awful design, and I should not use the registery pattern anymore.
So I looked at teresko's answer, even though it's very old, can be seen here How should a model be structured in MVC? and I got the idea there, to use a global factory, which will create other factories
The idea I got from there, is to have a big factory, called ServiceFactory which will create instances of other factories like ModelFactory which will create instances of classes that are serving the model layer, and a ViewFactory, which will serve the view (Pretty useless at the moment I think).
But got something like this:
namespace library\application;
use library\application\exceptions\ClassNotFoundException;
use library\application\exceptions\PackageNotFoundException;
class ServiceFactory {
private $mapper;
private $package;
private $services = array();
public function __construct(DataMapper $mapper) {
$this->mapper = $mapper;
}
/**
* Setting a default package
* #param $package string Package path
* #throws exceptions\PackageNotFoundException
*/
public function setDefaultPackage($package) {
if (is_dir('library' . $package)) {
$this->package = 'library' . $package;
return;
}
throw new PackageNotFoundException("The package: " . $package . " was not found.");
}
/**
* #param $name string Class name
* #param null $constructor IF the class needs constructor parameters, use it.
* #return Factory
* #throws exceptions\ClassNotFoundException
*/
public function create($name, $constructor = null) {
$path = $this->package . "\\" . $name;
if (file_exists($path)) {
if ($constructor) {
return new $path($constructor);
}
return new $path;
}
throw new ClassNotFoundException("The requested class was not found: " . $path);
}
/**
* #param $name string Class name
* #param $object object Object to register
*/
public function register($name, $object) {
$this->services[$name] = $object;
}
/**
* #param $name string Class name
* #return Factory
*/
public function get($name) {
if (isset($this->services[$name])) {
return $this->services[$name];
}
return null;
}
}
This is awesome actually, but in my opinion, OO-wise, it's totally wrong, because it's not object-oriented friendly, since the returning object of S ServiceFactory::create() is anonymous, well fine, It's no longer anonymous, because the purpose of the class is to create instances of factories, sure, just added the interface of the factories in return.
But now the next problem, when I want to use the factories to create objects, that should not have the same interface, will cause problems to my IDE, since it doesn't know what is the object I am going to return, it can be Foo, can be Car and one day it can be HelloWorld.
So I always will have to declare a variable with the object when using it in the controller:
/**
* #var $items Items
* #var $categories CategoryModel
*/
$items = parent::getModelFactory()->create('Items');
$items->setDb(parent::getDatabase());
$categories = parent::getModelFactory()->create('Categories');
$categories->setDb(parent::getDatabase());
Now in one of Teresko's answer(s), it says the following rule: A controller can not create instances of the Model layer
So I assume he means like $foo = new Foo(); $foo->method($db); in the controller, but why not? how should a controller actually communicate the model layer?
The main purpose of me creating that god factory class, is to prevent doing new Object() in the controller class, but since it's calling the factory, which creates the instance, it's still considered as I am creating an instance of a model layer in a controller (I think).
I also could cache objects, using ServiceFactory::register() method, but that doesn't change the situation.
So is the ServiceFactory and factories idea fine ? What is the correct way communcating between the controller and the model layer?
Not really, but there aren't any rules in how to implement MVC as long as you have M V C clearly defined and C acts as a coordinator for M and V.
A factory of factories is clearly over engineering, if you don't need it don't implement it. What you need to understand in order to be easy for you to apply MVC is that 1) MVC is part of the UI layer and 2) Model refers to the bits of other layers (like, for example, Business, Persistence or Application/Service) which are used by the UI.
The controller always communicates directly to the Model, but depending on the use case, the Model means that bits of layers mentioned above. In a 'normal' app, it usually is like this: the controller uses an application service (the MVC Model) to update the business model. The service should be injected in the controller constructor.
For querying purposes, the controller can use a "querying service" or a query only repository so that it won't contain persistence logic (like writing sql). It still the same approach like above, only this time, the Model represents Persistence bits.
So you see, the Mvc Model is actually a facade for other layers used by UI and you want the controller not to have direct access to the business layer for example, because the controller should really just coordinate what model to change and what view model to be rendered. In contrast, an application service will implement an application use case using business/persistence objects.
Everything is really about respecting Separation of Concerns and Single Responsibility Principle

Cached user class vs seperate class holding necessary cached fields

I need to cache some info about a user who is logged in (such as security groups, name, username, etc.)
Currently I have a separate class to achieve just this, lets call it CurrentUserHelper. Given a user object, it will cache the appropriate data and save store info in the $_SESSION variable.
The issue is I'm finding a bunch of classes relying just on CurrentUserHelper because they only need a couple of common fields. In fact, most of the functions have the same name as my User class. There's a couple of functions in CurrentUserHelper, such as getSecurityGroupsNames(), that contains a cache of all security group names, but there is no reason this function name could not be in the user class also.
Instead, should I create a CachedUser class and pass this around? This class can extend User, but then I can override getName(), getSecurityGroups(), etc, and returned the cached data, and not preform db requests to get the data.
The downside of passing around a CachedUser object is that this kind of hides the fact the data isn't really up to date if a constructor/function is accepting a type User. I also need to find way to handle merging the entity with Doctrine 2, and making sure entities associating themselves with a CachedUser won't break. If I decide to cache some temporary data (such as # of page views since logged in), this property shouldn't be part of the User class, it's more about the current user's session.
If I continue using the CurrentUserHelper class, maybe I should create an interface and have both CurrentUserHelper and User for the common functionality the two classes would share?
Preface: Extension isn't the best way for these sorts of things.. I'm sure you've heard composition over inheritance shouted at you over and over again. In fact, you can even gain inheritance without using extends!
This sounds like a good use-case for the decorator pattern. Basically, you wrap your existing object with another one that implements the same interface, so it has the same methods as the inner object, but this object's method adds the extra stuff around the method call to the inner object, like caching, for example.
Imagine you have a UserRepository:
/**
* Represents an object capable of providing a user from persistence
*/
interface UserProvider
{
/**
* #param int $id
*
* #return User
*/
public function findUser($id);
}
/**
* Our object that already does the stuff we want to do, without caching
*/
class UserRepository implements UserProvider
{
/**
* #var DbAbstraction
*/
protected $db;
/**
* #var UserMapper
*/
protected $mapper;
/**
* #param DbAbstraction $db
* #param UserMapper $mapper
*/
public function __construct(DbAbstraction $db, UserMapper $mapper)
{
$this->db = $db;
$this->mapper = $mapper;
}
/**
* {#inheritDoc}
*/
public function findUser($id)
{
$data = $this->db->find(['id' => $id]);
/** Data mapper pattern - map data to the User object **/
$user = $this->mapper->map($data);
return $user;
}
}
The above is a really simple example. It'll retrieve the user data from it's persistence (a database, filesystem, whatever), map that data to an actual User object, then return it.
Great, so now we want to add caching. Where should we do this, within the UserRepository? No, because it's not the responsibility of the repository to perform caching, even if you Dependency Inject a caching object (or even a logger!).. this is where the decorator pattern would come in useful.
/**
* Decorates the UserRepository to provide caching
*/
class CachedUserRepository implements UserProvider
{
/**
* #var UserRepository
*/
protected $repo;
/**
* #var CachingImpl
*/
protected $cache;
/**
* #param UserRepository $repo
*/
public function __construct(UserRepository $repo, CachingImpl $cache)
{
$this->repo = $repo;
$this->cache = $cache;
}
/**
* {#inheritDoc}
*
* So, because this class also implements UserProvider, it has to
* have the same method in the interface. We FORWARD the call to
* the ACTUAL user provider, but put caching AROUND it...
*/
public function findUser($id)
{
/** Has this been cached? **/
if ($this->cache->hasKey($id))
{
/**
* Returns your user object, or maps data or whatever
*/
return $this->cache->get($id);
}
/** Hasn't been cached, forward the call to our user repository **/
$user = $this->repo->findUser($id);
/** Store the user in the cache for next time **/
$this->cache->add($id, $user);
return $user;
}
}
Very simply, we've wrapped the original object and method call with some additional caching functionality. The cool thing about this is that, not only can you switch out this cached version for the non-cached version at any time (because they both rely on the same interface), but you can remove the caching completely as a result, just by changing how you instantiate this object (you could take a look at the factory pattern for that, and even decide which factory (abstract factory?) depending on a configuration variable).

How can I reuse columns and events in Doctrine 2 Entities?

Suppose I'm writing a blog app: My Posts Entity should have a createddate, modifieddate, name, and slug property (the slug would be generated when I call setName() for the Entity). I want to reuse this code, but I also want to reuse it on-demand. I may, for instance, have a Log Entity which wants to use createddate, but not modifieddate or slug functionality.
Class inheritance, whether via traits (mixins) or abstract classes, seems to be an insufficient, or at least improper, approach to reusing this functionality, as it doesn't pass the is-a test. After all, the Entity has-a createddate, but isn't a createddate itself.. so we should use Composition rather than Inheritance, right?. However, observer doesn't seem to work here, since while I want to use this functionality on-demand, Doctrine's use of annotation and object properties seem to make horizontal injection difficult (and expensive?) without use of Reflection.
To show some code and simplify the question a bit: can I at once inject the following definition and functionality into an Entity without breaking DRY or good OOP (proper use of composition / inheritance) practice?
/**
* #var DateTime $createddate
*
* #ORM\Column(type="datetime")
*/
private $createddate;
/**
* #ORM\PrePersist
*/
public function createddatePrePersist() {
$this->createddate = new \DateTime('now');
}
My personal opinion is that you're trying to find the nail for your hammer. You don't have to use composition, inheritance, traits, or any OOP pattern for every entity.
I tend to consider that the creationDate is an inherent property of my entity, and any entity that requires a creation date has this property. One may argue that's repeating myself, but in my opinion that makes the whole entity much easier to read.
Oh, and if I may, I wouldn't use an ORM-specific method, which is not part of my domain model, to initialise the creation date. This would be done in my constructor:
/**
* #var DateTime $createddate
*
* #ORM\Column(type="datetime")
*/
private $createddate;
public function __construct()
{
$this->createddate = new \DateTime();
// ... and other business rules that need to be enforced in the constructor
}

Categories