While in general injecting something is not bad and does not automatically result into breaking the SRP (nor it does in your case - you have one class which only fetches data and another constructing an object from it), you have a different problem: wrong understanding of layering and abstraction.
The repository layer is the one to bind data to your domain models, you should not need another layer to do that. Not to mention your solution is overengineered.
Simply construct the quote directly in the repository, unless you have a really good reason for doing otherwise, no need for a factory.
From your comment below:
What do you mean by binding data to domain models? It sounds too abstract and I can't find a way to understand it. Can you give an example?
You have pure data which somehow finds its way into your system. The ways may include:
- SOAP API,
- REST API,
- database,
- contents from a read file,
- ...
This data is just data and nothing else, it contains no rules.
You then have your business (the fun parts of the application, where the rules are) core, you domain. The problem is your domain does not understand the pure data. In order to understand the pure data the data shall be transformed to be represented by business objects, domain models.
Also overall you are saying, effectively merge Factory and Repository together (into Repository) but keep Quote as a separate concept, or should Quote since it contains data be bound to Repository as well?
I am not saying you shall merge the factory and repository together, I am saying you should remove the factory completely and instantiate a Quote object directly in the getLines method. And since we're already talking about it, it might be wise to rename the method to something better, such as: getQuoteWithLineItemsById.
Also, the Quote shall have no direct ties to the repository. Why? Repository acts as a gateway to your system - as I have already mentioned - by taking pure data and transforming it to objects.
The proposed design:
class QuoteRepository { /** * @var \Doctrine\ORM\EntityManager */ private $entityManager; public function __construct(\Doctrine\ORM\EntityManager $entityManager) { $this->entityManager = $entityManager; } /** * @param string $quoteId * @return Quote */ public function getQuoteWithLineItemsById($quoteId) { $query = $this->entityManager ->createQuery('SELECT s FROM... where id = :id') ->setParameter('id', $quoteId); $quote = new Quote(); $quote->setLines($query->getResult()); return $quote; } } class Quote { private $lines = []; public function setLines(array $lines) { $this->lines = $lines; } } $quoteRepository = new QuoteRepository(DoctrineConnector::getEntityManager()); $quote = $quoteRepository->getQuoteWithLineItemsById('1');
Your repository is now responsible for transforming the data retrieved from the database to a domain model, the Query. It knows nothing about business logic, the business logic shall be within the domain model.
Besides transforming raw data to business-understandable entities the repository layer also exists for another reason:
- transforming business-understandable entities back to raw data for persistence purposes.
So in effect, I can end up with a single QuoteRepository class that will contain my data, have business domain functionality and read/write to/from the database?
No. You will end up with repository layer only responsible for reading/writing from/to the database and transforming the data either one way or another.
Then you will have another layer (the domain) which is persistence ignorant, knows absolutely nothing about SQL, is pure PHP (most likely classes), and contains all your business rules - eg. a username must not be empty or longer than 32 characters.
When dealing with business operations your domain models ensure your business rules are preserved. If a domain model exists and is sent to a repository to be saved the repository no longer cares about the state of the domain model, because it simply trusts the domain model is in a valid state. The responsibility of the repository is to save the model, not to check for its state.
$idbeing used for? Don't see anything reading it.