Zend2: how to render view wrapped in layout into a variable? - zend-framework2

I need to save all rendered content (layout + view) in a variable to save it with Zend_Cache, I can't use Varnish, nginx or other software to do so. Currently I'm doing it like that:
$view->setTemplate('application/index/index');
$viewContent = $renderer->render($view);
$view = $this->getEvent()->getViewModel();
$view->content = $viewContent;
$content = $renderer->render($view);
Can anyone suggest me more elegant solution? Mb catching native render event with EventManager or some tricks with Response object or dispatch event? Would like to hear all suggestions.
Thanks!

Add two listeners to your Module class. one listener checks early, just after route if the match is one that's cached. The second listener waits for render and grabs the output to store it in cache:
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
// A list of routes to be cached
$routes = array('foo/bar', 'foo/baz');
$app = $e->getApplication();
$em = $app->getEventManager();
$sm = $app->getServiceManager();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($sm) {
$route = $e->getRouteMatch()->getMatchedRouteName();
$cache = $sm->get('cache-service');
$key = 'route-cache-' . $route;
if ($cache->hasItem($key)) {
// Handle response
$content = $cache->getItem($key);
$response = $e->getResponse();
$response->setContent($content);
return $response;
}
}, -1000); // Low, then routing has happened
$em->attach(MvcEvent::EVENT_RENDER, function($e) use ($sm, $routes) {
$route = $e->getRouteMatch()->getMatchedRouteName();
if (!in_array($route, $routes)) {
return;
}
$response = $e->getResponse();
$content = $response->getContent();
$cache = $sm->get('cache-service');
$key = 'route-cache-' . $route;
$cache->setItem($key, $content);
}, -1000); // Late, then rendering has happened
}
}
Just make sure you register a cache instance under cache-service in the service manager. You can update above example to check during the render event if the route is in the $routes array. Now you just check if the cache has the key, which might be slower than doing in_array($route, $routes) like during the render event.

Related

Pagination is not working in twilio call logs laravel

<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Controllers\ApiController;
use App\Model\User;
use Illuminate\Support\Facades\Validator;
use Auth;
use Twilio\Rest\Client;
use Twilio\TwiML\VoiceResponse;
class TwilioController extends ApiController
{
public function __construct(){
$sid = env('TWILIO_SID');
$token = env('TWILIO_TOKEN');
$this->twilio = new Client($sid, $token);
if (request('lang'))
\App::setLocale(request('lang'));
}
public function callLogs(Request $request){
try{
$twilioNumber = Auth::user()->twilio_number;
$calls = $this->twilio->calls
->read([], 20);
$data = [];
$i = 0;
foreach($calls as $call){
$data[$i]['from'] = $call->from;
$i++;
}
$responseData = array('status'=>true, 'message'=>'Data has been returned successfully!', 'data'=>$data);
} catch (\Exception $e) {
$responseData = array('status'=>false, 'message'=>$e->getMessage(), 'data'=>[]);
}
$res = json_encode($responseData);
print $res;
}
Pagination is not working when i get call history in twilio using laravel rest api. when i use page parameter with this then pagination is not working it give me save output as given me 1st page.
Postman parameters - Page:2
Thanks
I think Twilio's page() function will help you. Like read(), it accepts an array of options to narrow your search along with a pageSize parameter defaulting to 50 calls per page. (I believe the maximum value is 1000.)
Here is an example.
//Get all call logs. 20 per page
$calls = $twilio->calls->page( [], 20 );
$data = [];
foreach($calls as $call){
$data[] = $call->sid;
}
//You can access the previous and next page urls using the resource functions in the calls object return from twilio.
return response()->json(["prev" => $calls->getPreviousPageUrl(), "next" => $calls->getNextPageUrl(), "calls" => $data]);

ZF2/3 Load Modules from Database

I would like to know if there is a way to load modules from a database table in zend framework 2 preferable 3? I want to be able to dynamically disable or enable modules based on a status column inside a database table
I'm pretty sure you can accomplish this by attaching listener to some of ModuleManager events.
There are docs for v3 https://docs.zendframework.com/zend-modulemanager/module-manager/ and v2 https://framework.zend.com/manual/2.1/en/modules/zend.module-manager.module-manager.html
And don't forget autoloading for v3
By reading your question tom_cruz, I realize that I have exactly the same one ;-)
I went through the ZF2 source code of ModuleManager, ModuleManagerFactory, ModuleEvent and some listeners. After analyzing the flow, my new question is:
"What do I expect from an active/inactive module?"
Nearly every important stuff is done by the events Nemutaisama mentioned. i.e. loading config by adding getConfig() Method to the Module.php class.
ATM I'm not able to answer the above question. I'll come back to this one laters. But right now, I think it's an application problem, not a framework one.
I have done this some time ago by:
Create a "Core" Module responsible with fetching modules from database.
1.1 In the Module.php add module listener
public function init(ModuleManagerInterface $manager)
{
$sharedEventManger = $manager->getEventManager()->getSharedManager();
$sharedEventManger->attach(ModuleManager::class, ModuleEvent::EVENT_LOAD_MODULES_POST, new ModuleListener(), 10000);
}
Module Listener created by me was something like:
public function __invoke(ModuleEvent $event)
{
$target = $event->getTarget();
$serverName = $_SERVER['SERVER_NAME'];
if(! $serverName) { return; }
//module ok
if(! $target instanceof ModuleManagerInterface) { return; }
//config data
$configListener = $event->getConfigListener();
$config = $configListener->getMergedConfig(false);
//app modules
$modules = $target->getModules();
//select active modules
$adapter = new Adapter($config['db']);
$sql = new Sql($adapter);
$select = $sql->select(['c' => 'customers'])
->join(['cm' => 'customers_modules'], 'cm.customer_id = c.id', ['module' => 'module'])
->where(['c.domain' => $serverName])
->where(['cm.active' => 1]);
$statement = $sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
if($result instanceof ResultInterface && $result->isQueryResult() && $result->getAffectedRows()) {
//change db connection params here (if you use different db for customers)
while ($current = $result->current()) {
if (! in_array($current['module'], $modules)) {
try {
$target->loadModule($current['module']) ;
} catch (\RuntimeException $e) {
$target->loadModule('WQ' . str_replace($current['prefix'], '', $current['module']));
}
$modules[] = $current['module'];
$module = $target->getModule($current['module']);
if (($module instanceof ConfigProviderInterface) || (is_callable([$module, 'getConfig']))) {
$moduleConfig = $module->getConfig();
$config = ArrayUtils::merge($config, $moduleConfig);
}
}
$result->next();
}
}
$target->setModules($modules);
$configListener->setMergedConfig($config);
}
Hope it was useful.

Router Match not returning matchedRouteName

I have been slowly working my way through a few issues with the help of this website. I think I am onto my last problem now. In my boostrap I have the following code:
$eventManager = $event->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$serviceManager = $event->getApplication()->getServiceManager();
$eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, function($event) use ($serviceManager ){
$exception = $event->getParam('exception');
if ($exception) {
do {
$serviceManager->get('Logger')->crit(
sprintf(
"%s:%d %s (%d) [%s]\n",
$exception->getFile(),
$exception->getLine(),
$exception->getMessage(),
$exception->getCode(),
get_class($exception)
)
);
} while ($ex = $exception->getPrevious());
$response = $event->getResponse();
$response->setHeaders(
$response->getHeaders()->addHeaderLine('Location', "/")
);
$response->setStatusCode(302);
$response->sendHeaders();
return $response;
} else {
//no route - redirect to cms handler
$newEvent = clone $event;
$eventManager = $newEvent->getApplication()->getEventManager();
$routeMatch = new Router\RouteMatch(array('controller'=>'Website\Controller\Index','action'=>'index'));
$event->stopPropagation(TRUE);
$newEvent->setRouteMatch($routeMatch);
$newEvent->getResponse()->setStatusCode(200);
$eventManager->trigger('dispatch', $newEvent);
}
});
This all works fine except that $routeMatch is not returning matchedRouteName - and so I get a 404. I know that the contoller and action names are correct as for now I am just redirecting to homepage and by echoing out $routeMatch on "Displatch" the same info is coming back.
So, it seems simply using the trigger "dispatch" is not enough.
Any help appreciated.
Thanks
Adam
Further Info: I have confirmed that the dispatch is actually working - however, the triggered 404 is still being acted upon. That is a 404 header status is being set and the default 404 handler (view etc) is being displayed.
Solved by adding: $newEvent->getResponse()->setStatusCode(200);

Silex + RememberMeServiceProvider - login from code

I'm trying to make an ajax login form.
I've configured RememberMeServiceProvider according to http://silex.sensiolabs.org/doc/providers/remember_me.html
It works great with default login form - the one that goes to 'login_path' and is managed by Silex.
I'm now trying to make custom login controller (AJAX only).
It looks like this:
public function ajaxLogin(Request $request, Application $app) {
if (!AJAX)
die;
$email = $request->request->get('_username');
$pass = $request->request->get('_password');
if (!$email || !$pass)
return 0;
try {
$user = APP::repo('users')->findOneByEmail($email);
if (!$user)
return 0;
$encodedPass = $app['security.encoder_factory']->getEncoder($user)->encodePassword($pass, $user->getSalt());
if ($encodedPass !== $user->getPassword())
return 0;
$token = new UsernamePasswordToken($user, $pass, 'all', $user->getRoles());
$app['security']->setToken($token);
return 1;
} catch (Exception $e) {
return 0;
}
}
It works great, but I have a problem with Remember Me option. How can I make it work with above custom login controller?
The problem is that you bypass the security listerner to create the authentiated token by yourself.
To create the remember-me cookie, you must trigger the loginSuccess method of your RememberMeServices.
$app['security.remember_me.service.my-firewall']->loginSuccess($request, $response, $token);
Otherwise, you should implement your own PreAuthenticator :
http://symfony.com/doc/current/cookbook/security/api_key_authentication.html

ZF2 Redirect out of Controller

Before all, sorry for my poor english.
Good night/day/afternoon (depending of your location)
Sorry if I ask something that could be searched here, and I searched, even found it, but maybe I di not understand.
I need to check authentication in my controllers, so, I implement a master controller and extend all my real controllers to it. In my master controller I check authentication and do it well, but when I try to redirect an unauthenticated user it crashes!
Searching in web, I realized that "init, preDispatch, etc" methods even don't exist more, just the "construct" method, so I try in it, but in construct there is not an event manager, so I stop here...
This is my code:
public function __construct(){
$r = new SimpleRouteStack();
$r->addRoute('logoff', Literal::factory(array(
'route'=>'/suporte/logoff',
'defaults' => array(
'action' => 'logoff',
'controller' => 'Suporte\Controller\Index',
)
)
)
);
$e = new MvcEvent();
$e->setRouter($r);
$this->setEvent($e);
$this->getEvent()->setResponse(new Response());
$this->getEventManager()->attach('*',array($this,'mvcPreDispatch'),100);
public function mvcPreDispatch(){
$uri = explode('/',substr($_SERVER['REQUEST_URI'],1));
$uri[2] = !isset($uri[2]) ? "index" : $uri[2];
$auth = new AuthenticationService();
$identity = $auth->getStorage()->read();
$acl = $identity[2];
if (!$auth->hasIdentity()){
$this->redirect()->toRoute('suporte/logoff');
}elseif ( !$acl->hasResource($uri[0].'/'.$uri[1])
||
!$acl->isAllowed($identity[1],
$uri[0].'/'.$uri[1],
$uri[2]
)
)
$this->redirect()->toRoute('logoff');
else{
/* $this->layout()->id = $identity[0]->getId();
$this->layout()->nome = $identity[0]->getNome();
$this->layout()->cargo = $identity[0]->getCargo(); */
echo "permitido";
//$this->layout()->recursos = $this->acl->getResources();
}
}
is your Suporte\Controller\logoff also extending the masterclass. If yes then it will lead to an infinite loop indexController>masterController(!loggedin)>logoutController>masterController(!loggedin)>logoutController>..
How I used to say "IT is witchcraft"!!!
When I wake up today, turn on my computer, test the controller and BAZINGA!!! is running ok...
Why? I don't dare to question...
my code is that:
abstract class PadraoControllerSuporte extends AbstractActionController{
public function setEventManager(EventManagerInterface $events){
parent::setEventManager($events);
$controller = $this;
$events->attach('dispatch', function ($e) use ($controller) {
if (is_callable(array($controller, 'verificaAuth'))){
call_user_func(array($controller, 'verificaAuth'));
}
}, 100);
}
public function verificaAuth(){
$uri = explode('/',substr($_SERVER['REQUEST_URI'],1));
$uri[2] = !isset($uri[2]) ? "index" : $uri[2];
$auth = new AuthenticationService();
$identity = $auth->getStorage()->read();
$acl = $identity[2];
if (!$auth->hasIdentity())
$this->redirect()->toRoute('suporte/logoff');
elseif ( !$acl->hasResource($uri[0].'/'.$uri[1]) !$acl->isAllowed( $identity[1],
$uri[0].'/'.$uri[1],
$uri[2]
)
)
$this->redirect()->toRoute('logoff');
else{
/* $this->layout()->id = $identity[0]->getId();
$this->layout()->nome = $identity[0]->getNome();
$this->layout()->cargo = $identity[0]->getCargo(); */
echo "permitido";
//$this->layout()->recursos = $this->acl->getResources();
}
}
What I understood:
I think that "setEventManager" was superimposed, so it sets to listen the "dispatch" event and call my verificaAuth function what redirects (if not authenticated) to logoff or allow it is authenticated.
Hope this is usefull...
Thanks for all!!!

Resources