How to create session in ZF2 - zend-framework2

Hi i started learning ZF2 a week ago and i am facing some issues in how to work with session in ZF2.
namespace MyApplication\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Session\Container; // We need this when using sessions
class UserController extends AbstractActionController {
public function loginAction() {
$user_session = new Container('user');
$user_session->username = 'bravo';
}
public function welcomeAction() {
$user_session = new Container('user');
$username = $user_session->username; // $username now contains 'bravo'
}
}
Can anyone please help me with the exact code snippet or tell me where i am wrong.
Thanks in advance :)

I am also not much familiar to ZF2 but my code may solve the issue you are facing.
class UserController extends AbstractActionController {
public function loginAction() {
// Store username in session
$user_session = new Container('user');
$user_session->username = 'bravo';
return $this->redirect()->toRoute('welcome');
}
The issue was in the function named loginAction you have not redirected after creating the session
Hope it helps you

Related

How to use my custom google calendar module into another custom todo module in Laminas MVC?

I've created a Calendar module in laminas MVC which interacts with Google Calendar and then created another Todo module which is supposed to interact with my Calendar module. The signature of CalendarController in Calendar module is like
public function __construct(
ListProcess $listProcess,
AddProcess $addProcess,
EditProcess $editProcess,
DeleteProcess $deleteProcess
)
Now my code in Todo module that is supposed to initiate the scheduling process is as below
public function execute(): array
{
$todo = json_decode((new CrmApiService())->getTodo($this->getTodoId()), true);
$eventData["summary"] = $todo->title;
$eventData["description"] = $todo->content;
$eventData["startDateTime"] = $todo->nextActionDate;
$eventData["endDateTime"] = $todo->nextActionDate;
$calendar = new CalendarController();
return $calendar->scheduleFromAnotherSource($eventData);
}
when I execute this, I get an error like below
Too few arguments to function CalendarModule\Controller\CalendarController::__construct(), 0 passed in D:\laminas\todo-module-integrated\vendor\iss-module\todo-module\src\Process\TodoScheduleProcess.php on line 53 and exactly 4 expected
I know that I'm not supposed to call the CalendarController directly rather it should be through a Service.
My question is, how should I create a Service in Todo module that has the dependency on Calendar module and it should interact with Calendar module without requiring the involvement of CalendarController which has further dependencies?
Thanks for all the help.
Here's how I've implemented it. (May it'll help someone)
In my Calendar-module, the logic of adding is separate from CalendarController and its called AddProcess, this is how I add an event from the controller. $result = $this->addProcess->execute($this->params()->fromPost());. The Google authentication is being handled through a separate service CalendarClientService. All my processes access this service to authenticate as below and then get executed.
$client = $this->calendarClientService->getClient();
if (!$this->calendarClientService->authenticateClient($client)) {
return ["error" => "authentication", "url" => filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL)];
}
Now I've created a new service in Calendar-module as below where I just called AddProcess and passed it the new eventData.
class CalendarService
{
protected AddProcess $addProcess;
public function __construct(AddProcess $addProcess)
{
$this->addProcess = $addProcess;
}
public function scheduleAsEvent($eventData)
{
$eventData["startDateTime"] = Carbon::parse($eventData["startDateTime"])->format("Y-m-d\TH:i");
$eventData["endDateTime"] = Carbon::parse($eventData["endDateTime"])->format("Y-m-d\TH:i");
return $this->addProcess->execute($eventData);
}
}
Then from my Todo-module, I access this service as below
namespace TodoModule\Process;
use Carbon\Carbon;
use Google\Exception;
use Laminas\Cache\Exception\ExceptionInterface;
use Laminas\Mvc\Controller\AbstractActionController;
use CalendarModule\Service\CalendarService;
use TodoModule\Service\CrmApiService;
class TodoScheduleProcess extends AbstractActionController
{
protected int $todoID;
protected CalendarService $calendarService;
public function __construct(CalendarService $calendarService)
{
$this->calendarService = $calendarService;
}
public function execute(): array
{
$todo = json_decode((new CrmApiService())->getTodo($this->getTodoId()));
$eventData["summary"] = $todo->title;
$eventData["description"] = $todo->content;
$eventData["startDateTime"] = $todo->nextActionDate;
$eventData["endDateTime"] = $todo->nextActionDate;
return $this->calendarService->scheduleAsEvent($eventData);
}
public function getTodoId()
{
return $this->todoID;
}
public function setTodoId($id)
{
$this->todoID = $id;
}
}```

LightInject - No scope when calling WebApi OWIN Identity TokenEndpointPath

I have a very basic WebAPI setup with token authentication.
In the application start I do:
protected void Application_Start()
{
DependencyConfig.RegisterDependecis();
//...
//...
}
Which calls:
public class DependencyConfig
{
private static ServiceContainer _LightInjectContainer;
public static ServiceContainer LightInjectContainer
{
get { return _LightInjectContainer; }
}
public static void RegisterDependecis()
{
var container = new LightInject.ServiceContainer();
container.RegisterApiControllers();
container.ScopeManagerProvider = new PerLogicalCallContextScopeManagerProvider();
container.EnableWebApi(GlobalConfiguration.Configuration);
container.Register<IRegistrationManager, RegistrationManager>(new PerScopeLifetime());
_LightInjectContainer = container;
}
}
Now, when the client invokes the token endpoint (requests a token), the provider I defined here:
OAuthOptions = new OAuthAuthorizationServerOptions
{
//...
//...
Provider = new SimpleAuthorizationServerProvider()
//...
//...
};
Is being used with this method:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
//...
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//...
// Here I get the exception!
var registrationManager = DependencyConfig.LightInjectContainer.GetInstance<IRegistrationManager>();
//...
}
//...
}
And when I try to get the instance the following error:
Attempt to create a scoped instance without a current scope.
I know the LightInject has a concept of Start/End scope for each request and It's actually telling me that no scope was started. But I can't seem to figure what exactly is broken and needs to be fixed.
By reading on of the last answers in this question, I came up with this solution: (Starting a scope manually)
using(DependencyConfig.LightInjectContainer.BeginScope())
{
IRegistrationManager manager = DependencyConfig.LightInjectContainer.GetInstance<IRegistrationManager>();
}
Technicaly it works, but I not sure if it's the right solution regarding whats happening behind the scenes.
I am the author of LightInject
Can you try this inside your handler(SimpleAuthorizationServerProvider)
request.GetDependencyScope().GetService(typeof(IRegistrationManager)) as IRegistrationManager;
There is actually no reason that you should expose the container as a static public member as this makes it very easy to start using the service locator anti-pattern.
Take a look at this blog post for more information.
http://www.strathweb.com/2012/11/asp-net-web-api-and-dependencies-in-request-scope/

where is the autoload? trying to unit test

i am following a getting started tutorial on zend framework 2, in one of the topics it suggests using tests, the code it suggests is:
namespace ApplicationTest\Controller;
use ApplicationTest\Bootstrap;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use Application\Controller\IndexController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use PHPUnit_Framework_TestCase;
class IndexControllerTest extends PHPUnit_Framework_TestCase
{
protected $controller;
protected $request;
protected $response;
protected $routeMatch;
protected $event;
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
}
as you can see there is no __autoload($class).
to manually make it work i added include "../../Bootstrap.php"; it did solve the problem but i remember once i could get this code to work, and the tutorial doesn't seem to forget something conceptually obvious and there is no feedback about it in the Topic comments , there may be something I am missing, how would the code above probably work?
I managed to get it working but noticed you couldn't use phpUnit's extended Request and Response objects. These are the instructions for early 2.0 release. At least after 2.0.7, the instructions are much different and the code is cleaner:
http://zf2.readthedocs.org/en/latest/user-guide/unit-testing.html
<?php
namespace ApplicationTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class IndexControllerTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(
include '/path/to/application/config/test/application.config.php'
);
parent::setUp();
}
public function testIndexActionCanBeAccessed()
{
$this->dispatch('/');
$this->assertResponseStatusCode(200);
$this->assertModuleName('application');
$this->assertControllerName('application_index');
$this->assertControllerClass('IndexController');
$this->assertMatchedRouteName('home');
}
}
In this example testing is carried out by extending Zend's controller test case, which was the way controller tests were carried out with zf1.

How to intercept all the ASP.NET WebApi controller action methods calls with Ninject interception for logging?

Our company has the need to log certain things each time one of our action methods of our ASP.NET WebApi controllers gets called. Since we use Ninject for the DI right now, we'd like to use it also for this purpose. This is what I have tried so far.
I have Ninject, Ninject.Extensions.Interception and Ninject.Extensions.Interception.DynamicProxy installed through NuGet and I have the following module
public class InterceptAllModule : InterceptionModule
{
public override void Load()
{
Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
}
}
Where TimingInterceptor is
public class TimingInterceptor : SimpleInterceptor
{
readonly Stopwatch _stopwatch = new Stopwatch();
protected override void BeforeInvoke(IInvocation invocation)
{
_stopwatch.Start();
}
protected override void AfterInvoke(IInvocation invocation)
{
_stopwatch.Stop();
string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
Log.Info(message + "\n");
_stopwatch.Reset();
}
}
Now, when I try to hook the module up with ninject kernel, and run my site
var kernel = new StandardKernel(new InterceptAllModule());
However, whenever there is a call coming in to one of the action method, it throws an error saying
Cannot instantiate proxy of class: MyApiController.
Could someone with experience point out what I'm doing wrong please? Thanks.
Update
So using your Code and Remo's excellent point about needing the action methods to be virtual and putting in an empty default constructor (just to placate dynamic proxy, keep your other constructor still) I have got both the action filter and the interception approach working.
I would say that as it stands your code will intercept potentially unwanted methods on the ApiController so you will probably also need to put some code in place to filter these out e.g. ExecuteAsync and Dispose.
My only other point is performance. Huge disclaimer these are just very basic tests (using the action filter approach each time to log the stats), I invite you to do your own(!)... but using the DynamicProxy interceptor I was getting a time of around 4 milliseconds per get request
[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Commenting out the Interception code and using an Action filter I was getting sub millisecond performance:
[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
It is up to you whether this is actually an issue or concern but I thought I would point this out.
Previous Response
Have you rulled out using ActionFilters? This is the natural extension point for AOP on an MVC action.
If you were interested in methods other than the actual action on the controller then I would understand but I thought I would post a suggestion anyway.
Inspired by Are ActionFilterAttributes reused across threads? How does that work? and Measure Time Invoking ASP.NET MVC Controller Actions.
Updated to show the exclusion of the timer when method tagged. Inspiration from core WebApi framework specifically AllowAnonymousAttribute and AuthorizeAttribute
Register this globally so that all actions are monitored by this:
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Then:
public class TimingActionFilter : ActionFilterAttribute
{
private const string Key = "__action_duration__";
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (SkipLogging(actionContext))
{
return;
}
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
stopWatch.Start();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if(stopWatch != null)
{
stopWatch.Stop();
var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
}
}
private static bool SkipLogging(HttpActionContext actionContext)
{
return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
}
}
And
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{
}
Now you can exclude the global filter using:
public class ExampleController : ApiController
{
// GET api/example
[NoLog]
public Example Get()
{
//
}
}
For anyone still lurking, the reason I wanted to use Ninject was so I could inject a logger (or anything else) into the interceptor, but I wanted to intercept all actions.
Mark's answer is perfect, but instead of registering globally using
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
bind your filter with Ninject using
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).
You'll need to create an appropriate contructor in the TimingActionFilter class.

Mock a web service used in an action filter

I have an external-to-my-solution web service that I'm using in an ActionFilter. The action filter grabs some basic data for my MasterPage. I've gone back and forth between using an action filter and extending the base controller class, and decided the action filter was the best approach. Then I started unit testing (Yeah, yeah TDD. Anyway... :D )
So I can't mock (using Moq, btw) a web service in an action filter because I can't inject my mock WS into the action filter, since action filters don't take objects as params. Right? At least that's what I seem to have come to.
Any ideas? Better approaches? I'm just trying to return a warning to the user that if the web service is unavailable, their experience might be limited.
Thanks for any help!
namespace MyProject.ActionFilters
{
public class GetMasterPageData : ActionFilterAttribute
{
public ThatWS ws = new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
try {
DoStuff();
}
catch ( NullReferenceException e ) {
context.Session["message"] = "There is a problem with the web service. Some functionality will be limited.";
}
}
}
}
Here's a quick and dirty approach:
public class GetMasterPageData : ActionFilterAttribute
{
public Func<ISomeInterface> ServiceProvider = () => new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var result = ServiceProvider().SomeMethod();
...
}
}
And in your unit test you could instantiate the action filter and replace the ServiceProvider public field with some mocked object:
objectToTest.ServiceProvider = () => new SomeMockedObject();
Of course this approach is not as clean as the one suggested by #Ryan in the comments section but it could work in some situations.

Resources