Laravel 5 how to validate route parameters? - laravel

I want to validate the route parameters in the "form request" but don't know how to do it.
Below is the code sample, I am trying with:
Route
// controller Server
Route::group(['prefix' => 'server'], function(){
Route::get('checkToken/{token}',['as'=>'checkKey','uses'=> 'ServerController#checkToken']);
});
Controller
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests;
class ServerController extends Controller {
public function checkToken( \App\Http\Requests\CheckTokenServerRequest $request) // OT: - why I have to set full path to work??
{
$token = Token::where('token', '=', $request->token)->first();
$dt = new DateTime;
$token->executed_at = $dt->format('m-d-y H:i:s');
$token->save();
return response()->json(json_decode($token->json),200);
}
}
CheckTokenServerRequest
namespace App\Http\Requests;
use App\Http\Requests\Request;
class CheckTokenServerRequest extends Request {
//autorization
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
}
}
But when I try to validate a simple url http://myurl/server/checkToken/222, I am getting the response: no " token " parameter set.
Is it possible to validate the parameters in a separate "Form request", Or I have to do all in a controller?
ps. Sorry for my bad English.

For Laravel < 5.5:
The way for this is overriding all() method for CheckTokenServerRequest like so:
public function all()
{
$data = parent::all();
$data['token'] = $this->route('token');
return $data;
}
EDIT
For Laravel >= 5.5:
Above solution works in Laravel < 5.5. If you want to use it in Laravel 5.5 or above, you should use:
public function all($keys = null)
{
$data = parent::all($keys);
$data['token'] = $this->route('token');
return $data;
}
instead.

Override the all() function on the Request object to automatically apply validation rules to the URL parameters
class SetEmailRequest
{
public function rules()
{
return [
'email' => 'required|email|max:40',
'id' => 'required|integer', // << url parameter
];
}
public function all()
{
$data = parent::all();
$data['id'] = $this->route('id');
return $data;
}
public function authorize()
{
return true;
}
}
Access the data normally from the controller like this, after injecting the request:
$setEmailRequest->email // request data
$setEmailRequest->id, // url data

If you dont want to specify each route param and just put all route params you can override like this:
Laravel < 5.5:
public function all()
{
return array_merge(parent::all(), $this->route()->parameters());
}
Laravel 5.5 or above:
public function all($keys = null)
{
// Add route parameters to validation data
return array_merge(parent::all(), $this->route()->parameters());
}

The form request validators are used for validating HTML form data that are sent to server via POST method. It is better that you do not use them for validating route parameters. route parameters are mostly used for retrieving data from data base so in order to ensure that your token route parameter is correct change this line of your code, from
$token = Token::where('token', '=', $request->token)->first();
to
$token = Token::where('token', '=', $request->input(token))->firstOrFail();
firstOrFail() is a very good function, it sends 404 to your user, if the user insert any invalid token.
you get no " token " parameter set because Laravel assumes that your "token" parameter is a POST data which in your case it is not.
if you insist on validating your "token" parameter, by form request validators you gonna slow down your application, because you perform two queries to your db,
one in here
$token = Token::where('token', '=', $request->token)->first();
and one in here
return [
'token' => ['required','exists:Tokens,token,executed_at,null']
];
I suggest to use firsOrFail to do both validating and retrieving at once.

A trait can cause this validation to be relatively automagic.
Trait
<?php
namespace App\Http\Requests;
/**
* Class RouteParameterValidation
* #package App\Http\Requests
*/
trait RouteParameterValidation{
/**
* #var bool
*/
private $captured_route_vars = false;
/**
* #return mixed
*/
public function all(){
return $this->capture_route_vars(parent::all());
}
/**
* #param $inputs
*
* #return mixed
*/
private function capture_route_vars($inputs){
if($this->captured_route_vars){
return $inputs;
}
$inputs += $this->route()->parameters();
$inputs = self::numbers($inputs);
$this->replace($inputs);
$this->captured_route_vars = true;
return $inputs;
}
/**
* #param $inputs
*
* #return mixed
*/
private static function numbers($inputs){
foreach($inputs as $k => $input){
if(is_numeric($input) and !is_infinite($inputs[$k] * 1)){
$inputs[$k] *= 1;
}
}
return $inputs;
}
}
Usage
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyCustomRequest extends FormRequest{
use RouteParameterValidation;
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize(){
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules(){
return [
//
'any_route_param' => 'required'//any rule(s) or custom rule(s)
];
}
}

For \App\Http\Requests\CheckTokenServerRequest you can add use App\Http\Requests\CheckTokenServerRequest; at the top.
If you pass the token by url you can use it likes a variable in controller.
public function checkToken($token) //same with the name in url
{
$_token = Token::where('token', '=', $token)->first();
$dt = new DateTime;
$_token->executed_at = $dt->format('m-d-y H:i:s');
$_token->save();
return response()->json(json_decode($token->json),200);
}

$request->merge(['id' => $id]);
...
$this->validate($request, $rules);
or
$request->merge(['param' => $this->route('param')]);
...
$this->validate($request, $rules);

You just missing the underscore before token. Replace with
_token
wherever you check it against the form generated by laravel.
public function rules()
{
return [
'_token' => ['required','exists:Tokens,token,executed_at,null']
];

FormRequest has a method validationData() that defines what data to use for validation. So just override that one with route parameters in your form request class:
/**
* Use route parameters for validation
* #return array
*/
protected function validationData()
{
return $this->route()->parameters();
}

or leave most of the all logic in place and override input method from trait \Illuminate\Http\Concerns\InteractsWithInput
/**
* Retrieve an input item from the request.
*
* #param string|null $key
* #param string|array|null $default
* #return string|array|null
*/
public function input($key = null, $default = null)
{
return data_get(
$this->getInputSource()->all() + $this->query->all() + $this->route()->parameters(), $key, $default
);
}

Related

How to set a maximum per page in laravel dynamically?

I have a Products class that looks for these paginated items, but in the front end I allow the user to define how many items he wants to display per page (10, 30, 50, 100) the problem is that if someone passes 1000, the api returns 1000 records per page.
How can I validate this for all controllers and models dynamically?
I could do this "easily" by validating each request ('limit') on each controller, but it would not be practical, how can I do that?
public function index(Request $request)
{
$perPage = $request->input('limit'); // User input
$sort = 'global_performance';
$descending = 'desc';
$products = Product::where('status', 1)
->orderBy($sort, $descending)
->paginate($perPage); //
return $products;
}
You can validate the limit like this:
public function index(Request $request)
{
$this->validate($request, [
'limit' => ['required', 'integer', Rule::in([10, 30, 50, 100])]
]);
$perPage = $request->input('limit'); // User input
$sort = 'global_performance';
$descending = 'desc';
$products = Product::where('status', 1)
->orderBy($sort, $descending)
->paginate($perPage); //
return $products;
}
Now, add following line in just before controller class:
use Illuminate\Validation\Rule;
Update
More dynamic way might be creating custom request class like this:
Run following command to create a new form request class:
php artisan make:request PaginateRequest
This will create PaginateRequest class at App\Http\Requests directory like this:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PaginateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
Now change this class into following:
<?php
namespace App\Http\Requests;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
class PaginateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'limit' => ['required', 'integer', Rule::in([10, 30, 50, 100])]
];
}
}
After this, you can use in controller function by adding it as function parameter.
public function index(PaginateRequest $request)
{
$perPage = $request->input('limit'); // User input
$sort = 'global_performance';
$descending = 'desc';
$products = Product::where('status', 1)
->orderBy($sort, $descending)
->paginate($perPage); //
return $products;
}
Please don't forget to import it just before controller class like this:
use App\Http\Requests\PaginateRequest;
In this way, You can use this request class everywhere you need.
You can see more at documentation here: https://laravel.com/docs/5.8/validation
You could easily create a middleware. Apply it simply from kernel to each and every route or make a group in your route file to apply it to selective routes.
Inside the middleware just check the limit, if empty or more than the max limit you want let's say 100, make it 100 like so:
$limit = $request->input('limit');
if (empty($limit) || ($limit > 100)) {
$request['limit'] = 100;
}
wouldn't that work?
Here is link for middlewares in Laravel.

How to validate images array type using rule object with custom message in Laravel

Actually, I tried to create rule object which is able to validate every image type in array of images and not only enough but also, I must to show custom message in override message function in rule object.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class ImagesArray implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return [$attribute => 'mimes:jpeg,jpg,png' ];
here i need to validate these file types.
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
here, I need to show my custom messgae.
}
}
You should use Request.
For example, create q request class: php artisan make:request MyRequest.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'image' => 'mimes:jpeg,jpg,png',
];
}
public function messages()
{
return [
'image.mimes' => 'This image is not supported.',
];
}
}
In your controller import class MyRequest and in the method use MyRequest
e.g:
public function store(MyRequest $request)
{ // your code
}
Let me know if that was helpful. Thanks!
When validating arrays or nested parameters, you should use . in your rules access a specific array index. but if you want to apply a rule to every index on that array, you can use .*.
$validator = Validator::make($request->all(), [
'image.*' => 'mimes:jpeg,jpg,png',
], [
'image.*' => 'Invalid file type.',
]);
Or if you're using Request Forms
public function rules(){
return [
'image.*' => 'mimes:jpeg,jpg,png',
];
}
public function mesages(){
return [
'image.*' => 'Invalid file type.',
];
}
For more info, see Laravel's Documentation on Validation Arrays

Laravel testing Failed asserting that two strings are equal

I am very new to testing, but have now found it essential to automate my testing.
I have a test that is working fine up until it gets to the link '/cart' it gets to the link '/cart' no problem, but any other link I try to click afterwards always ends up back at the cart.
here is my error after trying to navigate away from the cart.
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-'http://ngwenya-mtb.dev/events'
+'http://ngwenya-mtb.dev/cart'
And here is my test script
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase {
//use Illuminate\Foundation\Testing\WithoutMiddleware;
//use DatabaseTransactions;
//use withoutMiddleware;
//use DatabaseMigrations;
/**
*
* A basic functional test example.
* Please choose a unique email address for your new participant
* #return void
*/
public function testNewUserRegistration() {
$this->visit('http://ngwenya-mtb.dev/')
// View Event
->click('View event details')
->seePageIs('/event?id=30')
->click('#enter-race47')
->press('Enter yourself to this race')
->seePageIs('/events/courses/register/addtocart')
//->withSession(['This email is already registered' => 'alert-danger'])
/////////////////////////////////////////////
// Fill the register for for new user
/////////////////////////////////////////////
->type('Bingo', 'first_name')
->type('11111111', 'password')
->type('11111111', 'password_confirmation')
->type(''.substr(md5(time()), 0, 12).'#tesing.com', 'email')
//->check('terms')
->select('Male', 'gender')
->select('1985', 'year')
->select('07', 'month')
->select('21', 'day')
->select('Small', 'shirt_size')
->select('Swaziland ID', 'id_type')
->type('badassnumber', 'id_number')
->select('Swazi', 'nationality')
//Contact details Physical
->type('Dawlish', 'town_physical')
->select('Swaziland', 'country_physical')
->type('864741', 'phone_cell')
//Emergency contact details 1
->type('Simon', 'emergency_contact_1')
->type('Brother', 'emergency_relationship_1')
->type('864741', 'emergency_phone_1');
$this->press('Register');
$this->seePageIs('/cart');
/////////////////////////////////////////////
// Add a new user
/////////////////////////////////////////////
$this->visit('http://ngwenya-mtb.dev/');
$this->click('#events-link')
->seePageIs('/events');
dd($this->response->getContent());exit;
$this->click('#event-30');
$this->seePageIs('/event?id=30');
$this->click('#enter-race48');
$this->press('Enter someone else to this race');
$this->seePageIs('/events/courses/register/addtocart');
}
}
Everything is working fine up until this comment
/////////////////////////////////////////////
// Add a new user
/////////////////////////////////////////////
Here is my Registration controller
<?php
namespace App\Http\Controllers;
use Vinkla\Hashids\HashidsManager;
use Illuminate\Routing\Controller as BaseController;
use Sentinel\FormRequests\RegisterRequest;
use Sentinel\FormRequests\EmailRequest;
use Sentinel\FormRequests\ResetPasswordRequest;
use Sentinel\Repositories\Group\SentinelGroupRepositoryInterface;
use Sentinel\Repositories\User\SentinelUserRepositoryInterface;
use Sentinel\Traits\SentinelRedirectionTrait;
use Sentinel\Traits\SentinelViewfinderTrait;
use Sentry;
use View;
use Request;
use Event;
use Redirect;
use Session;
use Config;
use App\Models\Users;
use Illuminate\Support\Facades\Input;
use Gloudemans\Shoppingcart\Facades\Cart;
class RegistrationController extends BaseController
{
/**
* Traits
*/
use SentinelRedirectionTrait;
use SentinelViewfinderTrait;
/**
* Constructor
*/
public function __construct(
SentinelUserRepositoryInterface $userRepository,
SentinelGroupRepositoryInterface $groupRepository,
HashidsManager $hashids
) {
$this->userRepository = $userRepository;
$this->groupRepository = $groupRepository;
$this->hashids = $hashids;
}
/**
* Show the registration form, if registration is allowed
*
* #return Response
*/
public function registration()
{
// Is this user already signed in? If so redirect to the post login route
if (Sentry::check()) {
return $this->redirectTo('session_store');
}
//If registration is currently disabled, show a message and redirect home.
if (! config('sentinel.registration', false)) {
return $this->redirectTo(['route' => 'home'], ['error' => trans('Sentinel::users.inactive_reg')]);
}
// All clear - show the registration form.
return $this->viewFinder(config('sentinel.view.user_register', 'Sentinel::users.register'));
}
/**
* Process a registration request
*
* #return Response
*/
public function register(RegisterRequest $request)
{
// Gather input
$data = $request->all();
// collect cart items
$email = Input::get('email');
$course_id = Input::get('course_id');
$event_name = Input::get('event_name');
$entry_fee = Input::get('entry_fee');
// check user exists
if (Users::where('email', '=', $email)->exists()) {
// user found
$request->session()->flash('alert-danger', 'Warning: This email is already registered.');
Input::flash();
return View::make('sentinel.users.register')
->with('course_id',$course_id)
->with('event_name',$event_name)
->with('entry_fee',$entry_fee);
}
// Add user and course to cart
if ($course_id) {
$firstUserRowId = Cart::add($course_id, $event_name , 1, $entry_fee, [
'first_name' => Input::get('first_name'),
'last_name' => Input::get('last_name'),
'email' => Input::get('email'),
'no_email' => 0,
'master_user' => 1,
'gender' => Input::get('gender'),
'dob' => Input::get('dob'),
'shirt_size' => Input::get('shirt_size'),
'id_type' => Input::get('id_type'),
'id_number' => Input::get('id_number'),
'nationality' => Input::get('nationality'),
'phone_cell' => Input::get('phone_cell'),
'town_physical' => Input::get('town_physical'),
'country_physical' => Input::get('country_physical'),
'emergency_contact_1' => Input::get('emergency_contact_1'),
'emergency_relationship_1' => Input::get('emergency_relationship_1'),
'emergency_phone_1' => Input::get('emergency_phone_1'),
]);
}
// get email from request
$email = $request->only('email');
foreach ($email as $userModel) {}
// Edit date of birth from request
$year = Input::get('year');
$month = Input::get('month');
$day = Input::get('day');
$dob = $year.'-'.$month.'-'.$day;
$data['dob'] = $dob;
// Attempt Registration
$result = $this->userRepository->store($data);
// Log user in
FunctionsController::loginUser($userModel);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_complete', $result);
}
/**
* Activate a new user
*
* #param int $id
* #param string $code
*
* #return Response
*/
public function activate($hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Attempt the activation
$result = $this->userRepository->activate($id, $code);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_activated', $result);
}
/**
* Show the 'Resend Activation' form
*
* #return View
*/
public function resendActivationForm()
{
return $this->viewFinder('Sentinel::users.resend');
}
/**
* Process resend activation request
* #return Response
*/
public function resendActivation(EmailRequest $request)
{
// Resend the activation email
$result = $this->userRepository->resend(['email' => e($request->get('email'))]);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_resend', $result);
}
/**
* Display the "Forgot Password" form
*
* #return \Illuminate\View\View
*/
public function forgotPasswordForm()
{
return $this->viewFinder('Sentinel::users.forgot');
}
/**
* Process Forgot Password request
* #return Response
*/
public function sendResetPasswordEmail(EmailRequest $request)
{
// Send Password Reset Email
$result = $this->userRepository->triggerPasswordReset(e($request->get('email')));
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_reset_triggered', $result);
}
/**
* A user is attempting to reset their password
*
* #param $id
* #param $code
*
* #return Redirect|View
*/
public function passwordResetForm($hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Validate Reset Code
$result = $this->userRepository->validateResetCode($id, $code);
if (! $result->isSuccessful()) {
return $this->redirectViaResponse('registration_reset_invalid', $result);
}
return $this->viewFinder('Sentinel::users.reset', [
'hash' => $hash,
'code' => $code
]);
}
/**
* Process a password reset form submission
*
* #param $hash
* #param $code
* #return Response
*/
public function resetPassword(ResetPasswordRequest $request, $hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Gather input data
$data = $request->only('password', 'password_confirmation');
// Change the user's password
$result = $this->userRepository->resetPassword($id, $code, e($data['password']));
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_reset_complete', $result);
}
}
It seems that when you click and a your link "Register" your redirection fail, so check if you have multiple "Register" links/buttons, and if they are pointing to the right URL
And for easiest debugging, you should make less assertions per Test, you will gain in visibility :)

How to pass parameters to a redirect route in Request

Language is a model binding in route file.
Route
Route::post('managment/{Language}/create', ['as' => 'dictionary.store', 'uses' => 'DictionaryController#store' ]);
I like to declare a Request (DictionaryRequest) file which extends Request(FormRequest), and it's responsible for the request parameter at Controller.
method prototype is :
public function store(DictionaryRequest $request, Language $lang)
the redirectRoute in the request class is set as :
protected $redirectRoute = "dictionary.create";
how can I pass on parameter to the route?? (the Langauge model)
I checked FormRequest class, but redirectRoute just passes on to the UrlGenerator with no parameters.
/**
* Get the URL to redirect to on a validation error.
*
* #return string
*/
protected function getRedirectUrl()
{
$url = $this->redirector->getUrlGenerator();
if ($this->redirect) {
return $url->to($this->redirect);
} elseif ($this->redirectRoute) {
return $url->route($this->redirectRoute);
} elseif ($this->redirectAction) {
return $url->action($this->redirectAction);
}
return $url->previous();
}
Did you try override getRedirectUrl?
/**
* Get the URL to redirect to on a validation error.
*
* #return string
*/
protected function getRedirectUrl()
{
$url = $this->redirector->getUrlGenerator();
return $url->route($this->redirectRoute, [ /*your parameters*/ ]);
}

Laravel Form Request issue with validation

I am trying to use form request in my REST API built using laravel 5.2. My controller is
public function save(SbcEntityFormRequest $request)
{
$requestData = Input::all();
try {
list($success, $message) = $this->sbcService->saveSbcEntity($requestData);
if ($success) {
return $this->successJsonResponse($request, ['id' => $message]);
}
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, [$message]);
} catch (Exception $e) {
AppLog::write($e);
$message = [config('messages.save_failed')];
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, $message);
}
}
My form request is
namespace App\Http\Requests;
use Illuminate\Http\Request;
class SbcEntityFormRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'logo' => 'Required',
'bio' => 'Required|Max:150'
];
}
}
My validation rules are never called. I put a die statement in authorize() function and it is neither called. When I printed $request->all() in the controller it shows empty array. Any Idea on what is wrong here?

Resources