I have a mapper that uses a \Zend\Db\TableGateway\TableGateway via DI. I have mocked it for the unit test.Here is the test:
class EMSMapperTest extends PHPUnit_Framework_TestCase
{
public function testFetchAllReturnsAllScheduledBlocks()
{
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock(
'Zend\Db\TableGateway\TableGateway',
array('select','getTable'),
array(),
'',
false
);
$mockTableGateway->expects($this->once())
->method('selectWith')
->with()
->will($this->returnValue($resultSet));
$mockTableGateway->expects($this->once())
->method('getTable')
->with()
->will($this->returnValue('table'));
$emsMapper = new EMSMapper($mockTableGateway);
$this->assertSame($resultSet, $emsMapper->fetchAll());
}
}
and the mapper being tested:
class EMSMapper extends BaseMapper
{
public function fetchAll( $building = null,
$room = null, DateRange $range = null )
{
$select = new Select;
$table = $this->tableGateway->getTable();
$select->from($table);
if(!empty($building))
{
$select->where(array('buildingCode'=>$building));
}
if(!empty($room))
{
$select->where(array("room"=>$room));
}
if(is_array($range))
{
if(!empty($range['start']))
{
$select->where("start >= '{$range['start']}'");
}
if(!empty($range['stop']))
{
$select->where("stop <= '{$range['stop']}'");
}
}
$resultSet = $this->tableGateway->selectWith($select);
$results = array();
foreach($resultSet as $r)
{
$results[] = $r;
}
return $results;
}
}
After returning a string from the TableGateway's getTable() method the unit test says:
There was 1 error:
1) EMSTest\Model\EMSMapperTest::testFetchAllReturnsAllScheduledBlocks
Zend\Db\TableGateway\Exception\RuntimeException:
This table does not have an Adapter setup
If would seem that the Select requires the table string supplied to the from() method have an adapter associated with it. How do I supply a mock of the required adapter?
Thanks for the help!
Your code is using the actual code for selectWith. This calls an initialize method that throws your error.
Change you mock code to:
$mockTableGateway = $this->getMock(
'Zend\Db\TableGateway\TableGateway',
array('selectWith','getTable'),
array(),
'',
false
);
This should properly configure your mock.
http://phpunit.de/manual/current/en/test-doubles.html
From the manual:
When the second (optional) parameter is provided, only the methods whose names are in the array are replaced with a configurable test double. The behavior of the other methods is not changed. Providing NULL as the parameter means that no methods will be replaced.
So you were setting the expects on the correct method, but were replacing the wrong one with your mock and so the real code was being executed.
Related
I am writing a new ZF2 app. I have noticed that ServiceLocator usage pattern of calling services "from anywhere" has been deprecated from ZF3. I want to write code in mind for ZF3.
I was able to set up my Controller to call all dependencies at constructor time. But that means loading i.e. Doctrine object upfront before I need it.
Question
How do I set it up so that it is only loaded when I need it immediately? (lazy-loaded). I understand that ZF3 moves loading to Controller construction, which makes it not apparent as to how to load something Just-In-Time.
Old Code
class CommissionRepository
{
protected $em;
function getRepository()
{
//Initialize Doctrine ONLY when getRepository is called
//it is not always called, and Doctrine is not always set up
if (! $this->em)
$this->em = $this->serviceLocator->get('doctrine');
return $this->em;
}
}
Current Code after Refactor of ServiceLocator pattern
class CommissionRepository
{
protected $em;
function getRepository()
{
return $this->em;
}
function setRepository($em)
{
$this->em = $em;
}
function useRepository($id)
{
return $this->em->find($id);
}
}
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $controllerManager->getServiceLocator();
// set up repository
$repository = new CommissionRepository();
$repository->setRepository($parentLocator->get('doctrine'));
// set up controller
$controller = new CommissionController($repository);
$controller->setRepository();
return $controller;
}
}
class CommissionController extends AbstractActionController
{
protected $repository;
public function setRepository(CommissionRepository $repository)
{
$this->repository = $repository;
}
public function indexAction()
{
//$this->repository already contains Doctrine but it should not
//I want it to be initialized upon use. How?
//Recall that it has been set up during Repository construction time
//and I cannot call it from "anywhere" any more in ZF3
//is there a lazy loading solution to this?
$this->repository->useRepository();
}
If you don't have any valid/strong reason to instantiate a custom entity repository, you should prefer extending of Doctrine\ORM\EntityRepository in your repositories like CommissionRepository. For example;
use Doctrine\ORM\EntityRepository;
class CommissionRepository extends EntityRepository
{
// No need to think about $em here. It will be automatically
// injected by doctrine when you call getRepository().
//
function fetchCommissionById($id)
{
// You can easily get the object manager directly (_em) or
// using getEntityManager() accessor method in a repository
return $this->_em->find($id);
}
}
By this way, entity manager will be automatically injected to the repository on construction when you call the $em->getRepository('App\Entity\Commission') method.
I assume that you already have a Commission entity in your app's Entity namespace:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repo\CommissionRepository")
* #ORM\Table
*/
class Commission
{
}
Then you can simplify the injecting process of the repository in your factory something like:
// ZF2 Way
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
$em = $services->getServiceLocator()->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
UPDATE - With the release of Service Manager V3, FactoryInterface has been moved to Zend\ServiceManager\Factory namespace (1), factories are literally invokables (2) and works with any container-interop compatible DIC (3) Updated factory would be like below:
// ZF3 Way
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use Doctrine\ORM\EntityManager;
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $dic, $name, array $options = null) {
$em = $dic->get(EntityManager::class);
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
For the question; as of marcosh's said, Lazy Services are way to go to create services when need it immediately. ZF3 will use the zend-servicemanager 3.0 component when released. (Currently zend-expressive uses it) As of servicemanager v3 you can create some proxied services by defining lazy_services and delegators in your service configuration:
'factories' => [],
'invokables' => [],
'delegators' => [
FooService::class => [
FooServiceDelegatorFactory::class,
],
],
'lazy_services' => [
// map of service names and their relative class names - this
// is required since the service manager cannot know the
// class name of defined services up front
'class_map' => [
// 'foo' => 'MyApplication\Foo',
],
// directory where proxy classes will be written - default to system_get_tmp_dir()
'proxies_target_dir' => null,
// namespace of the generated proxies, default to "ProxyManagerGeneratedProxy"
'proxies_namespace' => null,
// whether the generated proxy classes should be written to disk or generated on-the-fly
'write_proxy_files' => false,
];
Also, starting with service manager v3 factories are compatible with the ContainerInterface. For the forward-compatibility, you may want to keep both __invoke() and createService() methods in your factories for a smooth migration.
In the end, your ZF3 compatible factory may look like:
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
$em = $container->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName, []);
}
}
Hope it helps.
I am trying to call ZFCUser from a class within my model but i keep getting this message:
Call to undefined method Members\Model\MemberTable::zfcUserAuthentication()
this is how i tried to call it:
public function getUserEntity()
{
if($this->zfcUserAuthentication()->getAuthService()->hasIdentity())
{
if (!$this->user_entity)
{
$this->setUserEntity($this->zfcUserAuthentication()->getAuthService()->getIdentity());
}
return $this->user_entity;
}
}
i suspect that i need to implement/extend a class inorder for ZFcuser to be recoginised.
i would really appriciate some quick advice on this.
thank you
by the way
the ZFCUser works when i call it from my controller so, the use Zend\Mvc\Controller\AbstractActionController obviouly enables this to be recoginised.
but what is the equivalent of the AbstractActionController for other classes in your framework.
zfcUserAuthentication() is a controller plugin, so that's why it can be called from controllers. You can't (or shouldn't) try and access this from non-controllers. If you need the user entity within another class, you should pass it in as a dependency for that class.
Edit: Update your member table factory so it sets the user entity:
'Members\Model\MemberTable' => function($sm) {
$tableGateway = $sm->get('MemberTableGateway');
$table = new MemberTable($tableGateway);
$authService = $serviceLocator->get('zfcuser_auth_service');
$userEntity = $authService->getIdentity();
$table->setUserEntity($userEntity);
return $table;
}
add a property to your MemberTable class for it:
protected $userEntity;
and getters/setter for it:
public function setUserEntity($userEntity)
{
$this->userEntity = $userEntity;
}
public function getUserEntity()
{
return $this->userEntity;
}
then just call $this->getUserEntity() when you need it.
There are various ways to do that -
One of the way is - do the following in "MemberTable.php" file.
Maybe most of the below lines of code is already available in our project.
a. Add the below lines after 'namespace' statement -
use Zend\ServiceManager\ServiceLocatorAwareInterface; //Added Line
use Zend\ServiceManager\ServiceLocatorInterface; //Added Line
b. Change the class statement as -
class MemberTable implements ServiceLocatorAwareInterface { //Modified Line
....
.....
}
c. Add the below line at the top of the class statement -
class MemberTable implements ServiceLocatorAwareInterface {
protected $serviceLocator; //Added line
....
.....
}
d. Add the following functions -
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
e. In Members/Module.php, make the changes,
'Members\Model\MemberTable' => function($sm) {
$tableGateway = $sm->get('MemberTableGateway');
$table = new MemberTable($tableGateway);
$table->setServiceLocator($sm); //This is the important line.
return $table;
}
Now you have the 'ServiceLocator' available in the MemberTable class.
For accessing the zfcUserAuthentication() or any ControllerPlugin, you can do the following -
public function getUserEntity()
{
$zfcUserAuth = $this->getServiceLocator()->get('controllerPluginManager')->get('zfcUserAuthentication');
if($zfcUserAuth->getAuthService()->hasIdentity())
{
if (!$this->user_entity)
{
$this->setUserEntity($zfcUserAuth->getAuthService()->getIdentity());
}
return $this->user_entity;
}
}
I've done interception using Castle.DynamicProxy and StructureMap 2.6 API but now can't do it using StructureMap 3.0. Could anyone help me find updated documentation or even demo? Everything that I've found seems to be about old versions. e.g. StructureMap.Interceptors.TypeInterceptor interface etc.
HAHAA! I f***in did it! Here's how:
public class ServiceSingletonConvention : DefaultConventionScanner
{
public override void Process(Type type, Registry registry)
{
base.Process(type, registry);
if (type.IsInterface || !type.Name.ToLower().EndsWith("service")) return;
var pluginType = FindPluginType(type);
var delegateType = typeof(Func<,>).MakeGenericType(pluginType, pluginType);
// Create FuncInterceptor class with generic argument +
var d1 = typeof(FuncInterceptor<>);
Type[] typeArgs = { pluginType };
var interceptorType = d1.MakeGenericType(typeArgs);
// -
// Create lambda expression for passing it to the FuncInterceptor constructor +
var arg = Expression.Parameter(pluginType, "x");
var method = GetType().GetMethod("GetProxy").MakeGenericMethod(pluginType);
// Crate method calling expression
var methodCall = Expression.Call(method, arg);
// Create the lambda expression
var lambda = Expression.Lambda(delegateType, methodCall, arg);
// -
// Create instance of the FuncInterceptor
var interceptor = Activator.CreateInstance(interceptorType, lambda, "");
registry.For(pluginType).Singleton().Use(type).InterceptWith(interceptor as IInterceptor);
}
public static T GetProxy<T>(object service)
{
var proxyGeneration = new ProxyGenerator();
var result = proxyGeneration.CreateInterfaceProxyWithTarget(
typeof(T),
service,
(Castle.DynamicProxy.IInterceptor)(new MyInterceptor())
);
return (T)result;
}
}
The problem here was that SM 3.* allows interception for known types, i.e. doing something like this:
expression.For<IService>().Use<Service>().InterceptWith(new FuncInterceptor<IService>(service => GetProxyFrom(service)));
But what if you'd like to include the interception logic inside your custom scanning convention where you want to intercept all instances of type with specific signature (types having name ending on 'service', in my case)?
That's what I've accomplished using Expression API and reflection.
Also, I'm using here Castle.DinamicProxy for creating proxy objects for my services.
Hope someone else will find this helpful :)
I find the best place to go for any new versions is directly to the source.
If it's written well, then it will include test cases. Thankfully structuremap does include test cases.
You can explore the tests here
In the meantime I've written an example of an Activator Interceptor, and how to configure it.
static void Main()
{
ObjectFactory.Configure(x =>
{
x.For<Form>().Use<Form1>()
.InterceptWith(new ActivatorInterceptor<Form1>(y => Form1Interceptor(y), "Test"));
});
Application.Run(ObjectFactory.GetInstance<Form>());
}
public static void Form1Interceptor(Form f)
{
//Sets the title of the form window to "Testing"
f.Text = "Testing";
}
EDIT:
How to use a "global" filter using PoliciesExpression
[STAThread]
static void Main()
{
ObjectFactory.Configure(x =>
{
x.Policies.Interceptors(new InterceptorPolicy<Form>(new FuncInterceptor<Form>(y => Intercept(y))));
});
Application.Run(ObjectFactory.GetInstance<Form>());
}
private static Form Intercept(Form form)
{
//Do the interception here
form.Text = "Testing";
return form;
}
I have a method which returns Iqueryable result, but the result is based on an if else condition, where if condition satisfies then I will use "AssetDetails" class object ,otherwise "UserandClientdetails" object.
Here is the code:
private IQueryable<?> GetAssetDetails(ShareViewModel item)
{
...
if (type == "Video")
{
if (type == "Video")
{
return from meta in my.Assets().OfType<Model.Video>()
join content in my.Contents() on meta.ContentId equals content.ID
join channel in my.Channels() on content.ChannelId equals channel.ID
where meta.ID == item.ID
select new AssetDetails
{
ContentTitle = content.Title,
ChannelName = channel.ChannelName,
...
};
}
else
{ return from meta in my.Assets().OfType<Model.Client>()
join country in db.Countries on meta.ResellerCountry equals country.ID
where meta.ID == item.ID
select new UserAndClientDetails
{
Name = meta.ResellerName,
UserName = meta.ResellerEmail,
..
};}
So how to decide type of Iqueyable here at runtime??
So, I was able to verify that this works, so I'll go ahead and post it as an answer.
You can return IQueryable instead of the generic IQueryable<>. That will accept any IQueryable<T>. However, IQueryable, since it has no direct inner type, is very limited. So, you'll still likely need to cast to IQueryable<> at some other point in your code to get anything done:
// Piece of code where you know you are working with `IQueryable<AssetDetails>`
IQueryable<AssetDetails> assetDetails = GetAssetDetails(someItem);
That's a little dangerous, though, as you're assuming that your code is working perfectly and the right type of thing is being returned. Better would be:
try
{
var assetDetails = (IQueryable<AssetDetails>)GetAssetDetails(someItem);
// do something with `assetDetails`
}
catch (InvalidCastException)
{
// recover gracefully
}
What about using a base class ?
public abstract class BaseDetails
{
// ...
}
public class AssetDetails : BaseDetails
{
// ...
}
public class UserAndClientDetails: BaseDetails
{
// ...
}
Then you method would be like :
private IQueryable<BaseDetails> GetAssetDetails(ShareViewModel item)
{
// return either IQueryable<AssetDetails> or IQueryable<UserAndClientDetails>
}
(I use typo3 4.5 with extbase-extension.)
I was map the pages_language_overlay to my extbase-model
Tx_Extension_Domain_Model_ModelName {
mapping {
tableName = pages_language_overlay
}
}
I created a model Tx_Extension_Domain_Model_ModelName with some setters and getters. after adding the repository Tx_Extension_Domain_Repository_ModelNameRepository with
public function initializeObject() {
$this->defaultQuerySettings = $this->objectManager->create('Tx_Extbase_Persistence_Typo3QuerySettings');
$this->defaultQuerySettings->setRespectStoragePage(FALSE);
}
and inject the repository like this
public function injectModelNameRepository(Tx_Extension_Domain_Repository_ModelNameRepository $modelNameRepository) {
$this->modelNameRepository = $modelNameRepository;
}
i can not select entries with findByPid. I was testing it with findByUid and echo the pid and it works, but i get no results with findByPid.
Someone has an idea?
I only have to add
public function initializeObject() {
$this->defaultQuerySettings = $this->objectManager->create('Tx_Extbase_Persistence_Typo3QuerySettings');
$this->defaultQuerySettings->setRespectStoragePage(FALSE);
$this->defaultQuerySettings->setRespectSysLanguage(FALSE);
}
after this it works well. Otherwise the query has a check like
AND pages_language_overlay.sys_language_uid IN (0,-1)
in the where clause.