zend framework 3 - how to disable layout for entire controller - php

In Zend Framework 3, is it possible to disable the layout for an entire controller, preferably in the __construct() or onDispatch() methods?
I know that I can disable the layout for specific actions, for example:
public function indexAction()
{
$view = new \Zend\View\Model\ViewModel();
$view->setTerminal(true);
return $view;
}
However, I would like to disable the layout for all actions in the controller without having to copy and paste the above code in every action.

In your Module class :
public function onBootstrap(MvcEvent $e)
{
$sharedEvents = $e->getApplication()
->getEventManager()
->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch',
function ($e) {
if ($e->getRouteMatch()->getParam('controller') == '[your controller name in lowercase]') {
$result = $e->getResult();
if ($result instanceof \Zend\View\Model\ViewModel) {
$result->setTerminal(true);
} else {
throw new \Exception(
__METHOD__ . ' expected \Zend\View\Model\ViewModel');
}
}
});
}

Related

php shortest version of calling a class

i saw a lot of ways that you can use to call a class inside another one in PHP and i want your opinion about the shortest version of calling a class.
lets say we have a class name view,
and another class name controller
class View
{
private $data = array();
private $render = FALSE;
public function __construct($template , $datas = null)
{
try {
$file = strtolower($template) . '.php';
if (file_exists($file)) {
if($datas > 0) {
foreach($datas as $data) {
array_push($this->data, $data);
}
}
$this->render = $file;
} else {
die('Template ' . $template . ' not found!');
}
}
catch (customException $e) {
echo $e->errorMessage();
}
}
public function __destruct()
{
extract($this->data);
include($this->render);
}
}
and
require_once "system/autoload.php";
class Controller {
function index() {
$view = new View('something');
}
i know that i can use
$view = new View('something');
or use OOP and extent and call a function from view inside controller like
$this->viewFunction();
but is there any way that i can call view class inside controller like this
View('something)
i want to make it shortest version possible
if it is not possible or i have to make change inside compiler well just give me the shortest version
thank you all
You can surely do this in PHP. Have a look at magic methods, especially __invoke()
class View
{
public function __invoke(string $template)
{
return $template;
}
}
You can simply invoke it by doing
$view = new View();
$view('my template');

redirect for only admin module

I have the following code inside my Admin Module.php
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$em = $application->getEventManager();
if(!SystemUtils::isApiRequest()){
$em->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this,'initUser'),10000);
}
}
public function initUser(MvcEvent $e)
{
$isLoggedIn=false;
// determined elsewhere
$action = Pluto::registry('application_action');
if($action!=='login' && !$isLoggedIn){
$viewHelperManager = $e->getApplication()->getServiceManager()->get('ViewHelperManager');
$renderer = $viewHelperManager->getRenderer();
$url = $renderer->plutourl('login');
$response = $e->getApplication()->getServiceManager()->get('Response');
$response->getHeaders()->clearHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302)->sendHeaders();
exit();
}
}
The problem is this is only supposed to activate when inside a page within the admin module and you are not logged in but its operating for every request regardless of the module the code is inside
I want to limit this check to just when accessing the admin module.
Update
$sm = $e->getApplication()->getServiceManager();
$router = $sm->get('router');
$request = $sm->get('request');
$matchedRoute = $router->match($request);
returns the admin controller class name which i can use but i want it for all of the admin controller classes
You can use the shared event manager to attach to events in a given name space. In the example below we attach to an event only for the current modules namespace.
use Zend\Mvc\MvcEvent;
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager()->getSharedManager();
$sm = $app->getServiceManager();
$em->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function ($e) use ($sm) {
/**
* this will be triggered only by controlers in
* this module/namespace...
*/
$this->doSomething();
});
}
You could add this inside your admin module and only this event will only be listened to for classes inside this module / namespace.
You should use the MvcEvent object!
If all controllers are under a single namespace, for instance \Admin\Controller:
$namespace = 'Admin\\Controller\\';
$matchController = $e->getRouteMatch()->getParam('controller');
if( $namespace === substr($matchController, 0, strlen($namespace))
{
// check here if user is logged in.
}
else {
// not an admin controller, do nothing.
}

Cannot attach Events in module init()-method

I read about some of the best practices for ZF2. There, it was explained to attach the events from MVC in the init()-Method of the module's Module class:
class Module {
public function getAutoloaderConfig() {
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
}
public function init(ModuleManager $moduleManager) {
echo 'init<br>';
$em = $moduleManager->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
public function onDispatch(MvcEvent $e){
echo 'onDispatch<br>';
}
...
It results in getting no error, nice. But the event is not caught...
Any ideas? I tried the SharedManager too, but it only worked for the EVENT_DISPATCH ...
Unless for specific cases, it's better to register your events in onBootstrap.
init is for "early events".
I found a link that is quite clear : http://samsonasik.wordpress.com/2013/03/30/zend-framework-2-getting-closer-with-eventmanager/
You can find the order of defaults MVC events in Zend\ModuleManager\Listener\DefaultListenerAggregate::attch :
public function attach(EventManagerInterface $events)
{
$options = $this->getOptions();
$configListener = $this->getConfigListener();
$locatorRegistrationListener = new LocatorRegistrationListener($options);
// High priority, we assume module autoloading (for FooNamespace\Module classes) should be available before anything else
$this->listeners[] = $events->attach(new ModuleLoaderListener($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE_RESOLVE, new ModuleResolverListener);
// High priority, because most other loadModule listeners will assume the module's classes are available via autoloading
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new AutoloaderListener($options), 9000);
if ($options->getCheckDependencies()) {
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new ModuleDependencyCheckerListener, 8000);
}
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new InitTrigger($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new OnBootstrapListener($options));
$this->listeners[] = $events->attach($locatorRegistrationListener);
$this->listeners[] = $events->attach($configListener);
return $this;
}
You'll be better using the shared manager. The example bellow is for disabling layout when we got a xmlHttpRequest and the priority -95 is a key point to make things work.
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
// Hybrid view for ajax calls (disable layout for xmlHttpRequests)
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractController', MvcEvent::EVENT_DISPATCH, function(MvcEvent $event){
/**
* #var Request $request
*/
$request = $event->getRequest();
$viewModel = $event->getResult();
if($request->isXmlHttpRequest()) {
$viewModel->setTerminal(true);
}
return $viewModel;
}, -95);
}
See http://akrabat.com/zend-framework-2/module-specific-bootstrapping-in-zf2/
Only found this solution: attach listeners in onBootstrap()-Method in the Module.php class:
...
public function onBootstrap(MvcEvent $e){
echo 'onBootstrap<br>';
$em = $e->getApplication()->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
...

Kostache - before() method

Well, is there something like before() method in kostache module? For example, if I have a couple of PHP lines inside of the view file, I'd like to execute them separately inside of the view class, without echoing anything in the template itself. How can I handle that?
You can put this type of code in the constructor of your View class. When the view is instantiated, the code will run.
Here is a (slightly modified) example from a working application. This example illustrates a ViewModel that lets you change which mustache file is being used as the site's main layout. In the constructor, it chooses a default layout, which you can override if needed.
Controller:
class Controller_Pages extends Controller
{
public function action_show()
{
$current_page = Model_Page::factory($this->request->param('name'));
if ($current_page == NULL) {
throw new HTTP_Exception_404('Page not found: :page',
array(':page' => $this->request->param('name')));
}
$view = new View_Page;
$view->page_content = $current_page->Content;
$view->title = $current_page->Title;
if (isset($current_page->Layout) && $current_page->Layout !== 'default') {
$view->setLayout($current_page->Layout);
}
$this->response->body($view->render());
}
}
ViewModel:
class View_Page
{
public $title;
public $page_content;
public static $default_layout = 'mytemplate';
private $_layout;
public function __construct()
{
$this->_layout = self::$default_layout;
}
public function setLayout($layout)
{
$this->_layout = $layout;
}
public function render($template = null)
{
if ($this->_layout != null)
{
$renderer = Kostache_Layout::factory($this->_layout);
$this->template_init();
}
else
{
$renderer = Kostache::factory();
}
return $renderer->render($this, $template);
}
}

Action_Helper in separte module does not get loaded

I followed this great article http://weierophinney.net/matthew/archives/246-Using-Action-Helpers-To-Implement-Re-Usable-Widgets.html, but currently i can't get work my simplified example.
PROBLEM The preDispatch does not get loaded.
I created new module user (there is also controller UserController, i hope this wont mess up the loading).
I have added two files in user.
Bootstrap.php - under module user
class User_Bootstrap extends Zend_Application_Module_Bootstrap {
public function initResourceLoader() {
$loader = $this->getResourceLoader();
$loader->addResourceType('helper', 'helpers', 'Helper');
}
protected function _initHelpers() {
Zend_Controller_Action_HelperBroker::addHelper(
new User_Helper_HandleLogin()
);
}
New folder under /user/helpers and class HandleLogin.
class User_Helper_HandleLogin extends Zend_Controller_Action_Helper_Abstract {
protected $view;
public function preDispatch() {
echo 'called';
if (($controller = $this->getActionController()) === null) {
return;
}
$this->createProfileWidget();
}
public function createProfileWidget() {
if (!$view = $this->getView()) {
return;
}
$view->user = '<h2>HELLO WORLD</h2>';
}
public function createLoginForm() {
}
public function getView() {
if ($this->view !== null) {
return $this->view;
}
$controller = $this->getActionController();
$view = $controller->view;
if (!$view instanceof Zend_View_Abstract) {
return;
}
//$view->addScriptPath(dirname(__FILE__) .'/../views/scripts');
$this->view = $view;
return $view;
}
}
And lastly added into layout.phtml the output.
<?php echo $this->user ?>
is init() function of User_Helper_HandleLogin works? is User_Bootstrap works? :) maybe you forget resources.modules[] = in config.ini?

Categories