I'm new to Laravel and am trying to figure out how to authenticate against two tables during new user registration.
I've modified the default methods in AuthController - I'm checking to see if a store number is valid, and if it is, register the user. This works fine - if the store number provided checks out, the user is inserted into both tables (user and user_store) and redirected to the dashboard page.
However, if the validation against $store is false, then I receive the following error
Argument 1 passed to Illuminate\Auth\Guard::login() must be an instance of Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Http\RedirectResponse given
As you can see in the code I'm just trying to redirect to the auth/register view and provide an error message that the store number was invalid. Where am I going wrong?
SEE UPDATED CODE BELOW THIS BLOCK...
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:user',
'password' => 'required|min:6',
'store_number' => 'required',
]);
}
protected function create(array $data)
{
if(!$store = Store::where('number', $data['store_number'])->first()) {
// HERE'S WHERE I'M HAVING THE PROBLEM
return redirect('auth/register')->withErrors('store_number','Could not find a match for the Store Number');
} else {
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$data['sid'] = $store->id;
$data['uid'] = $user->id;
$store = UserStore::create($data);
return $user;
}
}
UPDATE
I've since moved this into the validator() method, because, well, it makes more sense to do the validation in the validator() method... right?
Here's my new code.
protected function validator(array $data)
{
if(!Store::where('number', $data['store_number'])->first()) {
// still not working!
return redirect('auth/register')->withErrors('store_number','Could not find a match for the Store Number');
}
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:user',
'password' => 'required|min:6',
'store_number' => 'required',
]);
}
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$s['sid'] = $data['store_number'];
$s['uid'] = $user->id;
$store = UserStore::create($s);
return $user;
}
And here's my new error message
BadMethodCallException in RedirectResponse.php line 198:
Method [fails] does not exist on Redirect.
Figured it out. I needed to use the After validation hook inside the validator() method. :)
protected function validator(array $data)
{
$validator = Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:user',
'password' => 'required|min:6',
'store_number' => 'required',
]);
$validator->after(function($validator) {
if(!Store::where('number', $_POST['store_number'])->first()) {
$validator->errors()->add('store_number', 'Could not find a match for the Store number');
}
});
return $validator;
}
Related
I have a small question. I create simple API using Laravel. When I use validation and if it fails, I got a common message:
{
"result": false,
"message": "The given data failed to pass validation.",
"details": []
}
But how can I get details about which field fails and why like that:
{
"result":false,
"message":"The given data failed to pass validation.",
"details":{
"email":[
"The email field is required."
],
"password":[
"The password must be at least 3 characters."
]
}
}
My code in controller looks like this:
protected function validator(array $data)
{
$validator = Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:3',
]);
return $validator;
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'role_id' => 2
]);
}
It is better to handle the validator within the same process, like this:
public function register(Request $request){
$validator = Validator::make($request->all(),[
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
if($validator->fails()){
return response()->json([
"error" => 'validation_error',
"message" => $validator->errors(),
], 422);
}
$request->merge(['password' => Hash::make($request->password)]);
try{
$user = User::create($request->all());
return response()->json(['status','registered successfully'],200);
}
catch(Exception $e){
return response()->json([
"error" => "could_not_register",
"message" => "Unable to register user"
], 400);
}
}
You should make sure you're sending the request with the Accept: application/json header.
Without that - Laravel won't detect that it's an API request,
If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
check the documentation
I used validate in my project:
1.I created app/http/requests/CreateUserRequestForm.php
public function rules()
{
return [
"name" => 'required',
"address" => 'required',
"phnumber" => 'required|numeric',
];
}
public function messages()
{
return [
'name.required' => 'Please Enter Name',
'addresss.required' => 'Please Enter Address',
'phnumber.required' => 'Please Enter PhNumber'
];
}
call the RequestForm in controller
use App\Http\Requests\CreateUserRequestForm;
public function createUser(CreateUserRequestForm $request)
{
// create
$user= UserModel::create([
'name' => $request->input('name'),
'address' => $request->input('address'),
'phnumber' => $request->input('phnumber')
]);
return response()->json(['User' => $user]);
}
Try this i didn't try but it should be work for you.
You may use the withValidator method. This method receives the fully
constructed validator, allowing you to call any of its methods before
the validation rules are actually evaluated.
take reference from here. laravel validation
/**
* Configure the validator instance.
*
* #param \Illuminate\Validation\Validator $validator
* #return void
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('email', 'Please enter valid email id');
}
});
}
Try this:
public function create(){
// ------ Validate -----
$this->vallidate($request,[
'enter code here`name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:3'
]);
// ------ Create user -----
$user = User::create(['name' => $request->name']);
return response()->json([
'message' => "success",
'data' => $user``
]);
}
In user creation proccess I need to query a external server for additional data and if that happens to fail i need to return an error at validation or creation. How is it best to do this? Here is the default Laravel code for registering user with some of my attemps:
protected function validator(array $data)
{
$query = queryExternalServerForAdditionalData();
if($query->success){
//add data from query to other user data?
//$data['data'] = $query->data;
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
} else {
//somehow return an error, but how?
}
}
protected function create(array $data)
{
// or maybe do the query and error return here?
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
// add my additional data
// 'data' => $data['data']
]);
}
So as you can see my question is that how can i return an error from validator or create method? I tried to return redirect() with an error property added, but i got an error that something else was expected by the method calling validator/create and redirect was returned so that doesn't seem to be an option.
IMHO, the best way to do this is creating form requests.
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users|check_server_for_additional_data',
'password' => 'required|string|min:6|confirmed',
];
}
As an example, I added check_server_for_additional_data, a custom validation rule for email field (An error message will appear in the email field).
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Validator::extend('check_server_for_additional_data', function ($attribute, $value, $parameters, $validator) {
return // queryExternalServerForAdditionalData()
});
}
}
Don't forget to defining the error message (resources/lang/LOCALE/validation.php)
"check_server_for_additional_data" => "Umm ohh.. Invalid!",
I am working on a laravel project with user login. The admin can create new users and edit existing users. I have got a password and a passwordConfirm field in the update-user-form. If the admin puts a new password in the form, it should check for validation and update the record in the db. If not, it shouldn't change the password (keep old one), but update the other user data (like the firstname).
If I try to send the form with an empty password and passwordConfirm field, it doesn't validate. I got a validation error, that the password must be a string and at least 6 characters long, but I don't know why. It seems like the first line of my update function will be ignored.
UserController.php
public function update(User $user, UserRequest $request) {
$data = $request->has('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
$user->update($data);
return redirect('/users');
}
UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'string|min:6',
'passwordConfirm' => 'required_with:password|same:password',
];
}
If you want to validate a field only when it is present then use sometimes validation rule in such cases.
Add sometimes validation to both password & passwordConfirm. Remove the $data line from update();
// UserController.php
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
// UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'sometimes|required|string|min:6',
'passwordConfirm' => 'sometimes|required_with:password|same:password',
];
}
Reference - https://laravel.com/docs/5.4/validation#conditionally-adding-rules
I always do this in my projects:
//Your UserController file
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
//Your UserRequest file
public function rules() {
$rules= [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255'
];
if($this->method()=="POST"){
$rules['password']='sometimes|required|string|min:6';
$rules['passwordConfirm']='sometimes|required_with:password|same:password';
}
return $rules;
}
So, as you can see if your method is POST it means that you want to add a new user so its going to ask for password and passwordConfirm but if your method is PATCH or PUT it means you don't need to validate password and passwordConfirm.
Hope it helps
Maybe you should try the following:
// ... more code
// Removes password field if it's null
if (!$request->password) {
unset($request['password']);
}
$request->validate([
// ... other fields,
'password' => 'sometimes|min:6'
// ... other fields,
]);
// ... more code
you should replace "has" with "filled" in your code
$data = $request->filled('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
and actually it's better if you use the expression like this
$request->request->remove('password_confirmation');
( ! $request->filled('password') ) ? $request->request->remove('password'):"";
( $request->has('password') ) ? $request->merge([ 'password' => Hash::make($request->post()['password']) ]):"";
//then you can use
$user->update($request->all());
Even better, however, you have to use separate request classes for create and update "php artisan make:request" for ex:
UserUpdateRequest.php and UserCreateRequest.php
for UserCreateRequest your rule is
'password' => 'required|confirmed|min:6',
for UserUpdateRequest your rule is
'password' => 'sometimes|nullable|confirmed|min:6',
and your controller head add this line
use App\Http\Requests\UserCreateRequest;
use App\Http\Requests\UserUpdateRequest;
and your update method must change
public function update(UserUpdateRequest $request, $id)
{
//
}
Standard way of doing this
UserRequest.php
first import Rule
use Illuminate\Validation\Rule;
in your rules array:
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST")]
Example:
public function rules()
{
return [
'name' => 'required',
'email' => ['required', 'email'],
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST"), 'confirmed'],
];
}
below php 7.4 use this way
'password' => [Rule::requiredIf(function(){
return $this->route()->method == "POST";
})]
I use a standart Laravel Authentication.
php artisan make:auth
But I need to check the status of the user email (confirmed/not confirmed). If not confirmed, then an error will be shown in the login page.
Laravel 5.2
Thanks!
Make a column is_verified and another column verification_hash in users table. In AuthController.php
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'username' => $data['username'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'verification_hash' => md5($data['name'] . Carbon::now()->timestamp),
'is_verified' => 0,
]);
}
override register method to send him mail with Verification link
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$user = $this->create($request->all());
Auth::guard($this->getGuard())->login($user);
// Send verification email
// use $user->verification_hash to generate link
return redirect($this->redirectPath());
}
Now this verification link should look like below as it contain a hash specific to that user
http://your_domain.com/auth/verify/6d533b7664483d3cadd13c23477e4f12
on this link method, change is_verified to 1 for that user.
Now modify your validator method like this
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'username' => 'required|max:255|unique:users',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
'is_verified'=>'in:1'
]);
}
This will allow only those users to login who have is_verified value as 1.
Hope this helps.
When a new user registers, they should be automatically assigned to a plan subscription. I can do that manually in Tinker (Laravel 5):
$token = Input::get('stripeToken');
$user = User::all();
$user->subscription('monthly')->create($token);
flash('Your account has been created with a membership');
Where, in Laravel 5, do I put such logic?
Edit
public function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'company_name' => $data['company_name'],
// I have added the below:
$token = Input::get('stripeToken');
$user = User::all();
$user->subscription('loop')->create($token);
]);
}
If you are using Laravels Registrar service then I'd do it in there. That could look like this:
public function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'company_name' => $data['company_name']
]);
$token = Input::get('stripeToken');
$user->subscription('loop')->create($token);
return $user;
}
You might also want to get the stripeToken from the $data array but I'll leave that to you.