Laravel testing Failed asserting that two strings are equal - laravel

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 :)

Related

Declaration of App\Http\Requests\UserUpdateRequest::user() should be compatible with Illuminate\Http\Request::user($guard = NULL)

I am trying to back and implement FormRequest objects for validation. I have successfully setup the form requests for all of my models except for the User model. I am getting the following error Declaration of App\Http\Requests\UserUpdateRequest::user() should be compatible with Illuminate\Http\Request::user($guard = NULL). Researching this error it seems like it may be an issue with the way that I'm handling the authorization through Policies. Note that the UserStoreRequest works but the UserUpdateRequest returns the error.
UserStoreRequest
<?php
namespace App\Http\Requests;
use App\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class UserStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// Authorize action - create-user
return Gate::allows('create', User::class);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|string',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8|confirmed',
'markets' => 'required|array',
'roles' => 'required|array',
];
}
/**
* Save the user.
*
* #return \App\User
*/
public function save()
{
// Create the user
$user = new User($this->validated());
// Set the password
$user->password = Hash::make($this->validated()['password']);
$user->setRememberToken(Str::random(60));
// Save the user
$user->save();
// Set users markets
$user->markets()->sync($this->validated()['markets']);
// Update the users role if included in the request
if ($this->validated()['roles']) {
foreach ($this->validated()['roles'] as $role) {
$user->roles()->sync($role);
if ($user->hasRole('admin')) {
$user->markets()->sync(Market::all());
}
}
}
return $user;
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'name.required' => 'The name is required.',
'email.required' => 'The email is required.',
'email.unique' => 'The email must be unique.',
'password.required' => 'The password is required.',
'password.confirmed' => 'The passwords do not match.',
'password.min' => 'The password must be at least 8 characters.',
'markets.required' => 'A market is required.',
'roles.required' => 'A role is required.',
];
}
}
UserUpdateRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class UserUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// Authorize action - update-user
return Gate::allows('update', $this->user);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|string',
];
}
/**
* Get the user from the route.
*
* #return \App\User
*/
public function user()
{
return $this->route('user');
}
/**
* Save the email role.
*
* #return \App\Role
*/
public function save()
{
// Update the user
$this->user->update($this->validated());
// // Check to see if password is being updated
// if ($this->validated()['password']) {
// $this->user->password = Hash::make($this->validated()['password']);
// $this->user->setRememberToken(Str::random(60));
// }
// // Set users markets
// $this->user->markets()->sync($this->validated()['markets']);
// // Set users roles
// // // Update the users role if included in the request
// if ($this->validated()['roles']) {
// foreach ($this->validated()['roles'] as $role) {
// $this->user->roles()->sync($role);
// if ($this->user->hasRole('admin')) {
// $this->user->markets()->sync(Market::all());
// }
// }
// }
// // Save the user
// $this->user->save();
return $this->user;
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'name.required' => 'The name is required.',
'email.required' => 'The email is required.',
'email.unique' => 'The email must be unique.',
'markets.required' => 'A market is required.',
'roles.required' => 'A role is required.',
];
}
}
As you can see I have commented out most of the code for the UpdateRequest for troubleshooting. It seems that the issue is with the authorize() method. Below is the code from the UserPolicy
UserPolicy
/**
* Determine whether the user can create models.
*
* #param \App\User $user
*
* #return mixed
*/
public function create(User $user)
{
return $user->hasPermission('create-user');
}
/**
* Determine whether the user can update the model.
*
* #param \App\User $user
* #param \App\User $model
*
* #return mixed
*/
public function update(User $user, User $model)
{
return $user->hasPermission('update-user');
}
UserController
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\UserStoreRequest $request
*
* #return \Illuminate\Http\Response
*/
public function store(UserStoreRequest $request)
{
return redirect($request->save()->path());
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\UserUpdateRequest $request
* #param \App\User $user
*
* #return \Illuminate\Http\Response
*/
public function update(UserUpdateRequest $request, User $user)
{
return redirect($request->save()->path());
}
I'm using a permission based authorization for this system. The user has the hasPermission() method on it to verify if the user has the required permission to perform the action. I fear that I've confused myself on this setup and that I am not validating correctly. Everything has worked up until trying to implement this on the User model.
hasPermission()
/**
* Check to see if the model has a permission assigned.
*
* #param string $permission
*
* #return bool
*/
public function hasPermission($permission)
{
if (is_string($permission)) {
if (is_null(Permission::whereName($permission)->first())) {
return false;
} else {
return $this->hasRole(Permission::where('name', $permission)->first()->roles);
}
}
return $this->hasRole($permission->roles);
}
hasRole()
/**
* Check to see if model has a role assigned.
*
* #param string $role
*
* #return bool
*/
public function hasRole($role)
{
if (is_string($role)) {
return $this->roles->contains('name', $role);
}
return (bool) $role->intersect($this->roles)->count();
}
Update
I have tried renaming the user() method in UserUpdateRequest to frank() to resolve any issues overriding the Request user. This clears up the error listed above, but then the response is returned unauthorized. The logged in user has permissions set to allow updating users. This is called in the authorize() method using Gate::allows. I'm just not sure if it's checking the logged in user or the model user.
I investigated further and found that there is a new error after changing the method to frank(). I am getting Call to a member function update() on null. I should be returning the user pulled from the route from the frank method but it seems to be returning null.
Updated UserUpdateRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class UserUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// Authorize action - update-user
return Gate::allows('update', $this->frank);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|string',
];
}
/**
* Get the user from the route.
*
* #return \App\User
*/
public function frank()
{
return $this->route('user');
}
/**
* Save the email role.
*
* #return \App\Role
*/
public function save()
{
// Update the user
$this->frank->update($this->validated());
// // Check to see if password is being updated
// if ($this->validated()['password']) {
// $this->user->password = Hash::make($this->validated()['password']);
// $this->user->setRememberToken(Str::random(60));
// }
// // Set users markets
// $this->user->markets()->sync($this->validated()['markets']);
// // Set users roles
// // // Update the users role if included in the request
// if ($this->validated()['roles']) {
// foreach ($this->validated()['roles'] as $role) {
// $this->user->roles()->sync($role);
// if ($this->user->hasRole('admin')) {
// $this->user->markets()->sync(Market::all());
// }
// }
// }
// // Save the user
// $this->user->save();
return $this->frank;
}
/**
* Get the error messages for the defined validation rules.
*
* #return array
*/
public function messages()
{
return [
'name.required' => 'The name is required.',
'email.required' => 'The email is required.',
'email.unique' => 'The email must be unique.',
'markets.required' => 'A market is required.',
'roles.required' => 'A role is required.',
];
}
}
The issue is the user() method you defined on your UserUpdateRequest class.
UserUpdateRequest extends Illuminate\Foundation\Http\FormRequest, which in turn extends Illuminate\Http\Request. Illuminate\Http\Request already has a user() method defined, so the user() method in your UserUpdateRequest class is attempting to override this definition.
Since your UserUpdateRequest::user() method doesn't match the Illuminate\Http\Request::user($guard = null) signature, you're getting that error.
You can either:
Remove the user() method from your UserUpdateRequest class, or
Rename your user() method on your UserUpdateRequest class, or
Add the $guard = null parameter to your user() method on your UserUpdateRequest class, so that it matches the signature of the base user() method.

Access Controller resources based on Laravel/Spatie Permissions

I am working on Laravel passport api in which i am using spatie package for user role's and permission's. I have to perform certain operation ('store','view','update','delete') based on user permission's.
For this purpose i have created a trait and used in controller but it is not working correctly.
On every api request it throw's an exception "This action is unauthorized" either the user has permission or not.
Authorize Trait :
<?php
namespace App;
/*
* A trait to handle authorization based on users permissions for given controller
*/
trait Authorizable
{
/**
* Abilities
*
* #var array
*/
private $abilities = [
'index' => 'view',
'edit' => 'edit',
'show' => 'view',
'update' => 'edit',
'create' => 'add',
'store' => 'add',
'destroy' => 'delete'
];
/**
* Override of callAction to perform the authorization before it calls the action
*
* #param $method
* #param $parameters
* #return mixed
*/
public function callAction($method, $parameters)
{
if( $ability = $this->getAbility($method) ) {
$this->authorize($ability);
}
return parent::callAction($method, $parameters);
}
/**
* Get ability
*
* #param $method
* #return null|string
*/
public function getAbility($method)
{
$routeName = explode('.', \Request::route()->getName());
$action = array_get($this->getAbilities(), $method);
return $action ? $action . '_' . $routeName[0] : null;
}
/**
* #return array
*/
private function getAbilities()
{
return $this->abilities;
}
/**
* #param array $abilities
*/
public function setAbilities($abilities)
{
$this->abilities = $abilities;
}
}
Routes:
Route::middleware('auth:api')->group(function () {
Route::post('user', 'ApiController#user');
Route::post('view_department', 'DepartmentController#index');
Route::post('add_department', 'DepartmentController#store');
Route::post('edit_department', 'DepartmentController#update');
Route::post('delete_department', 'DepartmentController#destroy');
Route::post('/logout', 'ApiController#logout');
}); // auth middleware ends
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\User;
use App\Authorizable;
use Illuminate\Support\Facades\Validator;
use App\Department;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class DepartmentController extends Controller
{
use Authorizable;
//
public function index(Request $request) {
// return response
return response()->json([
'success' => 'You have the permission to view departments!']);
}
//
public function store(Request $request) {
// validate the posted data
$validator = Validator::make($request->all(), [
'name' => 'required|string|unique:departments',
]);
// return errors
if ($validator->fails())
{
return response(['errors'=>$validator->errors()->all()]);
}
$department = new Department;
$department->name = $request->name;
$department->save();
// return response
return response()->json([
'success' => 'Successfully created department!']);
}
}
I am badly stack at it, don't know where i am going wrong. I would highly appreciate if anyone guide me through this.
Thanks,

Laravel : Overwrite Socialite Provider to add new fields

I want to extend/overwrite my LinkedInProvider.php (in vendor\laravel\socialite\src\Two) to add new fields in the Linkedin Request.
I've create a new LinkedInProvider.php (in app\Providers) with the following code :
namespace App\Providers;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;
use Laravel\Socialite\Two\User;
class LinkedInProvider extends AbstractProvider implements ProviderInterface
{
/**
* The scopes being requested.
*
* #var array
*/
protected $scopes = ['r_basicprofile', 'r_emailaddress'];
/**
* The separating character for the requested scopes.
*
* #var string
*/
protected $scopeSeparator = ' ';
/**
* The fields that are included in the profile.
*
* #var array
*/
protected $fields = [
'id', 'first-name', 'last-name', 'formatted-name',
'email-address', 'headline', 'location', 'industry', 'positions',
'public-profile-url', 'picture-url', 'picture-urls::(original)',
];
/**
* {#inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
}
/**
* {#inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.linkedin.com/oauth/v2/accessToken';
}
/**
* Get the POST fields for the token request.
*
* #param string $code
* #return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* {#inheritdoc}
*/
protected function getUserByToken($token)
{
$fields = implode(',', $this->fields);
$url = 'https://api.linkedin.com/v1/people/~:('.$fields.')';
$response = $this->getHttpClient()->get($url, [
'headers' => [
'x-li-format' => 'json',
'Authorization' => 'Bearer '.$token,
],
]);
return json_decode($response->getBody(), true);
}
/**
* {#inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'], 'nickname' => null, 'name' => Arr::get($user, 'formattedName'),
'email' => Arr::get($user, 'emailAddress'), 'avatar' => Arr::get($user, 'pictureUrl'),
'avatar_original' => Arr::get($user, 'pictureUrls.values.0'),
]);
}
/**
* Set the user fields to request from LinkedIn.
*
* #param array $fields
* #return $this
*/
public function fields(array $fields)
{
$this->fields = $fields;
return $this;
}
}
But now, I've got this error :
Type error: Argument 1 passed to Laravel\Socialite\Two\AbstractProvider::__construct() must be an instance of Illuminate\Http\Request, instance of Illuminate\Foundation\Application given, called in G:\laragon\www\localhost\vendor\laravel\framework\src\Illuminate\Foundation\ProviderRepository.php on line 201
I know I can install Socialite Manager, but I just want to overwrite the fields list to add new field (like position and industry)
You shouldn't have to overwrite/extend the whole class. In the Laravel\Socialite\Two\User object that is being created, there is a $user property, which contains the raw information the provider sent back.
When making the request, you can set the fields you want LinkedIn to return in your controller method:
public function redirectToProvider()
{
$fields = [
'id', 'first-name', 'last-name', 'formatted-name',
'email-address', 'headline', 'location', 'industry',
'public-profile-url', 'picture-url', 'picture-urls:(original)',
'positions', 'summary' // <-- additional fields here
];
return Socialite::driver('linkedin')->fields($fields)->redirect();
}
You can see two additional fields being requested, positions and summary, which aren't included by default.
Happy hacking!

Laravel 5 FormRequest validator with multiple scenarios

I would like to ask how should I handle validation on multiple scenarios using FormRequest in L5? I know and I was told that I can create saparate FormRequest files to handle different validations but it is very redundant and also noted that I would need to inject it into the controller manually using the use FormRequest; keyword. What did previously in L4.2 is that I can define a new function inside my customValidator.php which then being called during controller validation via trycatch and then the data is being validated by service using the below implementation.
class somethingFormValidator extends \Core\Validators\LaravelValidator
{
protected $rules = array(
'title' => 'required',
'fullname' => 'required',
// and many more
);
public function scenario($scene)
{
switch ($scene) {
case 'update':
$this->rules = array(
'title' => 'required',
'fullname' => 'required',
// and other update validated inputs
break;
}
return $this;
}
}
Which then in my LaravelValidator.php
<?php namespace Core\Validators;
use Validator;
abstract class LaravelValidator {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
/**
* Set data to validate
*
* #return \Services\Validations\AbstractLaravelValidator
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
/**
* Validation passes or fails
*
* #return Boolean
*/
public function passes()
{
$validator = Validator::make(
$this->data,
$this->rules,
$this->messages
);
if ($validator->fails())
{
$this->errors = $validator->messages();
return false;
}
return true;
}
/**
* Return errors, if any
*
* #return array
*/
public function errors()
{
return $this->errors;
}
}
and then finally this is how i call the scenarios inside services like this
public function __construct(somethingFormValidator $v)
{
$this->v = $v;
}
public function updateSomething($array)
{
if($this->v->scenario('update')->with($array)->passes())
{
//do something
else
{
throw new ValidationFailedException(
'Validation Fail',
null,
$this->v->errors()
);
}
}
So the problem is now since i have migrated to L5 and L5 uses FormRequest, how should I use scenario validation in my codes?
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class ResetpasswordRequest 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 [
'login_email' => 'required',
'g-recaptcha-response' => 'required|captcha',
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'g-recaptcha-response.required' => 'Are you a robot?',
'g-recaptcha-response.captcha' => 'Captcha session timeout'
];
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = array(
//scenario rules
);
break;
}
return $this;
}
}
also how should I call it in the controller?
public function postReset(ResetpasswordRequest $request)
{
$profile = ProfileService::getProfileByEmail(Request::input('login_email'));
if($profile == null)
{
$e = array('login_email' => 'This email address is not registered');
return redirect()->route('reset')->withInput()->withErrors($e);
}
else
{
//$hash = ProfileService::createResetHash($profile->profile_id);
$time = strtotime('now');
$ip = Determinator::getClientIP();
MailProcessor::sendResetEmail(array('email' => $profile->email,
'ip' => $ip, 'time' => $time,));
}
}
I believe the real issue at hand is everything is validated through the form request object before it reaches your controller and you were unable to set the appropriate validation rules.
The best solution I can come up with for that is to set the validation rules in the form request object's constructor. Unfortunately, I am not sure how or where you are able to come up with the $scene var as it seems to be hard-coded in your example as 'update'.
I did come up with this though. Hopefully reading my comments in the constructor will help further.
namespace App\Http\Requests;
use App\Http\Requests\Request;
class TestFormRequest extends Request
{
protected $rules = [
'title' => 'required',
'fullname' => 'required',
// and many more
];
public function __construct()
{
call_user_func_array(array($this, 'parent::__construct'), func_get_args());
// Not sure how to come up with the scenario. It would be easiest to add/set a hidden form field
// and set it to 'scene1' etc...
$this->scenario($this->get('scenario'));
// Could also inspect the route to set the correct scenario if that would be helpful?
// $this->route()->getUri();
}
/**
* 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 $this->rules;
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = [
//scenario rules
];
break;
}
}
}
You can use laratalks/validator package for validation with multiple scenarios in laravel. see this repo

Laravel 5 how to validate route parameters?

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
);
}

Resources