How to forward with GET parameters?
I have two actions in same controller. I do some magic in the first action, and if all goes well, I want it to forward to the other one, but with GET parameters created in the first one.
What I have now is this:
return $this->forward('AppBundle:default:tracked', array(), array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
Empty array in there because of a desperate effort to get it to work. Documentation tells that second array is for the query parameters, but nothing really happens, so I must be doing something wrong.
The second action tries to get the parameters like this:
/**
* #Route("/tracked", name="tracked")
*/
public function trackedAction()
{
$request = Request::createFromGlobals();
// If there is the required variables in the GET:
if($request->query->has('tracking_code') && $request->query->has('company'))
But no, variables never get there it seems.
Reason I have this kind of setup, is that user can get into the trackedAction from another place as well.
I think the proper way to get your parameters in your second action would be to do like this :
return $this->forward('AppBundle:default:tracked', array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
And your second action would be :
/**
* #Route("/tracked/{tracking_code}/{company}", name="tracked")
*/
public function trackedAction($tracking_code=null, $company=null)
{
...
}
I used the $tracking_code = null because you specified this could be accessed from another place, that maybe does not provide these parameters. This way it works for both of them.
Hope this helps.
The way that Symfony controller forwarding works is by duplicating the current the Request with the options that you pass and then re-dispatching it through the HttpKernel component. You can see this in the code. Because it's a sub-request, your second action is creating a Request from globals (i.e. $_GET etc.) which haven't changed.
The solution is to change your second action method signature to:
public function trackedAction(Request $request)
This means that the $request variable will be "local" to your action and will contain the variables you want.
In practice you should always pass the Request in to your actions this way as it makes your controllers a lot more testable and prevents strange issues like this.
Alternatively to all of the above, you could use a redirect instead which would do an HTTP redirect rather than just forwarding within the Symfony request system.
Related
I have a bit of a complicated issue. I could use some help.
I have a form that is being handled by the following function:
$module = request('module');
$classe = request('classe');
$horaire = request('horaire');
$date = request('date');
$students = DB::select('SELECT * FROM `etudiants` WHERE etudiants.id_classe = '.$classe);
return view('g_absence.absence',['module'=> $module, 'classe'=>$classe,'horaire'=>$horaire,'date'=>$date,'students'=>$students]);
I take the values $module, $class, $horaire, $date and $students and need to use them inside a different view: g_absence.absence. This works fine and when the view is returned I have access to said variables.
The issue is, inside the g_absence.absence view, I have another form that also needs to be handled, and because the url remains the same even tho a different view is returned, I cant make two posts for the same path.
web.php:
Route::get('/testboy', [App\Http\Controllers\g_absence::class,'index'])->name('marquer');
Route::post('/testboy',[App\Http\Controllers\g_absence::class, 'marquer']);
Route::post('/testboy',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
The first line is the one that send to the form page just a simple
return view
The second one handle the form in that view
The third one, I want it to handle the form inside the
g_absence.absence view, but they share the same path.
Excuse me if I'm being unclear, I'm a bit of a beginner in Laravel
your problem is using the same route for different methods
basically the first route gets executed every time you use the '/testboy' action that is why your second function never get's called.
you can solve this issue by changing your urls for example:
Route::post('/testboy-marquer',[App\Http\Controllers\g_absence::class, 'marquer']);
Route::post('/testboy-ajoutabsence',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
Or you can use one function that's handle both with one url by pathing additional parameter to your url depending on your function call :
Route::post('/testboy?type=marquer',[App\Http\Controllers\g_absence::class, 'ajoutabsence']);
in your function check the type :
if(request('type') == 'marquer') {
execute marquer logic here...
} else {
execute absence logic here...
}
Using method and path with two functionalities is wrong, but if you want to somehow use both routes my same method and path which I don't recommend you must let the request to pass through like a middleware in your first block of code Instead of return a view.
Recommended way is to have 2 routes with different paths or at least one route with a parameter by which you can determine what code block must be executed.
So I have a POST URL with two parameters and I want to assign default values for both parameters .
I know you can implement this way for a URL with a single param:
Route::post('activity-log/datatable/{tag_access?}/{page_access?}',
'SettingsController#datatable_activity_log')
->defaults('tag_access', 'activity-log');
But how do i go about it with a URL that looks like this:
Route::post('activity-log/datatable/{tag_access?}/{page_access?}',
'SettingsController#datatable_activity_log')
You can achieve it by following way:
Keep your route as you want like this:
Route::post('activity-log/datatable/{tag_access?}/{page_access?}','SettingsController#datatable_activity_log')
Now, In controller function you can take these parameters with default value like this,
public function datatable_activity_log($tag_access='activity-log', $page_access='activity-log', Request $request){
// Here write your logic
}
This may not be the best way to achieve what you want but this is one of the way.
From what I see regarding the usage of defaults you can either do one at a time:
Route::post('activity-log/datatable/{tag_access?}/{page_access?}',
'SettingsController#datatable_activity_log')
->defaults('tag_access', 'activity-log')
->defaults('page_access', 'defaultValue');
An alternative (since defaults is public) is to do:
$route = Route::post('activity-log/datatable/{tag_access?}/{page_access?}',
'SettingsController#datatable_activity_log');
$route->defaults = [ 'tag_access' => 'activity-log', 'page_access' => 'defaultValue' ];
My personal favourite is what #Sagar Gautam suggest which is to use default function parameters.
I have a problem with Laravel 5, and to be precise, I can't find the solution for it.
In C# (ASP.NET MVC) it's easy to solve.
For example, I have these routes (I'll just type the route content, and the function header, for the sake of simplicity)
/{category}/Page{page}
/Page{page}
/{category}
The function is defined inside Product controller.
function header looks like this:
public function list($page = 1, $category = null)
the problem is, whenever I enter just one argument, it doesn't send the value for the parameter by the name I set in the route, but rather, it pushes values by function parameter order.
So, when I open /Page1, it works properly, value of 1 is sent to $page variable,
but when I access /Golf(made up on the spot), it also sends the value to the $page variable.
Any possible idea how to avoid this, or do I really need to make different functions to handle these cases?
In C#, it properly sends the value, and keeps the default value for undefined parameter.
Hope you have an answer for me.
Thank you in advance and have a nice day :)
So, as you've seen the parameters are passed to the function in order, not by name.
To achieve what you want, you can access these route parameters from within your function by type hinting the request object to it like this:
class ProductController extends Controller
{
function list(Request $request){ # <----------- don't pass the params, just the request object
$page = $request->route('page'); # <--- Then access by name
$category = $request->route('category');
dd("Page: $page | Category: $category");
}
}
Then of course you would set all 3 of your routes to hit that same controller method:
Route::get('/{category}/Page{page}', 'ProductController#list');
Route::get('/Page{page}', 'ProductController#list');
Route::get('/{category}', 'ProductController#list');
Hope this helps..!
if you want to get the parameters in your controller, you can use this:
public function list() {
$params = $this->getRouter()->getCurrentRoute()->parameters();
}
for /aaa/Page3, the $params would be array(category => 'aaa', page => '3')
for /Page3, the $params would be array(page => '3')
for /aaa, the $params would be array(category => 'aaa')
I am trying to figure out how to access two (or more) parameters passed to a Laravel controller. I know how to create the route, and the URL is created correctly, but then I can only access the first passed parameter in my controller.
Route:
Route::get('managers/{id}/{parameter2}', array('as'=>'dosomething', 'uses'=> 'ManagersController#dosomething'));
where the first parameter is obviously the $id for managers, and the second parameters is to be processed by the controller.
View:
Do Something
generates the URL:
http://domain/managers/1/2
where 1 is easily accessed as the $id for managers, but when I try to access the 2nd parameter "2" using $parameter2, e.g. using a simple return: "id=$id and parameter2=$parameter2" statement, I get an "unidentified variable: $parameter2" error.
What am I doing wrong?
Is there a better way to pass multiple parameters? I'm especially asking the "better way?" question because what I want to do is use the 2nd parameter to change a value in a database table, and using a 'get' method, somebody could change the parameter value in the URL and therefore cause mischief. Must I use a 'post' method? I'd love to be able to use a link, since that works much better with the design of my application.
Thanks!
I was asked to include the controller, which I'm happy to do. Initially, just for testing, as I mentioned, my controller was a simple return to display the values of the two passed parameters. But here is what I want to be able to do, including the actual name of the function ("update_group" rather than "dosomething") --
ManagersController:
public function update_group($id)
{
DB::table('groups')->where('id','=',$parameter2)->update(array('manager_id'=>$id));
return Redirect::route('managers.show', array('id'=>$id));
}
The update table works perfectly if I replace $parameter2 with an actual value, so that syntax is fine. The issue is that Laravel says that $parameter2 is an undefined variable, despite the fact that the URL contains the value of $parameter2 as you can see above.
And since it occurs to me that the answer to this may involve adding a function to the Manager model, here is the current
Manager.php
class Manager extends Eloquent {
protected $table = 'managers'; ... (mutator and error functions)
}
Just change
public function update_group($id)
to
public function update_group($id, $parameter2)
All looks ok in your route. Seeing the controller code would help, but likely, you may not have a second parameter in your controller's dosomething() method.
public function dosomething($id, $parameter2){
var_dump($id).'<br />';
var_dump($paremter2);
}
If that isn't the case, you can try dumping it from the route's callback to further diagnose.
Route::get('managers/{id}/{parameter2}', function($id, $parameter2)
{
var_dump($id).'<br />';
var_dump($paremter2);
});
Depending on your use case, you can pass them in a query string like so: but it isn't really the 'best way', unless you're doing something like building an API that won't use the same variables in the same order all the time.
/managers?id=1¶mter2=secondParameter
var_dump(Request::query('id')).'<br />';
var_dump(Request::query('paramter2'));
Zend Framework 1 had a very simple way of parsing URL routes and setting found params in the $_GET superglobal for easy access. Sure, you could use ->getParam($something) inside the controller, but if the param was found in the URL, it was also accessible via $_GET.
Example for url mypage.com/mymodule/mycontroller/myaction/someparam/5:
ZF1
$this->getParam('someparam'); // 5
$_GET['someparam']; // 5
ZF2
$this->getEvent()->getRouteMatch()->getParam('someparam'); // 5
$_GET['someparam'] // undefined index someparam
Obviously, the difference is that ZF2 does NOT put the route params into the $_GET superglobal.
How do I make it put the parsed parameters into the $_GET superglobal, since extending the controller and just defining a constructor that does that is out of the question (because RouteMatch is not an object yet and cannot be called from the controller's constructor)?
Calling $_GET = $this->getEvent()->getRouteMatch()->getParam('someparam'); in every one of my controllers would work, but I don't want that.
In other words, following the example URL from above, I want to be able to do $_GET['someparam'] and still get the value "5" in any component in the application.
Edit: Looks like I wasn't clear enough, so I'll try to clarify some more. I want whatever param I enter in the URL via /key/value formation to be available in $_GET instantly. I don't really have a problem with getting the param, I know how to get it and I extended Zend's controller so I can just call $this->getParams again like in ZF1, and now all controllers extend that one, I just want the params from the URL to automatically be in $_GET as well, so I can access them easily in third party components which use $_GET natively.
Edit 2: Updated as reaction to Samuel Herzog's answer:
I don't really mind invalidating the SRP in this case, because the libraries are built in such a way that they need direct access to $_GET - they do their own filtering and directly depend on this superglobal. They also directly fetch $_FILES and $_POST for processing, it's just the way their code works.
I've made the following method in the abstract controller:
$this->mergeGet(); which basically makes $_GET absorb all the route matched params and everything works as intended, but seeing as the libraries will be required in every controller/action, it might get tedious to call that method every time. If only the controller had an init() method like in ZF1...
In ZF2, Im using this
$getparams = $this->getRequest()->getQuery();
First of all, you shouldn't use $_GET or any other superglobal directly if you're building on an object oriented stack. The SRP is invalidated this way.
If you have no possibility to change the way of your (3rd party?) librarys to change you might want to hook into the MvcEvent, listen to --event-- and then get the RouteMatch, you may fill $_GET with a simple loop.
For a most-performant answer, you should know if the named library will be needed for every action, just for one module, or only in certain controllers/actions.
If the latest is your use-case, you should write a controller plugin instead.
some example code for the first approach:
namespace YourModule;
use Zend\EventManager\EventInterface as Event;
use Zend\Mvc\MvcEvent;
class Module
{
...
public function onBootstrap(Event $ev)
{
$application = $e->getApplication();
$eventManager = $application->getEventManager();
$eventManager->attach('route', function(MvcEvent $mvcEvent) {
$params = $mvcEvent->getRouteMatch()->getParams();
foreach ( $params as $name => $value )
{
if ( ! isset($_GET[$name])
{
$_GET[$name] = $value;
}
}
});
}
}
You could use in your controlller:
$paramValue = $this->params()->fromQuery('your_param_here');
Regards