Entity Manager in Service in Zend Framework 2 - doctrine-orm

I have written a custom service for my module. This service provides public static functions which should validate a given token.
Now i want to implement another public static function which checks if an Doctrine-Entity exists or not. For this case i need the object-manager or the service-locator in my service.
class ApiService
{
const KEY_LENGTH = 10;
const USE_NUMBERS = true;
const USE_CHARS = true;
public static function isValid($apiKey) {
$isValid = false;
# more code tbd
$isValid = self::exists($apiKey);
return $isValid;
}
public static function exists($apiKey) {
# Insert Object-Manager here
$validator = new \DoctrineModule\Validator\ObjectExists(array(
'object_repository' => $objectManager->getRepository('Application\Entity\User'),
'fields' => array('email')
));
}
}
Is it "best-practice" to implement my functions as public static and call them as static methods?
What is the best practice to inject the object-manager into my doesEntityExist() function?

The best approach would be to completely remove the static methods from your class here. ZF2 makes it really easy to fetch services by their name, so you shouldn't really need static methods for such a use case.
First of all, clean up your service:
namespace MyApp\Service;
use Doctrine\Common\Persistence\ObjectRepository;
use DoctrineModule\Validator\ObjectExists;
class ApiService
{
// ...
protected $validator;
public function __construct(ObjectRepository $objectRepository)
{
$this->validator = new \DoctrineModule\Validator\ObjectExists(array(
'object_repository' => $objectRepository,
'fields' => array('email')
));
}
public function exists($apiKey)
{
return $this->validator->isValid($apiKey);
}
// ...
}
Now define a factory for it:
namespace MyApp\ServiceFactory;
use MyApp\Service\ApiService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApiServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
return new ApiService($repository);
}
}
Then map the service name to the factory (usually in your module):
namespace MyApp;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module implements ConfigProviderInterface
{
public function getConfig()
{
return array(
'service_manager' => array(
'factories' => array(
'MyApp\Service\ApiService'
=> 'MyApp\ServiceFactory\ApiServiceFactory',
),
),
);
}
}
NOTE: you may want to simply use a closure instead of defining a separate factory class, but having factory classes gives you a small performance boost when you're not using the service. Also, using a closure in configuration means you cannot cache the merged configuration, so consider using the method suggested here.
Here's an example without the factory class (again, consider using the approach explained above):
namespace MyApp;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ServiceProviderInterface
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyApp\Service\ApiService' => function ($sl) {
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
return new MyApp\Service\ApiService($repository);
},
),
);
}
}
Now you can use the service in your controllers:
class MyController extends AbstractActionController
{
// ...
public function apiAction()
{
$apiService = $this->getServiceLocator()->get('MyApp\Service\ApiService');
if ( ! $apiService->isValid($this->params('api-key')) {
throw new InvalidApiKeyException($this->params('api-key'));
}
// ...
}
// ...
}
You can also retrieve it wherever you have the service manager:
$validator = $serviceLocator->get('MyApp\Service\ApiService');
As an additional suggestion, consider simplifying your service. Since isValid is already a method of your validator, you could simply return the validator itself (hereby using the closure method for simplicity):
namespace MyApp;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use DoctrineModule\Validator\ObjectExists;
class Module implements ServiceProviderInterface
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MyApp\Validator\ApiKeyValidator' => function ($sl) {
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$repository = $entityManager->getRepository('Application\Entity\User');
new ObjectExists(array(
'object_repository' => $objectRepository,
'fields' => array('email')
));
},
),
);
}
}

Personally, I'd make the service a 'service' and put it in the ServiceManager. In addition I'd consider refactoring the code. Right now you have a dependency on the ObjectExists validator, which in turn depends on and entity repository, and that depends on the entity manager. It would be much simpler to compose the validator outside the service and inject it from a factory. That way, if you ever need to use a different validator, you just hand it a different one.
class ApiService
{
protected $validator;
public function isValid($apiKey)
{
// other code
$isValid = $this->exists($apiKey);
}
public function exists($apiKey)
{
return $this->getValidator()->isValid($apiKey);
}
public function setValidator(\Zend\Validator\AbstractValidator $validator)
{
$this->validator = $validator;
return $this;
}
public function getValidator()
{
return $this->validator;
}
}
In Module.php create the service as a factory method, or better still as a factory class, but that's left as an exercise for you :)
public function getServiceConfig()
{
return array(
'factories' => array(
'ApiService' => function($sm) {
$em = $sm->get('Doctrine\ORM\EntityManager');
$repo = $em->getRepository('Application\Entity\User');
$validator = new \DoctrineModule\Validator\ObjectExists($repo,
array('fields' => array('email')));
$service = new ApiService();
$service->setValidator($validator);
return $service;
},
),
);
}
Now if you need a different EntityManager, a different Entity repository, or even a whole different validator you only need to change a couple of lines above rather than having to delve into your services code.

Related

I need xUnit test case of this. Microsoft.Azure.Cosmos Container

I have use Cosmosdb Container its abstract class.
I need xUnit test case with mock using Moq library
public class SmsTemplateRepository : ISmsTemplateRepository
{
private Container _container;
public SmsTemplateRepository(CosmosClient dbClient, string databaseName, string containerName)
{
_container = dbClient.GetContainer(databaseName, containerName);
}
public IEnumerable<SmsTemplate> GetAll()
{
return _container.GetItemLinqQueryable<SmsTemplate>(true);
}
**public async Task InsertAsync(SmsTemplate smsTemplate)
{
await _container.CreateItemAsync(smsTemplate);
}**
}
You have to mock the whole chain from the dependency you pass into the constructor of the repository.
Create a list of templates you want to simulate GetAll to return:
var smsTemplates = new[]
{
new SmsTemplate { Name = "Name1" },
new SmsTemplate { Name = "Name3" }
}.AsQueryable()
.OrderBy(x => x.Name);
Create a mocked CosmosClient and a mocked Container and setup the CosmosClient mock to return the mocked container:
var container = new Mock<Container>();
var client = new Mock<CosmosClient>();
client.Setup(x => x.GetContainer(It.IsAny<string>(), It.IsAny<string>())
.Returns(container.Object);
Setup the mocked container to return the template list and pass in the mocked CosmosClient to the constructor of the repository:
container.Setup(x => x.GetItemLinqQueryable<SmsTemplate>(It.IsAny<bool>())
.Returns(smsTemplates);
var repository = new SmsTemplateRepository(client.Object, ....);
GetAll will now return the smsTemplates

PHPUnit testing file_get_contents

I have a class which has a method that makes use of PHP's global file_get_contents function. I need to test the method on the class, without actually calling the global function.
I know that I could use namespaces to override what gets returned from file_get_contents, however my tests are in a separate namespace already so I cannot simply match namespaces with the class.
Here's some code:
The class
<?php
namespace MyVendor\MyProject;
class MyClass
{
private $someProperty;
public function __construct($override = '')
{
$this->someProperty = $override;
}
public function myMethod()
{
$request = 'http://example.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
The test
<?php
namespace MyProjectTests;
public function test_it_does_something_with_the_response()
{
$sut = new MyClass();
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
I need to mock the someMethodUsingGlobals() method on the class, but not entirely sure how to go about this.
Solution: do a class-wrapper around the native function
The simplest and cleanest way to do it is to create a wrapper class around the native function.
If you follow DDD and/or hexagonal architecture, you'd probably place it in the "adapters" space, and if you do not follow DDD nor hex-arch, place it besides any group of classes that "touch the exterior world".
This wrapper is a one-liner class:
<?php
declare( strict_types = 1 );
namespace MyVendor\MyProject\Adapters;
class FileGetContentsWrapper
{
public function fileGetContents( string $filename )
{
return file_get_contents( $filename );
}
}
This class cannot be tested, as it just uses the native function.
But with it, you just "shift" the "untesting" to this one-liner class and you now make all the other places that used to use the file_get_contents() testable gaining coverage around teh logic that surrounded the code besides the file reading.
Your original class, modified
You proceed like this:
You inject services via constructor.
You treat the wrapper as a service.
If you are using frameworks like symfony in their recent versions you can use auto-wiring for the injection to simplify the construction of the class.
For example, your class could result in this:
<?php
namespace MyVendor\MyProject;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
class MyClass
{
private $fileGetContentsWrapper;
public function __construct( FileGetContentsWrapper $fileGetContentsWrapper )
{
$this->fileGetContentsWrapper = $fileGetContentsWrapper;
}
/* ... */
public function someMethodUsingTheWrapper( $url )
{
$contents = $this->fileGetContents( $url );
return json_decode( $contents, true )[ 'results' ][ 0 ];
}
}
How to test it
You do the following:
In a test class, you setup a mock for the wrapper.
Within the test method you configure the mock to return whatever you need.
You invoke your class normally.
For example:
<?php
declare( strict_types = 1 );
namespace MyProjectTests;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
use MyVendor\MyProject\MyClass;
use PHPUnit\Framework\TestCase;
class MyClassTest extends TestCase
{
private $fileGetContentsWrapper;
//---------------------------------------------------------------------//
// Setup //
//---------------------------------------------------------------------//
protected function setUp()
{
$this->fileGetContentsWrapper = $this->createMock( FileGetContentsWrapper::class )
parent::setUp();
}
//---------------------------------------------------------------------//
// Tests //
//---------------------------------------------------------------------//
public function testSomeMethodUsingTheWrapper()
{
$sut = $this->getSut();
$someSimulatedJson = '{"results":["abc","xyz"]}';
$this->fileGetContentsWrapper->method( 'fileGetContents' )->willReturn( $someSimulatedJson );
$this->assertEquals( 'xyz', $sut->someMethodUsingGlobals( 'dummy-url' ) );
}
//---------------------------------------------------------------------//
// Private //
//---------------------------------------------------------------------//
private function getSut() : MyClass
{
return new MyClass( $this->fileGetContentsWrapper );
}
}
That's all! Hope to help!
You can archive it using a partially Mock Objects: you can mock only a specific method of your class and execute (and tests) the other method.
Further reference here
As example, suppose your modified class:
<?php
namespace Acme\DemoBundle\Service;
class MyClass {
public function myMethod()
{
$request = 'http://domain.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
return $response;
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
You can test with the following test class:
<?php
namespace Acme\DemoBundle\Tests;
class MyProjectTest extends \PHPUnit_Framework_TestCase
{
public function test_it_does_something_with_the_response()
{
$sut = $this->getMock('Acme\DemoBundle\Service\MyClass', array('someMethodUsingGlobals') );
// Set up the expectation for the someMethodUsingGlobals() method
// to be called only once and with the string 'http://domain.com'
// as its parameter.
$sut->expects($this->once())
->method('someMethodUsingGlobals')
->with($this->equalTo('http://domain.com'))
->willReturn('Some expectation');
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
}
So the method someMethodUsingGlobals is not executed and return the values defined in the mock deifinition. The method myMethod is executed and processed with the mocked function.
Hope this help

Controller Cleaning. Doctrine in Model

I want to make my controller thin and to separate business-logic from other operations. For example I have an action:
public function indexAction()
{
$languages = $this ->getEntityManager()
->getRepository('\ApanelLanguage\Entity\LanguageCommon')
->getLanguagesList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
but I want to get action like this:
public function indexAction()
{
$model = $new LanguageModel();
$model->getLanguagesList();
return $viewModel;
}
Is it possible to do? What must I have in Language/Model/LanguageModel ?
Thank you
Removing the business logic from your controller is a great idea for code reuse and maintainability; however I would recommend against moving the logic to your models. A better solution would be to add a service layer to your application.
What is a service layer? Martin Fowler describes it as the following:
[A service layer] defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
This essentially means that we add a class in-between your controller and your model.
The great advantage of this approach is that should you need to update the business logic of your application there is no need to update the controller. The controller also becomes unaware of any specific code and therefore can be reusable in other unrelated projects.
This 'service' could have a simple API, for example:
interface ServiceInterface
{
public function setObjectManager($objectManager);
public function setRepository($respository);
public function find($id);
public function fetchRow($criteria);
public function fetchAll($criteria);
public function insert($object);
public function update($object);
public function delete($object);
}
Then you can implement this interface for your new 'LanguageService'.
class LanguageService implements ServiceInterface
{
// ... all methods from interface
public function getLanguageList()
{
return $this->repository->getLanguagesList();
}
}
Lastly update your controller to use the new service
class FooController extends AbstractActionController
{
protected $languageService;
public function __construct(ServiceInterface $languageService)
{
$this->languageService = $languageService;
}
public function indexAction()
{
$languages = $this->languageService->getLanguageList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
public function insertAction()
{
$request = $this->getRequest();
$service = $this->languageService;
$form = $service->getInsertForm();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
// if our form used the DoctrineObjectHydrator
// we will get a entity back populated with the
// form data
$language = $service->insert($form->getData());
if ($language instanceof Entity\Language) {
// success
} else {
// failure
}
}
}
//
}
}

ZF2 - Service manager error: invalid factory registered

I follwed the following tutorial to understand how Doctrine works: http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html
I now have a better understanding of Doctrine. However I am struggling to understand the entity manager and how to use it.
In the tutorial to get an instance of the entity manager, all you do is this:
$entityManager = \Doctrine\ORM\EntityManager::create($conn, $config);
That is loaded in the bootstrap and is available through out the code base.
So my question is, how is this instantiated in ZF2?
EDIT: I have had some help on this via ZF Talk and the following was recommended to me. I am still however struggling to get it to work:
My goal is to pull an array of users from my database and output them to my view using Doctrines pagination class.
In my Controller for the given action I have:
public function usersAction() {
$userFunctions = $this->getServiceLocator()->get('Administration\Model\UserFunctionFactory');
$userArray = $userFunctions->getUsers();
$viewModel = new ViewModel(array('users' => $userArray));
return $viewModel;
}
The users function Model is as such:
namespace Administration\Model;
use Doctrine\ORM\Tools\Pagination\Paginator;
class UserFunctions
{
protected $em;
function __constructor(EntityManagerInterface $em) {
$this->em = $em;
}
public function getUsers()
{
$em = $this->em;
$dql = "SELECT * FROM Users";
$query = $em->createQuery($dql)
->setFirstResult(0)
->setMaxResults(100);
$paginator = new Paginator($query, $fetchJoinCollection = true);
$c = count($paginator);
foreach ($paginator as $post) {
$myArray[] = $post->getHeadline() . "\n";
}
return $myArray;
}
}
The factory for the SM:
<?php
namespace Administration\UserFunctionFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Administration\Model\UserFunctions;
class UserFunctionsFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
return new UserFunctions($em);
}
}
The module.config:
'service_manager' => array(
'factories' => array(
'Administration\Model\UserFunctionFactory' => 'Administration\Model\UserFunctionFactory')
),
I am getting the following error:
While attempting to create administrationmodeluserfunctionfactory(alias: Administration\Model\UserFunctionFactory) an invalid factory was registered for this instance type.
Your immediate issue (based on your code snippets, and the resulting error about the factory being invalid) is actually trivial.
Your module.config.php says the factory class is:
Administration\Model\UserFunctionFactory,
but the fully-qualified class name of the class defined in your factory's classfile is:
Administration\UserFunctionFactory\UserFunctionFactory
The namespace mismatch means the SM can't find your factory. So, your first fix is to make sure your factory is indeed defined in a file like Administration/src/Administration/Model/UserFunctionsFactory.php (assuming your module is using PSR-0), and change the first line to read namespace Administration/Model
this is what basically you should never do in a ZF2 controller
$userFunctions = new UserFunctions();
Instead you create a service (your UserFunctions) and get it in your controller using the service locator
namespace Administration\Service;
use Zend\ServiceManager\FactoryInterface,
Zend\ServiceManager\ServiceLocatorInterface;
use Administration\Model\UserFunctions;
class UserFunctionsFactory
implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new UserFunctions($this->getServiceLocator()->get('Doctrine\ORM\EntityManager'));
}
}
So you user functions class constructor will be
use Doctrine\ORM\EntityManagerInterface;
// class declaration and props here
function __constructor(EntityManagerInterface $entityManager) {
$this->em = $entityManager;
}
Then you register the service
// file services.config.php
namespace Administration;
return array(
'factories' => array(
'admin.service.contact' => new Service\UserFunctionsFactory()
),
);
Please note that you can do injection using initializers more than use class constructor. I used this method in the above example for simplicity.
This is an example of an initializer that inject DoctrineEntityManager
namespace My\Service\Initializer;
use Zend\ServiceManager\InitializerInterface,
Zend\ServiceManager\ServiceLocatorInterface,
Zend\Debug\Debug;
use My\Service\EntityManagerAwareInterface;
class EntityManagerAwareInitializer
implements InitializerInterface
{
public function initialize($instance, ServiceLocatorInterface $serviceLocator)
{
if($instance instanceof EntityManagerAwareInterface) {
$instance->setEntityManager($serviceLocator->get('doctrine.entitymanager'));
}
}
}
and the interface
namespace My\Service;
use Doctrine\ORM\EntityManagerInterface;
interface EntityManagerAwareInterface
{
/**
* Set Doctrine 2 Entity Manager
*
* #param EntityManagerInterface $entityManager
*/
function setEntityManager(EntityManagerInterface $entityManager);
/**
* Get Doctrine 2 Entity Manager
*/
function getEntityManager();
}
and the registration of the initializer
'initializers' => array(
'My\Service\Initializer\EntityManagerAwareInitializer' => new EntityManagerAwareInitializer()
),
Please note that I have separated configuration files because my module implements some interfaces that gives the possibility to do so, for example
namespace My;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface,
Zend\ModuleManager\Feature\ControllerProviderInterface,
Zend\ModuleManager\Feature\ServiceProviderInterface,
Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module
implements
AutoloaderProviderInterface,
ControllerProviderInterface,
ServiceProviderInterface,
ConfigProviderInterface
{
public function getConfig()
{
return include __DIR__ . '/../../config/module.config.php';
}
public function getControllerConfig()
{
return include __DIR__ . '/../../config/controllers.config.php';
}
public function getServiceConfig()
{
return include __DIR__ . '/../../config/services.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/../../src/' . __NAMESPACE__,
),
),
);
}
}
My Module.php is inside my src, so pay attention to paths.
Hope it helped!

Testing methods that behaves like getter/setters but are more like factories

Let's say I have a small class that handles connections to MySQL databases:
class myDatabaseHandler{
private $_databases;
public function addDatabase($name, $dsn, $username, $password){
$this->_databases[$name] = array('dsn' => $dsn, 'username' => $username, 'password' => $password);
return true;
}
public function getDatabase($name){
$connectionInfo = $this->_databases[$name];
$database = new Database($connectionInfo['dsn'], $connectionInfo['username'], $connectionInfo['password']);
$database->doSomeSetup();
$database->attachMoreThings();
return $database;
}
}
I want to unit test these 2 methods:
class myDatabaseHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testAddDatabase(){
}
public function testGetDatabase(){
}
}
How can this I test those 2 methods? If I addDatabase(), at most it would return a Boolean telling me the operation succeeded. Since it writes to a private property, I cannot confirm that the correct data is indeed written to it.
I feel that using getDatabase() to get a Database object back and testing against it is not exactly ideal, because I would need to expose dsn, username and password just for the sake of testing. In addition, it is possible that the Database object might modify those values to a format it uses, so I need to store the original values just for testing.
What is the best way to approach this problem?
Testing certainly gets tricky when you try to construct and use an object in the same place. In this case, you are both constructing a Database and calling methods on it in your getDatabase($name) method. That makes it pretty much impossible to mock, for instance, and to get decent coverage your tests would need to test the functionality provided by the Database class to make sure the system was behaving as expected.
A better way might be using a proper factory as a dependency.
interface iDatabaseFactory
{
public function buildDatabase($dsn, $username, $password);
}
Then, you could mock both the database factory and the database instance itself to verify that it is both constructed correctly and initialized correctly:
class MockDatabaseFactory implements iDatabaseFactory
{
public $databaseParams = array();
public $databaseToReturn = NULL;
public function buildDatabase($dsn, $username, $password)
{
$this->databaseParams['dsn'] = $dsn;
$this->databaseParams['username'] = $username;
$this->databaseParams['password'] = $password;
return $this->databaseToReturn;
}
}
class myDatabaseHandlerTest extends PHPUnit_Framework_TestCase
{
public function testAddAndGetDatabaseUsesCorrectDbParameters(){
$mockDatabaseFactory = new MockDatabaseFactory();
$dbHandler = new myDatabaseHandler($mockDatabaseFactory);
// implement MockDatabase according to your interface
$mockDatabase = new MockDatabase();
$mockDatabaseFactory->databaseToReturn = $mockDatabase;
$dbHandler.addDatabase("some name", "some dsn",
"some username", "pa$$w0rd");
$builtDatabase = $dbHandler.getDatabase("some name");
$this->assertEquals($mockDatabase, $builtDatabase);
$dbParams = $mockDatabaseFactory->databaseParams;
$this->assertEquals("some dsn", $dbParams['dsn']);
$this->assertEquals("some username", $dbParams['username']);
$this->assertEquals("pa$$w0rd", $dbParams['password']);
}
public function testAddAndGetDatabaseInitializesDb(){
$mockDatabaseFactory = new MockDatabaseFactory();
$dbHandler = new myDatabaseHandler($mockDatabaseFactory);
$mockDatabase = new MockDatabase();
$mockDatabaseFactory.setDatabaseToBuild($mockDatabase);
$dbHandler.addDatabase("name", "dsn", "user", "pass");
$builtDatabase = $dbHandler.getDatabase("some name");
$this->assertTrue($mockDatabase->doSomeSetupWasCalled);
$this->assertTrue($mockDatabase->attachMoreThingsWasCalled);
}
}