I want to automatically set the view on a GET request in Laravel. In the BaseController constructor I do this:
if (Request::server('REQUEST_METHOD') === 'GET')
{
$action = explode('#', Route::currentRouteAction());
$view = explode('get', $action[1]);
$view = strtolower(end($view));
$controller = strtolower(explode('Controller', $action[0])[0]);
$this->data['view'] = $controller . '.' . $view;
}
So basically if we make a request for /some/page it will look for a view file named views/some/page.blade.php.
Currently I set some data and other properties using $this->data. So I build my data up before sending the view in each method:
$this->layout->with($this->data);
I end up having the above call in EVERY GET method and would like automate this whole thing. The problem with using $this->data is that I can't access it any filters or other closures. Is there a magic method or global data store I'm not using which I could call at the end of every request and just pump out the layout?
function afterEveryThing()
{
$this->layout->with($this->data);
}
Something like the above in the BaseController or somewhere where I could do this?
Shooting from the hip here, but could you do your routing something like below. This is a bad idea to use exactly as shown, but could be a starting point for what you are trying to do.
Route::any('{controller}/{method}', function($controller, $method) {
$controllerName = ucfirst($controller) . "Controller";
$controllerObject = new $controllerName;
if (Request::server('REQUEST_METHOD') === 'GET')
{
$controllerObject->$method();
return View::make("$controller.$method")->with('data', $controllerObject->data);
}
else
{
return $controllerObject->$method();
}
});
This will work (Laravel 4)
App::after(function($request, $response)
{
//
});
Or
In any version rename actions and implement magic method __call in controller class. For example for route to "IndexController#index" action:
IndexController.php
private function __call($method, $args) {
... look for a view...
if (in_array('my_'.$method, self::$methods)) {
//call to index translated to my_index
call_user_func_array(array($this,'my_'.$method), $args);
} else {
//error no action
abort(404);
}
... after every thing ...
}
public function my_index(Request $request) {
... do action ...
}
Related
I try to work with a simple Router class (learning basics before a framework, but I think I got something wrong with the example router I used. Below is a very small router class I got from a colleague and I tried to integrate it into my code to substitute previous uses where I just used echo before (commented out part of the code).
both loginController showLoggedInUser() and registerController index() are just used to render an html template.
Both $router->add() would work if I use it just to add a single route, however my router does not save multiple routes in the array because it seems every route will be saved under the key '/' and in case I provide mutiple routes it seems my previous routes are simply overwritten. So I guess I would need to adjust the Router class. How can I fix this?
PHP 7.4 used
Router.php
<?php
declare(strict_types=1);
class Router
{
private array $route;
public function add(string $url, callable $method): void
{
$this->route[$url] = $method;
}
public function run()
{
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if(!array_key_exists($path, $this->route))
{
exit();
}
return call_user_func($this->route[$path]);
}
}
index.php
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
session_start();
$router = new Router();
$mysqliConnection = new MysqliConnection();
$session = new SessionService();
$loginController = new Login($mysqliConnection);
$router->add('/', [$loginController, 'showLoggedInUser']);
//echo $loginController->showLoggedInUser();
$registerController = new Register($mysqliConnection);
$router->add('/', [$registerController, 'index']);
//echo $registerController->index();
echo $router->run();
Not sure of the overall principle of having two routes with the same name, but you could achieve this using a list of callables for each route.
I've made some changes (including the callable passed for each route) to show the principle, but you should get the idea...
class Router
{
private array $route;
public function add(string $url, callable $method): void
{
$this->route[$url][] = $method;
}
public function run()
{
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if(!array_key_exists($path, $this->route))
{
exit();
}
foreach ( $this->route[$path] as $paths ) {
$paths();
}
// Not sure what to return in this case.
// return call_user_func($this->route[$path]);
}
}
$router = new Router();
// $mysqliConnection = new MysqliConnection();
// $session = new SessionService();
// $loginController = new Login($mysqliConnection);
$router->add('/', function () { echo "login"; } );
// $registerController = new Register($mysqliConnection);
$router->add('/', function () { echo "Register"; });
echo $router->run();
I would instead recommend having separate url's, /login and /register so that they can be called separately.
Looking at altorouter Q&A, I found this example here on how to call a named controller and method when using altorouter.
$router->map('GET','/content/[:parent]/?[:child]?', 'content_controller#display_item', 'content');
The syntax
$router(method, route, target, name(optional));
I get how to map the method, route but do not understand how to get the target to map/call an unnamed controller or method?.
I tried this and it seems to work but I'll yet need confirmation if this is how we are supposed to it
$router->map('GET', '/[a:controller]/[a:action]?', function ($controller, $action = null) {
if ($action=='') {$action = 'index';}
if (method_exists($controller, $action)) {
$controller::$action();
} else {
echo 'missing';
}
});
Here the router is /[a:controller]/[a:action]? and the target is a function
function ($controller, $action = null) {
if ($action=='') {$action = 'index';}
if (method_exists($controller, $action)) {
$controller::$action();
} else {
echo 'missing';
}
});
which get the unnamed controller and checks for the method/action. If the method/action is null index is assigned to the variable $action and then mapped.
Please confirm if it is the right war of calling an undefined controller and mehtod
When I am using CodeIgniter to implement a small application, I want all business checking functions in controllers could be defined outside the controller for more flexible.
The checking functions just like session checking, user information completion checking, or has user post an article, and so on.
Firstly, I tried to use helper to implement. In my core controller which extended from CI_Controller I wrote a check function:
protected function check () {
$this->checked = TRUE;
$methods = func_get_args();
foreach ($methods as $method) {
$m = 'check_' . $method;
if (!function_exists($m)) {
$this->load->helper("filters/$m");
}
if ($m() === FALSE) {
return FALSE;
}
}
return TRUE;
}
Then in any controller I can use this method to check my business logic like this:
public function something ()
if (!$this->check('session')) {
return $this->relogin();
}
// ...
if (!$this->check('userinfo')) {
return $this->redirect('settings');
}
// ...
if (!this->check('has_post')) {
// ...
}
// ...
}
But it has a problem that all helper function are global, and can't invoke protected functions in $CI instance. I didn't find a way how to invoke a instance function outside like JavaScript's call/apply.
So I turned to check the hook document of CI. But I don't think it helps, because the hook point can't be inside of any controller functions. It must be outside.
At last, I can just fallback to put all checking functions into core controller class. So I wonder is there any way to use interceptor as in Java structs?
You can use _remap()!
add a function into the class:
_remap($method, $params=[]) {
if (true) {
$this->$method($params);
} else {
$this->method5();
}
}
The logic is that everytime someone call a Method, the API would FIRST execut the REMAP function... inside you can do whatever you need and then decide if the API should execute the called method or another or none...
Hello I am trying to retrieve the values returned by a controller action in my own library abstract in one of the methods of dispatch of Zend Framework, I wonder if this feat possible and if so how do it.
My code is as follows:
IndexController
class IndexController extends My_Controller
{
public function init()
{
/* Initialize action controller here */
}
public function indexAction()
{
// action body
return 'hello world';
}
}
My_Controller
abstract class My_Controller extends Zend_Controller_Action
{
/**
* Initialize Core_Controller
* #param Zend_Controller_Request_Abstract $request
* #param Zend_Controller_Response_Abstract $response
* #param array $invokeArgs
*/
public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
{
parent::__construct($request, $response, $invokeArgs);
$this->_helper->viewRenderer->setNoRender();
}
public function preDispatch()
{
//something here
}
public function postDispatch()
{
//something here
}
public function dispatch()
{
//something here
}
}
I need to get the value of what was returned in the controllador in this library in order to transform it into json and then print to screen.
Thnk
In ZF 1 there isn't a way to get the return value from the controller action. This value is never used or captured by Zend Framework itself.
Take a look at Zend/Controller/Action.php line 516 (ZF 1.11.11) and this is the point where ZF calls your controller action, and the return value is not captured or used.
public function dispatch($action)
{
// Notify helpers of action preDispatch state
$this->_helper->notifyPreDispatch();
$this->preDispatch();
if ($this->getRequest()->isDispatched()) {
if (null === $this->_classMethods) {
$this->_classMethods = get_class_methods($this);
}
// If pre-dispatch hooks introduced a redirect then stop dispatch
// #see ZF-7496
if (!($this->getResponse()->isRedirect())) {
// preDispatch() didn't change the action, so we can continue
if ($this->getInvokeArg('useCaseSensitiveActions') || in_array($action, $this->_classMethods)) {
if ($this->getInvokeArg('useCaseSensitiveActions')) {
trigger_error('Using case sensitive actions without word separators is deprecated; please do not rely on this "feature"');
}
$this->$action(); // <--- line 516 - this calls your action
} else {
$this->__call($action, array());
}
}
$this->postDispatch();
}
// whats actually important here is that this action controller is
// shutting down, regardless of dispatching; notify the helpers of this
// state
$this->_helper->notifyPostDispatch();
}
As you can see, the value returned by the controller is never used. Also, in ZF2, they are changing the way controller actions work so the return values actually have meaning so you may want to think of a different approach.
The quickest thing I can think of at the moment would be instead of trying to return a value from the controller, just set a registry value that you can fetch later.
e.g.
public function returnAction()
{
// ...
Zend_Registry::set('controller_return_value', 'hello world');
}
and then later, in your plugin or wherever you are trying to get the value:
try {
$retval = Zend_Registry::get('controller_return_value');
} catch (Zend_Exception $ex) {
$retval = null; // no return value set by controller
}
I have a question about the cache of a view, suppose I have the following block of code:
<?php
class View {
public function render ( $template , $path = null ) { }
// ...
}
This is my 'MainView', in which the class is extended in all other views, such as 'ClientsView' .. etc.
But, I wanted to implement a way to intercept the request of the surrender, through a cache, I say to the cache when I pass this parameter to the render method, or something .. I just wanted to keep control .. so I have a 'ViewCacheStorage', where you will store the files that are cached, and the remaining time to expiration of each cache, what is the best way to do this without me having to shake the main view?
One easy option:
class CachingView extends View {
protected $cacheStorage;
public function render($template, $path = null) {
if (! $this->getCacheStorage()->has($template)) {
$this->getCacheStorage()->store(parent::render($template, $path));
}
return $this->getCacheStorage()->get($template);
}
public function getCacheStorage() {
if (empty($this->cacheStorage)) {
$this->cacheStorage = new ViewCacheStorage();
}
return $this->cacheStorage;
}
}
And then all your other views extend from CachingView.