Execute a function for all requests in ZF2 AbstractRestfulController - zend-framework2

Using ZendFramework AbstractRestfulController how can I have a function to be executed for all the request and return the result immediately (if necessary) without explicitly calling it each time?

You can use following function in your conrtoller where AbstractRestfulController is extended.
public function setEventManager(EventManagerInterface $events)
{
$controller = $this;
$config = $this->getServiceLocator()->get('Config');
$events->attach('dispatch', function ($e) use ($controller) {
$route = $e->getRouteMatch();
$viewModel = $e->getViewModel();
$variables = $viewModel->getVariables();
$currentController = $route->getParam('controller');
}, 100); // execute before executing action logic
}
This will execute every request in your application.

Try the onDispatch method.
public function onDispatch(MvcEvent $e)

Related

How to integrate dependency injection with custom decorators

I'm trying to create a decorator that requires dependency injection.
For example:
#Injectable()
class UserService{
#TimeoutAndCache(1000)
async getUser(id:string):Promise<User>{
// Make a call to db to get all Users
}
}
The #TimeoutAndCache returns a new promise which does the following:
if call takes longer than 1000ms, returns a rejection and when the call completes, it stores to redis (so that it can be fetched next time).
If call takes less than 1000ms, simply returns the result
export const TimeoutAndCache = function timeoutCache(ts: number, namespace) {
return function log(
target: object,
propertyKey: string,
descriptor: TypedPropertyDescriptor<any>,
) {
const originalMethod = descriptor.value; // save a reference to the original method
descriptor.value = function(...args: any[]) {
// pre
let timedOut = false;
// run and store result
const result: Promise<object> = originalMethod.apply(this, args);
const task = new Promise((resolve, reject) => {
const timer = setTimeout(() => {
if (!timedOut) {
timedOut = true;
console.log('timed out before finishing');
reject('timedout');
}
}, ts);
result.then(res => {
if (timedOut) {
// store in cache
console.log('store in cache');
} else {
clearTimeout(timer);
// return the result
resolve(res);
}
});
});
return task;
};
return descriptor;
};
};
I need to inject a RedisService to save the evaluated result.
One way I could inject Redis Service in to the UserService, but seems kind ugly.
You should consider using an Interceptor instead of a custom decorator as they run earlier in the Nest pipeline and support dependency injection by default.
However, because you want to both pass values (for cache timeout) as well as resolve dependencies you'll have to use the mixin pattern.
import {
ExecutionContext,
Injectable,
mixin,
NestInterceptor,
} from '#nestjs/common';
import { Observable } from 'rxjs';
import { TestService } from './test/test.service';
#Injectable()
export abstract class CacheInterceptor implements NestInterceptor {
protected abstract readonly cacheDuration: number;
constructor(private readonly testService: TestService) {}
intercept(
context: ExecutionContext,
call$: Observable<any>,
): Observable<any> {
// Whatever your logic needs to be
return call$;
}
}
export const makeCacheInterceptor = (cacheDuration: number) =>
mixin(
// tslint:disable-next-line:max-classes-per-file
class extends CacheInterceptor {
protected readonly cacheDuration = cacheDuration;
},
);
You would then be able to apply the Interceptor to your handler in a similar fashion:
#Injectable()
class UserService{
#UseInterceptors(makeCacheInterceptor(1000))
async getUser(id:string):Promise<User>{
// Make a call to db to get all Users
}
}

ZF3 Shared Eventmanager ignores priority

I have the following classes and configuration to listen for the creation of a profile. Everything works fine, but now I need to listen a second time for it and this should be executed after the first time. I thought I could use the priority here, but it seems that this priority has no effect.
Any ideas?
Config
return [
'listeners' => [
CreateListener::class
]
]
ProfileService
class ProfileService implements EventManagerAwareInterface {
use EventManagerAwareTrait;
public function createProfile(Profile $profile) {
$this->getEventManager()->trigger(__FUNCTION__, $this, ['profile' => $profile]);
$this->profileRepository->save($profile);
$this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, ['profile' => $profile]);
}
}
CreateListener
class CreateListener extends AbstractListenerAggregate {
public function attach(EventManagerInterface $eventManager, $priority = 100) {
// Priority of 100 seems to be ignored...
$this->listeners[] = $eventManager->getSharedManager()->attach(
ProfileService::class,
'createProfile.post',
[$this, 'onPostCreateProfile'],
$priority
);
}
public function onPostCreateProfile(EventInterface $event) {
// Do something
}
}
Based upon the answer I linked in the comments, could you try the following?
(Replace naming with your own and such)
namespace Demo\Listener;
use Demo\Event\DemoEvent;
use Zend\EventManager\Event;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
class DemoListener implements ListenerAggregateInterface
{
use EventManagerAwareTrait;
// You had "$priority = 100", this will cause an error in PHP >7.1 as it does not match interface
public function attach(EventManagerInterface $events, $priority = 1)
{
$sharedManager = $events->getSharedManager();
$sharedManager->attach(
ProfileService::class,
'createProfile', // <<-- first function to execute
[$this, 'createProfile'],
100
);
$sharedManager->attach(
ProfileService::class,
'createProfilePost', // <<-- second function to execute via daisy-chain
[$this, 'onPostCreateProfile'],
100
);
}
public function createProfile(Event $event)
{
// Gets passed along parameters from the ->trigger() function elsewhere
$params = $event->getParams();
$specificClass = $params[SpecificClass::class]; // get your event managed object
//
// Do your magic
//
// Don't forget to detach this function (Handler) so it doesn't accidentally run again
$event->getEventManager()->getSharedManager()->clearListeners(get_class($specificClass), $event->getName());
// Trigger events specific for the Entity/class (this "daisy-chains" events, allowing for follow-up functionality)
$specificClass->getEventManager()->trigger(
'createProfile.post',
$specificClass ,
[get_class($specificClass) => $specificClass ] // Params getting passed along
);
}
public function createProfilePost(Event $event)
{
// This function is daisy-chained by the above. Copy contents, do magic
}
}

ZF2 Accessing routes and posts via a factory

I am trying to access a route and post via a form factory. The route or the post contains an ID which I need to inject into my form so that I can build a select statement.
Currently I am injecting into the form via the controller using
$this->MyForm->get('elementName')->setOptions(array('value_options' =>$myArrayOfOptions));
My goal is to keep the business logic out of the controller hence why I am keen to use the formFactory instead however I do need access to the ID in the post or route to achieve this.
My Form Factory looks like this:
<?php
namespace MyModule\Form;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use AdminLottery\InputFilter\MyFilter;
use AdminLottery\Service\MyService;
class MyFormFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(
ServiceLocatorInterface $serviceLocator
)
{
//$serviceLocator is FormElementManager
$realSL = $serviceLocator->getServiceLocator();
//*** I NEED TO ACCESS THE ID / POST HERE TO SEND TO MY FORM
return new MyForm(
$realSL->get('Doctrine\ORM\EntityManager'),
$realSL->get('InputFilterManager')->get(MyFilter::class),
$realSL,
$realSL->get(MyService::class)
);
}
}
Any Ideas??
You can access the request instance
MyFormFactory
//...
$request = $serviceLocator->getServiceLocator()->get('Request');
$id = $request->getPost('id', false);
if ($id) $form->setOption('id', $id);
//...
Edit: This is very similar to another question I answered
Edit 2
In your factory can access the route params via the router's Zend\Mvc\Router\RouteMatch.
$request = $serviceLocator->getServiceLocator()->get('Request');
$router = $serviceLocator->getServiceLocator()->get('Router');
$match = $router->match($request); // \Zend\Mvc\Router\RouteMatch
$id = ($match) ? $match->getParam('id', false) : false;
if ($id) $form->setOption('id', $id); //....
For anyone looking for a reference, I thought I would add the final code I used:
Semi-final code::
$router = $realSL->get('Router');
$request = $realSL->get('Request');
$routeMatch = $router->match($request);
$matchArray = $routeMatch->getParams();
if (isset($matchArray['id'])) {
$id = (int) $matchArray['id'];
} else {
$id = 0;
}
Final code::
$realSL->get('application')->getMvcEvent()->getRouteMatch()->getParam('id', 0)
Here is an example for a controller factory, that would work in the same manner for any other factory:
namespace MyModule\Controller\Factory;
use MyModule\Controller\MyController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
/*
* #var Zend\ServiceManager\ServiceManager
*/
$realServiceLocator = $serviceLocator->getServiceLocator();
...
$router = $realServiceLocator->get('router');
$request = $realServiceLocator->get('request');
$routerMatch = $router->match($request);
...
$test1 = $routerMatch->getParams();
$test2 = $request->getQuery();
$test3 = $request->getPost();
...
return new MyController(...);
}
}

Mocking with Dart - How to test that a function passed as a parameter was called?

Admittedly, this is a little bit of an odd test case, but it's a problem I've ran in to. I have a class that takes a function as a parameter in it's constructor. I'd like to know if the function that was passed was called. Here's an example:
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(testClassMock.myNamedFunction);
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
So to explain a bit, ClassThatCallsRealClass takes a function as a parameter and calls it. If you were to pass in (Instance Of RealClass).myNamedFunction, this would in turn call the private function _theNamedFunction on RealClass. However, if you try to mock RealClass and redirect all calls from myNamedFunction to the RealClass myNamedFunction, this seems to fail. I don't see any clear way to get this to work, but I would think it'd be possible.
Any ideas?
In Dart, all functions are instances of class Function as you know since you pass an instance of Function to the ClassThatCallsRealClass constructor. Instances of Function have a method call() as shown here.
Meanwhile, Dart has a very good mocking capability described here (with thanks to #KWalrath for the update).
So all you need to do is test with mocks like with any other object. Just as described in the reference, create a spy for ClassThatCallsRealClass and a mock for your Function instance. Then use a verify(happenedOnce) on the call() method of the function.
To mock your function do this:
class MockFunction extends Mock {
call(int a, int b) => a + b;
}
var mock = new MockFunction();
mock(1,2); //returns 3
Of course the parameter list to call will match that of the real function. Pass mock to your spy on ClassThatCallsRealClass.
That worked for me:
library x;
import "package:unittest/unittest.dart";
import "package:unittest/mock.dart";
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
class MyFunc implements Function {
Function func;
String functionName;
MyFunc(this.func, this.functionName);
call() {
var inv = new MyInvocation(functionName);
func(inv);
}
}
main(List<String> args) {
test('xx', () {
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(new MyFunc(testClassMock.noSuchMethod, "myNamedFunction"));
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
});
}
class MyInvocation extends Invocation {
final String f;
MyInvocation(this.f);
bool get isGetter => false;
bool get isMethod => true;
bool get isSetter => false;
Symbol get memberName => new Symbol(f);
Map<Symbol, dynamic> get namedArguments => {};
List get positionalArguments => [];
}
testClassMock.myNamedFunction returns null so I call noSuchMethod directly instead which needs an Invocation.
Invocation is abstract so I created an implemented.
MyFunc is a class that wraps the function. MyFunc can be called as a function because it implements the call method.

assign argument when calling a function in actionscript

var callback = null;
addEventListener(Event.MouseDown, callback = function(e:Event){blabla....});
Can I assign arguments when calling a function?
Of course. Try it this way instead.
var callback = function(e:Event, arg1, arg2) { blabla....};
addEventListener(Event.MouseDown, function(e) { callback(e, 0, 1); } );
Or If I misunderstood your question, perhaps this way:
var callback = null;
addEventListener(Event.MouseDown,
callback = function(e:Event) { anotherfunction(arg1, arg2); });

Resources