Using Doctrine inside ZF2 Models - php

I'm working for the first time to get Doctrine working with a new ZF2 app. Doctrine is working fine if I call it within the controller (like every last tutorial out there has you do), however it doesn't make any sense performing business logic in the controller.
A few things I found suggests dependency injection passing the Entity Manager in from the controller, some suggest having your class implement ServiceLocatorAwareInterface.
My question is, how is anyone else using it within their models? Surely someone is using it the correct (MVC) way and not putting all of their business logic within their application controllers?

There are of course different solutions for this, but I personally use a Service layer. So for instance you would have a UserService which takes care of handling the business logic of User objects.
To allow the service to do its job you would inject its dependencies. Implementing ServiceLocatorAwareInterface is an option, but if you find yourself using getServiceLocator()->get('...') a lot it becomes a pita to write unit tests and injecting mock objects. A hybrid solution for that would be to have your service implement ServiceLocatorAwareInterface and have a getServiceA() and setServiceA() method where the getServiceA would look like:
if (!$this->serviceA)
{
$this->getServiceLocator()->get('ServiceA');
}
return $this->serviceA;
That way you can still inject a mock version of the dependency in your unit test.

I usually make a Service and instantiate it with a Factory in which I inject either the ServiceLocator or the EntityManager itself directly into the service. The typical folder structure would look something like
\src
\Module
\Controller
\Service
BusinessService.php
\Factory
BusinessServiceFactory.php
In your Module.php or the equivalent in module.config.php for that matter
function getServiceConfig() {
return array(
'factories' => array(
'service.business' => 'Module\src\Module\Factory\BusinessServiceFactory,
),
)
}
Then the actual Service and the Factory itself
BusinessServiceFactory.php
namespace Module\Factory\Service;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class BusinessServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$service = new BusinessService($serviceLocator);
return $service;
}
}
BusinessService.php
namespace Module\Service;
use Zend\ServiceManager\ServiceLocatorInterface;
class BusinessService
{
/**
* #var Service locator
*/
protected $serviceLocator;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
}
You can now define all your business logic in the BusinessService class. It is callable from your Controllers by this->serviceLocator->get('service.business')

Related

How to access not-injected services directly on Symfony 4+?

I'm trying to update Symfony 2.8 to Symfony 4 and I am having serious problems with the Services Injection.
I'm looking the new way to use Services inside Controllers, with auto-wiring:
use App\Service\AuxiliarService;
class DefaultController extends AbstractController
{
public function index(AuxiliarService $service)
{
$var = $service->MyFunction();
....
This way works fine, but I dislike the explicit way to refer MyService as a parameter of the function. This way I don't even need to register the Service in the services.yaml
Is there any way to use Services as in Symfony 2.8:
class DefaultController extends Controller
{
public function index()
{
$var = $this->get('AuxiliarService')->MyFunction(); /*Doesn't need to be explicit indicate before*/
....
With the services.yaml
services:
auxiliar_service:
class: AppBundle\Services\AuxiliarService
arguments:
entityManager: "#doctrine.orm.entity_manager"
container: "#service_container" #I need to call services inside the service
This way I don't need to indicate the Service as a parameter in the function of the Controller. In some cases, inside a Service, I need to call more than 10 services depends on the data, so indicate them as a parameter in the function is annoying.
Another doubt in Symfony 4, is how to call a Service inside another Service without pass it as an argument or parameter. It used to be possible by injecting the service container to be able to call a service inside a service:
$this->container->get('anotherService')
In Symfony 4, I think it is more expensive (in code) use Service because you have to explicitely indicate them when you are going to use them.
tldr; you can achieve that by using Service Subscribers & Locators.
In your controller:
use App\Service\AuxiliarService;
class DefaultController extends AbstractController
{
public function index(AuxiliarService $service)
{
$var = $service->MyFunction();
}
public static function getSubscribedServices()
{
return array_merge(parent::getSubscribedServices(), [
// services you want to access through $this->get()
'auxiliar_service' => AuxiliarService:class,
]);
}
// rest of the implementation
}
If your service needs to implement a similar pattern, you'll need to implement ServiceSubscriberInterface (AbstractController, that you are extending for your controller, already does that for you).
class AuxiliaryService implements ServiceSubscriberInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
protected function has(string $id): bool
{
return $this->container->has($id);
}
protected function get(string $id)
{
return $this->container->get($id);
}
public static function getSubscribedServices()
{
return [
// array_merge is not necessary here, because we are not extending another class.
'logger' => LoggerInterface::class,
'service2' => AnotherService::class,
'service3' => AndMore::class
];
}
}
That being said, you are very probably not doing things right if you want to continue this way
Before Symfony 4+ you could do $this->get('service') because these controllers all had access to the container. Passing the dependency container around for this it is an anti-pattern, and shouldn't be done.
If you do not declare your dependencies, your dependencies are hidden. Users of the class do not know what it uses, and it's easier to break the system by changing the behaviour of one of the hidden dependencies.
Furthermore, with Symfony providing auto-wiring and a compiled container; dependency injection is both easier to implement and faster to execute.
That you are having trouble with implementing this probably reveals deeper issues with your code in general, and you should do some work on segregating the responsibilities of your classes. The fact that one service may depend on that many other services which you can't even know until runtime it's a very strong smell that the concerns are not well separated.
Try to adapt to the changes, it will do your application and yourself good in the long term (even if brings a small amount of pain right now).

zf2 controller factory serviceLocator

I'm trying to inject the service manager into a controller.
Actual Error:
\vendor\zendframework\zend-servicemanager\src\Exception\ServiceLocatorUsageException.php:34
Service "Project\Service\ProjectServiceInterface" has been requested to plugin manager of type "Zend\Mvc\Controller\ControllerManager", but couldn't be retrieved.
A previous exception of type "Zend\ServiceManager\Exception\ServiceNotFoundException" has been raised in the process.
By the way, a service with the name "Project\Service\ProjectServiceInterface" has been found in the parent service locator "Zend\ServiceManager\ServiceManager": did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code?
The process goes:
class BaseController extends AbstractActionController implements ServiceLocatorAwareInterface
{
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
}
Create controller and use constructor method
Extend this BaseController to AdminController
Setup Routes to AdminController => /admin
use Module.php
public function getControllerConfig()
Use closer as factory to create controller object injecting the serviceLocator
'Project\Controller\Project' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
return new \Project\Controller\ProjectController($serviceLocator);
},
try to use $this->getServiceLocator()->get('service_name')
Exception found for missing service.....
Now the problem is this:
/**
*
* #param ServiceLocatorInterface $sl
*/
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$rtn = $sl->has('Project\Service\ProjectServiceInterface');
echo '<br />in Constructor: '.__FILE__;var_dump($rtn);
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
$rtn = $this->serviceLocator->has('Project\Service\ProjectServiceInterface');
echo '<br />in getServiceLocator: '.__FILE__;var_dump($rtn);
return $this->serviceLocator;
}
Within the __constructor() the service IS FOUND. Within the getServiceLocator() method the service with the same name IS NOT FOUND....
in Constructor: Project\Controller\BaseController.php
bool(true)
in getServiceLocator: Project\Controller\BaseController.php
bool(false)
Am I missing something? Is the SharedServiceManager doing something here?
The entire purpose of this exercise was due to this message:
Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. ...
If you really need the ServiceLocator, you have to inject it with a factory
Something like this
Controller:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\ServiceManager\ServiceLocatorInterface;
class BaseController extends AbstractActionController
{
protected $serviceLocator = null;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->setServiceLocator($serviceLocator);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Factory:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Controller\BaseController;
class BaseControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator);
{
$controller = new BaseController($serviceLocator->getServicelocator());
return $controller;
}
}
?>
in module.config.php
<?php
// ...
'controllers' => [
'factories' => [
'Application\Controller\BaseController' => 'Application\Controller\Factory\BaseControllerFactory',
// ...
],
// ...
In Zend Framework 2 there are multiple service locators (docs here), one general (mainly used for your own services), one for controllers, one for view helpers, one for validators, ... The specific ones are also called plugin managers.
The error message you are receiving is just telling you that you are using the wrong service locator, the ones that retrieves controllers and not the general one. It is also suggesting you how to solve your problem:
did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code
What is probably happening (not 100% sure about this) is that in the constructor you are passing in an instance of the general service manager, and everything works fine with it. Then, since the controller implements the ServiceLocatorAwareInterface, the controller service locator is injected into your controller, overriding the one that you defided before.
Moreover, I think that the idea beyound the decision of removing ServiceLocatorAwareInterface in version 3 is that you don't inject the service locator inside your controller, but instead you inject directly the controller dependencies.
You should try to prevent injecting the service manager or service locator in the controller. It would be much better to inject the actual dependencies (in your case 'Project\Service\ProjectServiceInterface') directly into the __construct method of your class. Constructor injection (the dependencies are provided through a class constructor) is considered best practice in ZF2.
This pattern prevents the controller from ever being instantiated without your dependencies (it will throw an error).
If you inject a ServiceLocator or ServiceManager from which you will resolve the actual dependencies in the class, then it is not clear what the class actually needs. You can end up in a class instance with missing dependencies that should never have been created in the first place. You need to do custom checking inside the class to see if the actual dependency is available and throw an error if it is missing. You can prevent writing all this custom code by using the constructor dependency pattern.
Another issue is that it is harder to unit-test your class since you cannot set mocks for your individual dependencies so easily.
Read more on how to inject your dependencies in my answer to a similar question.
UPDATE
About the issue you encountered. Controller classes implement a ServiceLocatorAwareInterface and during construction of your controller classes the ControllerManager injects a ServiceLocator inside the class. This happens here in the injectServiceLocator method at line 208 in ControllerManager.php. Like #marcosh already mentioned in his answer, this might be a different service locator then you injected. In this injectServiceLocator method you also find the deprecation notice you mentioned in your question.
Yours is available in the __construct method because at that time (just after constructing the class) the variable is not yet overwritten. Later when you try to access it in your getServiceLocator method it is overwritten.

Laravel - Defining repository overrides

I am using Laravel 5.1 and have set up a Repository pattern. I have the concrete implementations of my repos injected into my controllers. I realize that your SUPPOSED to inject the interface but that over complicates my API and doesn't solve my issue. I have a client config that simply contains a string such as '' and I am already using that globally to use model overrides if they exist.
So, for example, if I have client 'yahoo' and they have an override in my Overrides/Yahoo/Models/User.php then it will use this User.php. Whether its an extension of the base User model or a whole new implementation is up to me.
I am trying to do the same thing for my repositories. I want to be able to put an override file in Overrides/Yahoo/Repos/UserRepository.php and on injection it will either use the base User repository or the override if it exists.
Any ideas of how I can accomplish this? I realize that you can inject a repository interface and use a binding but I want to do this globally. If you can tell me how I can abstract that functionality to automatically bind the correct implementation of the interface based on client config I would accept that.
You can resolve your concrete implementation by the configuration you use, but it requires that you use interfaces.
Example configuration (config/client.php):
<?php
return [
'users' => [
'repository' => \App\Overrides\Yahoo\Repos\UserRepository::class,
],
];
In your AppServiceProviders register method:
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
UserRepositoryInterface::class,
config('client.users.repository')
);
}
In your Yahoo UserRepository, you can then inject the Yahoo User model directly:
<?php
namespace App\Overrides\Yahoo\Repos;
use App\Repos\UserRepositoryInterface;
use App\Overrides\Yahoo\Models\User;
class UserRepository implements UserRepositoryInterface
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
// TODO: Implement UserRepositoryInterface methods
}
Finally, in your UserController you can inject the UserRepositoryInterface and it will bind the concrete implementation based on your configuration:
<?php
namespace App\Http\Controllers;
use App\Repos\UserRepositoryInterface;
class UserController extends Controller
{
private $users;
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
}
It might seem overkill at first, but once you set up everything it's pretty easy to add new overrides.
You could even create a base concrete implementation of the UserRepository and make every override repository inherit from it. This way you don't have to re-implement all methods required by the interface, but you stay flexible when the user repositories use different database technologies (SQL, MondoDB...)

Will I break the inversion of control principle if I use andersao/l5-repository in my Laravel 5 project?

I am novice Laravel developer and I am trying to understand and apply the SOLID principles like a good programmer. So I recently learnt and applied the repository pattern in laravel.
To do this, I created a directory archive and loaded it with psr-4 like so:
"Archive\\": "archive/"
Then I created a folder called Repositories and another in it called Contracts. Now in the Contracts folder I have interfaces like UserRepositoryInterface and ServicesRepositoryInterface and so on, and outside in the Repositories folder, I have implementation like DbUserRepository and DbServiceRepository and so on.
I am using a service provider called DataServiceProvider where I bind these like so:
$this->app->bind(UserRepositoryInterface::class, DbUserRepository::class);
$this->app->bind(ServiceRepositoryInterface::class, DbServiceRepository::class);
So this way I can inject the Contacts like UserRepositoryInterface and ServiceRepositoryInterface in my controllers and Laravel automatically resolves my dependencies out of the IoC container. So if in the future I need a FileUserRepository, I just need to create that class and change the binding in my service provider and nothing will break in my controllers.
This is what I have learnt from Taylor and Jeffrey. But now I am trying to use a package https://github.com/andersao/l5-repository for my project.
According to this, I will extend my DbUserRepository with the BaseRepository which comes with it like so:
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class UserRepository extends BaseRepository {
/**
* Specify Model class name
*
* #return string
*/
function model()
{
return "App\\Post";
}
}
Now in this I am obviously reusing all the code and power that comes with the BaseRepository like all(), panginate($limit = null, $columns = ['*']), find($id) and so on but now am I breaking the Inversion of Control Principle because now I will have to inject concrete implementations into my controller?
I am still a novice developer and trying to understand all this and may have gone wrong somewhere in the question when explaining things. What is the best way to go around using the package while also maintaining a loose coupling in the controllers?
There is no reason why you still can't implement your interface:
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class DbUserRepository extends BaseRepository implements UserRepositoryInterface {
/**
* Specify Model class name
*
* #return string
*/
function model()
{
return "App\\Post";
}
}
However, you are now faced with a problem; if you swap out your implementation, there is nothing in your UserRepositoryInterface to say that the BaseRepository methods must also be implemented. If you take a look at the BaseRepository class, you should see that it implements two interfaces of it's own: RepositoryInterface and RepositoryCriteriaInterface and 'luckily' php allows for multiple interface inheritence which means you can extend your UserRepositoryInterface as follows:
interface UserRepositoryInterface extends RepositoryInterface, RepositoryCriteriaInterface {
// Declare UserRepositoryInterface methods
}
and you can then bind and use your interface as normal:
$this->app->bind(UserRepositoryInterface::class, DbUserRepository::class);

Alternatives to Zend Registry for ZF2

I have just started using ZF2 and am really enjoying it.
One thing that puzzles me a bit is the absence of a Registry component. I realise that the Service Manager makes the Registry obsolete in most cases. I rely on it heavily and its great.
But from time to time I find myself needing access to a 'global' object, and I don't have access to the Service Manager. For example, in my Domain\User object I need access to a Zend\Log.
I don't want to make the Service Manager available in my Domain objects, since they are beautiful and pristine, and unadulterated by such considerations. I could 'new' a log instance whenever required, but I do it so often I'd rather have a preconfigured instance to hand. I could wrap it in a singleton, but that seems like a backward step. I could create my own mini-registry, but if that was a good idea, I'm sure the Zend guys would have left such a component in place.
So, what other options are there?
EDIT:
So, could I use Zend DI perhaps? I see this question partially covers it, Configuring class alias for using with Zend\Di\Di
You are exactly addressing the problem of dependency injection. I have blogged about this topic before, how to refactor towards dependency injection and what to do if you have soft dependencies like a Logger.
The idea is you inject the pre-configured logger. When your Domain\User object is created, the logger is injected. This makes the Domain\User object only dependent on the logger without having the knowledge how to create the logger. It's even better if you rely on a Logger interface, so you can swap to any logger implementation you want.
Example
As an example, I assume you are using Zend\Log. You have a Logger like Zend\Log\Logger with various writers attached. The logger implements Zend\Log\LoggerInterface.
Your Domain\User class:
namespace Domain;
Zend\Log\LoggerInterface;
class User
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function doSomething()
{
$this->logger->info('Do Something');
//
}
}
For Zend Framework, you should work with factories to inject this logger into your objects. The best way is to define the Logger as a service first, as you could reuse the logger for other objects as well.
Note I use the Service Manager and not Zend\Di here. Zend\Di is obsolete and replaced by the faster and more flexible Zend\ServiceManager. Both can achieve dependency injection.
The factory for you logger:
namespace MyModule\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Log\Logger;
class LoggerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$logger = new Logger;
// do more with $logger here
return $logger;
}
}
Then register this factory to create the service "logger" for you. In your module.config.php:
'service_manager' => array(
'factories' => array(
'logger' => 'MyModule\Factory\LoggerFactory',
),
),
Now logger is available in your service manger. For your domain model, do the same. Create a factory first:
namespace MyModule\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Domain\User;
class UserFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$logger = $serviceLocator->get('logger');
$user = new User($logger);
return $user;
}
}
Then register this one too:
'service_manager' => array(
'factories' => array(
'logger' => 'MyModule\Factory\LoggerFactory',
'Domain\User' => 'MyModule\Factory\UserFactory',
),
),
If you have access to the service locator, you can get the Domain\User and the logger is automatically injected:
$user = $sl->get('Domain\User');

Categories