Calling unnamed controller and method in a router - php

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

Related

Can't call parent function inside __call() in Laravel

This is my code below.
class MyModel extends Model
{
public function __call($method, $parameters = null) {
if($method == 'create'){
return parent::create($parameters[0]);
if(!$created) {
throw new \App\Exceptions\EloquentException;
}else{
return $created;
}
}
}
}
The problem is that when I call update function of MyModel class instance from postman, something bad happens. It gets stuck and I have to restart my computer each time. So what may the problem be?
I'll try to assume you're simply trying to have a common handler for the create function, that is, to throw an EloquentException in case the create returns a null or false.
If that's the case, you have an excess return statement on line above the if statement, and you should assign the return value of the parent's create method to a variable $created that you use later. You may also remove the else part as code below throw is never going to be executed if the exception is thrown.
class MyModel extends Model
{
public function __call($method, $parameters = null)
{
if ($method == 'create') {
$create = parent::create($parameters[0]);
if (!$created) {
throw new \App\Exceptions\EloquentException;
}
return $created;
}
}
}
It would be better if you could elaborate on the task you're trying to achieve, I feel you're doing it the wrong way.

How to call a method using array without object?

How can I call a method like in laravel or slim route?
Let's say I have class like this:
namespace App;
class App
{
public function getApp(){
return "App";
}
}
and I want to call on this way
$route->get('App\App','getApp');
How can I do this?
The simplest way
call_user_func_array(['App\App', 'getApp'], $params_if_needed);
php.net source call_user_func_array()
If you need to check if method exists, just use
method_exists('SomeClass','someMethod') // returns boolean
php.net method_exists()
So you Router class may be next:
class Router
{
public function get($class, $method)
{
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
throw new SomeCustomNotFoundException();
}
if (!method_exists($class, $method)) {
throw new SomeCustomNoMethodFoundException();
}
call_user_func_array([$class, $method], $_REQUEST); //with params
// OR
call_user_func([$class, $method]); //without params, not sure
}
}
If you want to do in the more clever way, you can use Reflection, it will give you information about class/method existence and also give information about method params, and which of them are required or optional.
UPDATE: This example expects methods to be static. For non-static you can add check, in the Router class, for class existence (class_exists($class)) and do smth like this
$obj = new $class();
$obj->$method(); //For methods without params
UPDATE(2) to check this out go here and paste next code
<?php
class Router
{
public function get($class, $method)
{
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
throw new SomeCustomNotFoundException();
}
if(!class_exists($class)) {
throw new ClassNotFoundException();
}
if (!method_exists($class, $method)) {
throw new SomeCustomNoMethodFoundException();
}
call_user_func_array([$class, $method], $_REQUEST); //with params
// OR
//call_user_func([$class, $method]); //without params, not sure
}
}
class Test
{
public static function hello()
{
die("Hello World");
}
}
$route = new Router();
$route->get('Test', 'hello');

How to design an interceptor in PHP CodeIgniter like in Java structs?

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...

Auto set view in Laravel

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 ...
}

Get returned of controller action in my own abstact library (Zend Framework)

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
}

Categories