Optional get parameter (with slashes) in default / route - php

The main page on my website is an empty link, like:
www.randomlink.com/
That's the controller with the "/" route. The problem is that I have to use get parameters here, according to the following pattern:
key1/value1/key2/value2
I add these parameters on form submit, and the form redirects back to the main page.
The problem is that, as you can see, I get:
www.randomlink.com/key1/value1/key2/value2
And thus it opens key1 controller, instead of the default one.
/**
* Display dashboard
*
* #Route("/{path}",
* name="dashboard",
* defaults={"path" = "-1"},
* requirements={"path" = ".+"})
* #Template()
*/
public function displayAction($path, Request $request)
{
if($_POST)
{
// add get parameters to $path
return $this->redirect($this->generateUrl('dashboard', ['path' => $path]));
}
// do something
}
How can I solve this issue?

Probably your routing configuration order is not correct: see "Earlier Routes always Win" in the docs
Workaround: What about using query string like: www.randomlink.com/?path=key1/value1/key2/value2, then $request->query->get('path') ?

Related

Symfony routing with params with specific structure

Is it possible to declare a route in symfony like this
/somestuff/{query}
Where the structure of the query would be
string-with-minus-id000001
I would like to be able to get the first part as an attribute in controller and the second part as id. Defining the route as
/somestuff/{name}-id{id}
did not work
if the structure would always look like this
/somestuff/{name}-id{id}
The universal way to get the name and id would be
/**
* #Route("/somestuff/{slug}")
*/
public function someAction(Request $request, $slug)
{
$reversed = strrev($slug);
$paramArray = explode('-', $reversed, 1); // limit
$id = strrev($paramArray[0]);
$name = strrev($paramArray[1]);
// rest of code
}
I found a way to do it in annotation, here's the answer:
#Route(
"/somestuff/{name}-id{id}",
methods={"GET"} ,
name="route_name",
defaults={"name"=""},
requirements={"name"=".*?", "id"="\d+"}
)

Laravel route redirecting with data

I have a basic route that looks like this:
Route::prefix('/group')->group(function () {
// Some routes here
Route::prefix('/{uuid}')->group(function () {
// Some routes here
Route::get('/user/{id}', 'Controller#preview')->name('view-user')->where('id', '[0-9]+');
}
}
The logic is that I want the id to be only numerical value. What I want to do now is, to declare a redirection to this, if the value is non-numerical. Let's say the input of id is fs. In that case I would want it to redirect to id with value 1.
I tried using Route:redirect, but could not make it work. It looks something like this:
Route::redirect('/group/{uuid}/user/{id}', '/group/{uuid}/user/1')->where('id', '[^0-9]+');
I would prefer to put the redirect inside the groups, but it can be outside if this is the only way. Any help will be greatly appreciated.
What happens is, that I get a 404 error if I have the route redirect declared.
EDIT: I want to do it in the routes/web.php file. I know how to do it in the controller, but it is not what I need in the current case.
Closures are not an option either, because that would prevent routes caching.
Following up on the comment
You can create a Route in routes/web.php file that catches non-digit ids and redirect this to 'view-user' with id=1
It would look something like this
Route::get('/group/{uuid}/user/{id}', function ($uuid, $id) {
return redirect('view-user', ['uuid' => $uuid, 'id' => 1]);
})->where('id', '[^0-9]+');
// and then below have your normal route
Route::get('/group/{uuid}/user/{id}', 'Controller#preview')->name('view-user')->where('id', '[0-9]+');
Update
Following you comment that you do not want to use closures.
Change the "bad input route" to
Route::get('/group/{uuid}/user/{id}', 'Controller#redirectBadInput')->where('id', '[^0-9]+');
and then add the method in class Controller:
public function redirectBadInput ($uuid, $id) {
return redirect('view-user', ['uuid' => $uuid, 'id' => 1]);
}
You can see more in this SO thread about redirects and caching.
You declared it inverted.
In Laravel you can redirect passing parameters in this way:
You can pass the name instead of the url and simply pass variables.
Redirect::route('view-user', [$uuid, $id])
I think that you can do it inside of the controller of the router, with a logic like this:
class Controller {
// previous code ..
public function preview($uuid, $id) {
if(! is_numeric($id))
return redirect("/my-url/1");
// run the code below if $id is a numeric value..
// if not, return to some url with the id = 1
}
}
I think that there is no way to override the 'where' function of laravel, but I guess that have something like that in Routing Bindings:
Alternatively, you may override the resolveRouteBinding method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route:
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value)
{
return $this->where('name', $value)->first() ?? abort(404);
}
But it's require that you manage consise model's values instead of ids of whatever you want.
assign route name in route as like.
return Redirect::route('view-user', ['uuid'=>$uuid,'id'=>$id]);
As you want in web.php file then.
Route::get('/group/{uuid}/user/{id}', function($uuid, $id){
echo $uuid;
echo $id;
})->name('view-user')->where('id', '[0-9]+');

TYPO3: Keep forms filled after form submit

I created an extension which allows the user to sign up via the frontend. I couldn't use working ones because the client requested special tasks.
This is the code which detects taken usernames.
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
$uname = $newFeUserX->getUsername();
$select_query = '*';
$from_table = 'fe_users';
$where_clause = 'username="'.$uname.'"';
$test = $GLOBALS['TYPO3_DB']->exec_SELECTquery($select_query, $from_table, $where_clause);
if ($GLOBALS['TYPO3_DB']->sql_num_rows($test)) {
$this->addFlashMessage('Username is already taken.', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR);
$this->redirect('new');
} else {
// do stuff when the username isn't taken yet
}
}
But unfortunately and obivously, when redirecting back to the new action, the fields are empty again.
Is there a way to pass the arguments back to the new action and fill the forms?
Yes, and extbase has a standardized way to do this. It works as follows:
If an action is called, its parameters are validated, except if validation is switched off in the doc comments. If validation fails, the previous action (the one whose view contained the submitted form) is called again, with the same parameters.
You can use this as follows:
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #ignorevalidation $newFeUserX
*/
public function newAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX = null)
{
$this->view->assign('user', $newFeUserX);
// View renders form with name="newFeUserX" and object="{user}",
// action="create", fields use the property-attribute to fill
// in values and field names.
}
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #validate $newFeUserX \Vendor\Feregister\Validator\UsernameDoesNotExistValidator
*/
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
// Do something with the user - you can be sure the username
// is not yet taken
}
The class \Vendor\Feregister\Validator\UsernameDoesNotExistValidator is a custom validator that implements the ValidatorInterface, or extends AbstractValidator. It should basically do the validation you are doing in your createAction (maybe using a repository instead of $GLOBALS['TYPO3_DB']). A validator returns errors in a standard way, making it easier to show nice error messages and localize them.
If the validation fails, extbase will try to forward to the action that rendered the form, in this case the new-action. In this case, it will work, because of the #ignorevalidation annotation on the new-action.
In addition, information about validation errors are available in the view, you can render them using the ViewHelper f:form.validationResults.

Explanation of how symfony2 annotations work

I am using symfony2 annotations and want to know how cascading works in this format.
Lets say I have:
/**
* #Route("/reviews/{slug}", name="reviewDetail")
* #Template()
*/
first, then I check to see if that pulls any data. If not, I do a redirect to the following controller using the following redirect:
return $this->redirect($this->generateUrl('reviewsDate', array('date' => $slug)), 301);
which should go to:
/**
* #Route("/reviews/{date}", name="reviewsDate", defaults={"date" = null})
* #Template()
*/
then check to see if that pulls any data and, if not, create a fallback to this using a redirect:
/**
* #Route("/reviews", name="reviews")
* #Template()
*/
When I run a redirect:
if ($ctx->getReview($slug)) {
$review = $ctx->getReview($slug);
} else {
return $this->redirect($this->generateUrl('reviewsDate', array('date' => $slug)), 301);
}
I get this error:
This webpage has a redirect loop
The actions are all stacked in the order of acceptance, so I would check for the slug first, then the date, then if no result, kick it to the main reviews page.
I can change the route to be more specific, which would work, but it seems not as user friendly. For instance, if I wanted to have these multiple routes:
reviews/my-review: shows the specific review
reviews/2014: shows all reviews from the 2014 year
Is this the wrong way of of executing this functionality?
/**
* #Route("/reviews/{date}", name="reviewsDate", defaults={"date" = null})
* #Template()
*/
this is this same route as
/**
* #Route("/reviews", name="reviews")
* #Template()
*/
becouse you have default value null for date parameter so if you redirect to reviews you are going to reviewDate without parameter that causes endless loop.

stop field to being update in cakephp

In my cake PHP application, I have a edit form where "email" field is readonly that means user can not update it.
NOw if I think according to security point of view, user can update the field by 'firebug' or some other browser plugins.
I am using $this->User->save($this->data) to save the updated data. By this function Email can be also be updated.
Do we have any way in cake php so that I can prevent this field to be update, like by passing here a argument or something like this?
You can simply remove the email field from $this->data:
unset($this->data['User']['email']);
$this->User->save($this->data);
You could do something like:
$dontUpdateField = array('email');
$this->Model->save(
$this->data,
true,
array_diff(array_keys($this->Model->schema()),$dontUpdateField)
);
If security is a concern, simply reject any data that has unexpected values. In cake you could do this, but it can be adapted for any framework/cms
/**
* Checks input array against array of expected values.
*
* Checks single dimension input array against array of expected values.
* For best results put this is in app_controller.
*
* #param array $data - 1 dimensional array of values received from untrusted source
* #param array $expected - list of expected fields
* #return boolean - true if all fields are expected, false if any field is unexpected.
*/
protected function _checkInput($data,$expected){
foreach(array_keys($data) as $key){
if (!in_array($key,$expected)){
return;
}
}
return true;
}
/**
* edit method.
*
* put this in <Model>_controller
* #param string $id
* #return void
* #todo create errors controller to handle incorrect requests
* #todo configure htaccess and Config/routes.php to redirect errors to errors controller
* #todo setup log functionality to record hack attempts
* #todo populate $expected with fields relevant to current model
*/
function edit($id=null){
$expected = ('expectedVal1', 'expectedVal2');
$this->Model->id = $id;
if (!$this->Model->exists()) {
throw new NotFoundException(__('Invalid model'));
}
if ($this->request->is('post')) {
if (!$this->_checkData($this->request->data['Model'], $expected)) {
//log the ip address and time
//redirect to somewhere safe
$this->redirect(array('controller'=>'errors','action'=>'view', 405);
}
if ($this->Model->save($this->request->data)) {
//do post save routines
//redirect as necessary
}
else {
$this->Session->setFlash(__('The model could not be saved. Please, try again.'));
}
}
$this->set('model',$this->Model->read($expected,$id));
}
You can use the security component and make the email hidden. While using this component, hidden fields cant be changed or cake will blackhole the form.
http://book.cakephp.org/1.3/en/view/1296/Security-Component
If your application is public it is strongly recommended that you use security, otherwise it is kinda trivial to inject data in your models by submitting extra fields on the form and when you do $this->Model->save($this->data)) the extra fields are saved, unless you do the extra work of validating every field of $this->data;

Categories