I just upgraded to Laravel 4.1 and can no longer use a function I was using in the past. I wrote a function to redirect an incoming request to another route, get the result, and replace the current route with the original incoming route. I used this on my frontend controllers to consume my own API which is defined in the same application.
Here is the function:
public static function redirectRequest($newRoute, $verb, $args = null)
{
// store the original request data and route
$originalInput = Request::input();
$originalRoute = Route::current();
$request = $args === null ? Request::create($newRoute, $verb) : Request::create($newRoute, $verb, $args);
// replace the request input for the new route...
Request::replace($request->input());
try
{
$response = Route::dispatch($request);
return $response;
}
catch (\Exception $e)
{
throw $e;
}
finally
{
// replace the request input and route back to the original state
Request::replace($originalInput);
Route::setCurrentRoute($originalRoute);
}
}
And I would use it like:
Helpers::redirectRequest('/api/v1/someroute', 'GET');
The problem is that, when I try to return things to the way they were before the redirect, I can't. setCurrentRoute has been removed from 4.1 and I can't figure out how to reset the current route.
One thing that I have done in the past is used the actual routes.php to handle that. If you register a "catchall" route as:
Route::get('api/v1', 'ApiController#route');
As long as this is after all other routes (Laravel uses the first matching route) you can then handle that within your ApiController as
public function route($uri) {
// Handle your API route using the $uri variable
}
This may not be the solution that you are looking for, but I have found it very convenient.
Related
Im currently using Lumen 8. Im having issues with routing. The controllers work fine for get methods without parameters but I get a "Bad Routes exception" when I pass parameters
$router->get('/accounts', 'AccountsController#index');
$router->get('/accounts/{id:[\d]+}', 'AccountsController#show');
For example for the first route, localhost/accounts it displays the required data. The second route however when localhost:8002/accounts/1 it returns the error.
The routes are defined in an AccountsController
public function index(){
$data = $this->collection(Account::all(), new AccountsTransformer());
return response()->json($data);
}
public function show($id){
try{
return $this->item(Account::query()->findOrFail($id), new AccountsTransformer());
} catch (ModelNotFoundException $e){
response()->json(['error'=>['message'=>'Account not found']]);
}
}
I'm trying to do some very basic key validation for my API, simply checking a provided key attribute against a key stored in the database.
I've got a middleware class that i'm adding to the slim object using
$app->add(new auth());
where auth.php is my class.
I have it working and i'd like to split the code in to functions however I can't seem to call functions from within __invoke without getting a Slim Application Error.
Right now I have
public function __invoke($request, $response, $next){
try {
$user_key = $request->getAttribute('route')->getArguments()['key'];
...
//fetch key from database code
...
if($user_key==$api_key){
$response = $next($request, $response);
}
else{
$response->getBody()->write("Invalid API Key");
}
}
catch (PDOException $e) {
$response->getBody()->write("An error has occurred");
}
return $response;
}
Which works, but i'd like to move the fetch code in to its own function e.g
public function fetchKey(){
...
//return key
...
}
and call $api_key = fetchKey(); however this causes a Slim error.
This is probably really simple but does anyone know what causes this?
I have a question : so for examples I have an app in symfony3 which have the following routes : /admin/login,admin/news,admin/gallery, but the route /admin/authentification doesn't exist. So the idea is if the route doesn't exist I want to redirect the user to homepage /. Can you help me please ? Thanks in advance and sorry for my english
I'm not confident this is the best solution, but you can use a UrlMatcher to check that the URL you're passing correlates to an available route:
/**
* #Route("/debug")
*/
public function DebugAction(){
$router = $this->get('router');
//Get all the routes that exist.
$routes = $router->getRouteCollection();
$context = $router->getContext();
$urlMatcher = new UrlMatcher($routes, $context);
$url = '/admin/login';
try{
//UrlMatcher::match() will throw a ResourceNotFoundException if the route
//doesn't exist.
$urlMatcher->match($url);
return $this->redirect($url);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e){
return $this->redirect('/');
}
}
I'm not particularly keen on this solution because it relies on catching an exception, rather than checking a boolean value to determine if the route exists.
You can check if the rout exist.
function routeExists($name)
{
// I assume that you have a link to the container in your twig extension class
$router = $this->container->get('router');
return (null === $router->getRouteCollection()->get($name)) ? false : true;
}
And depending on result, do the redirect to the rout, or to the default webpage, or whatever you need.
I want to redirect admins to /admin and members to /member when users are identified but get to the home page (/).
The controller looks like this :
public function indexAction()
{
if ($this->get('security.context')->isGranted('ROLE_ADMIN'))
{
return new RedirectResponse($this->generateUrl('app_admin_homepage'));
}
else if ($this->get('security.context')->isGranted('ROLE_USER'))
{
return new RedirectResponse($this->generateUrl('app_member_homepage'));
}
return $this->forward('AppHomeBundle:Default:home');
}
If my users are logged in, it works well, no problem. But if they are not, my i18n switch makes me get a nice exception :
The merge filter only works with arrays or hashes in
"AppHomeBundle:Default:home.html.twig".
Line that crashes :
{{ path(app.request.get('_route'), app.request.get('_route_params')|merge({'_locale': 'fr'})) }}
If I look at the app.request.get('_route_params'), it is empty, as well as app.request.get('_route').
Of course, I can solve my problem by replacing return $this->forward('AppHomeBundle:Default:home'); by return $this->homeAction();, but I don't get the point.
Are the internal requests overwritting the user request?
Note: I'm using Symfony version 2.2.1 - app/dev/debug
Edit
Looking at the Symfony's source code, when using forward, a subrequest is created and we are not in the same scope anymore.
/**
* Forwards the request to another controller.
*
* #param string $controller The controller name (a string like BlogBundle:Post:index)
* #param array $path An array of path parameters
* #param array $query An array of query parameters
*
* #return Response A Response instance
*/
public function forward($controller, array $path = array(), array $query = array())
{
$path['_controller'] = $controller;
$subRequest = $this->container->get('request')->duplicate($query, null, $path);
return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
By looking at the Symfony2's scopes documentation, they tell about why request is a scope itself and how to deal with it. But they don't tell about why sub-requests are created when forwarding.
Some more googling put me on the event listeners, where I learnt that the subrequests can be handled (details). Ok, for the sub-request type, but this still does not explain why user request is just removed.
My question becomes :
Why user request is removed and not copied when forwarding?
So, controller actions are separated part of logic. This functions doesn't know anything about each other. My answer is - single action handle kind of specific request (e.g. with specific uri prarams).
From SF2 docs (http://symfony.com/doc/current/book/controller.html#requests-controller-response-lifecycle):
2 The Router reads information from the request (e.g. the URI), finds a
route that matches that information, and reads the _controller
parameter from the route;
3 The controller from the matched route is
executed and the code inside the controller creates and returns a
Response object;
If your request is for path / and you wanna inside action (lets say indexAction()) handling this route, execute another controller action (e.g. fancyAction()) you should prepare fancyAction() for that. I mean about using (e.g.):
public function fancyAction($name, $color)
{
// ... create and return a Response object
}
instead:
public function fancyAction()
{
$name = $this->getRequest()->get('name');
$color = $this->getRequest()->get('color');
// ... create and return a Response object
}
Example from sf2 dosc:
public function indexAction($name)
{
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
'name' => $name,
'color' => 'green',
));
// ... further modify the response or return it directly
return $response;
}
Please notice further modify the response.
If you really need request object, you can try:
public function indexAction()
{
// prepare $request for fancyAction
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array('request' => $request));
// ... further modify the response or return it directly
return $response;
}
public function fancyAction(Request $request)
{
// use $request
}
I've literally downloaded Laravel today and like the looks of things but i'm struggeling on 2 things.
1) I like the controllers' actions method of analysing urls instead of using routes, it seems to keep everything together more cleanly, but lets say I want to go to
/account/account-year/
how can I write an action function for this? i.e.
function action_account-year()...
is obviously not valid syntax.
2) If i had
function action_account_year( $year, $month ) { ...
and visited
/account/account_year/
An error would be displayed about missing arguments, how do you go about making this user friendly/load diff page/display an error??
You would have to manually route the hyphenated version, e.g.
Route::get('account/account-year', 'account#account_year');
Regarding the parameters, it depends on how you are routing. You must accept the parameters in the route. If you are using full controller routing (e.g. Route::controller('account')) then the method will be passed parameters automatically.
If you are manually routing, you have to capture the params,
Route::get('account/account-year/(:num)/(:num)', 'account#account_year');
So visiting /account/account-year/1/2 would do ->account_year(1, 2)
Hope this helps.
You can think of the following possibility as well
class AccountController extends BaseController {
public function getIndex()
{
//
}
public function getAccountYear()
{
//
}
}
Now simply define a RESTful controller in your routes file in the following manner
Route::controller('account', 'AccountController');
Visiting 'account/account-year' will automatically route to the action getAccountYear
I thought I'd add this as an answer in case anyone else is looking for it:
1)
public function action_account_year($name = false, $place = false ) {
if( ... ) {
return View::make('page.error' );
}
}
2)
not a solid solutions yet:
laravel/routing/controller.php, method "response"
public function response($method, $parameters = array())
{
// The developer may mark the controller as being "RESTful" which
// indicates that the controller actions are prefixed with the
// HTTP verb they respond to rather than the word "action".
$method = preg_replace( "#\-+#", "_", $method );
if ($this->restful)
{
$action = strtolower(Request::method()).'_'.$method;
}
else
{
$action = "action_{$method}";
}
$response = call_user_func_array(array($this, $action), $parameters);
// If the controller has specified a layout view the response
// returned by the controller method will be bound to that
// view and the layout will be considered the response.
if (is_null($response) and ! is_null($this->layout))
{
$response = $this->layout;
}
return $response;
}