Q: How can i reuse Auth's ResigsterController validation rules from another controller without having to grab the whole RegisterController class?
Here are the rules:
...
protected function validator(array $data)
{
return Validator::make($data, [
'fname' => 'required|string|max:255',
'lname' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'tos' => 'boolean|accepted',
]);
}
...
The other controller in:
app\Http\Controllers\UserController.php
public function update(Request $request, User $user)
{
//I need to validate $request with the rules from
//ControllersAuth\RegisterController.php
}
To reuse same validation rules you can use Form Request Validation
I suggest to use trait. Create a trait like:
trait ValidationTrait {
public $errors;
public function validate($data) {
$Reflection = new \ReflectionClass(__CLASS__);
$ReflectionClass = $Reflection->newInstance();
if(empty($ReflectionClass->rules)) return TRUE;
$v = Validator::make($data, $ReflectionClass->rules);
if($v->fails()) {
$this->errors = $v->failed();
return FALSE;
}
return TRUE;
}
public function validationErrors() {
return $this->errors;
}
}
In model:
class myModel extends Eloquent {
use ValidationTrait;
public $rules = array(
'fname' => 'required|string|max:255',
'lname' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'tos' => 'boolean|accepted',
);
}
Call it like:
$myModel = new myModel();
if($myModel->validate(Input::all()) {
//validate success....
}else{
return $Customer->validationErrors()
}
You should try using creating a Helper class so you can have some of your code's reusable in any class.
If you don't know how to make a Helper class there are guides here
you can make a file request add the validation rule there, next time u need it, you can call the file name,ex:
public function update(UserRequest $request,User $user){
}
//1. Php artisan make:request UserRequest
//2. add rule to UserRequest file
public function rules()
{
return [
'fname' => 'required|string|max:255',
'lname' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'tos' => 'boolean|accepted',
];
}
https://laravel.com/docs/5.4/validation#form-request-validation
Related
I thought that if($this->validator($request->all())) this would return true or false, but it returns a object every time returns true
use RegistersUsers;
protected $redirectTo = '/home';
public function __construct()
{
$this->middleware('guest');
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'surname' => 'required|string|max:255',
'age' => 'required|integer|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'surname' => $data['surname'],
'age' => $data['age'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
public function register(Request $request){
if($this->validator($request->all())){
//create user
// function after registration
var_dump('success');
}else{
//redirect to ...
var_dump('fail');
}
}
You have a couple of options here:
Use validate()
public function register(Request $request)
{
$this->validator($request->all())->validate();
dd('success'); //This won't get called if validation doesn't pass
}
Using the above method, Laravel will handle the response for you.
or if you want to have more control over the how you handle the validation then you can use the passes() method instead:
public function register(Request $request)
{
if ($this->validator($request->all())->passes()) {
dd('success');
} else {
dd('fail');
}
}
Manually creating validators
I'm using the default laravel authentication. Every user who registeres in my site needs a activation pin. The valid PINs are stored in a different table. Whenever a user registeres, I want to test if the PIN is valid or not. So, is there any method that I can override in RegisterController that executes before regstering the user?
Yes. You can override protected register method in RegisterController. This is a simple solution. I do this to validate params, save a new user, and force return JSON in one of my projects.
For example:
protected function register(Request $request)
{
$validator = Validator::make($request->all(), [
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required|email|unique:users',
'phone' => 'required',
'pin' => 'required'
]);
//Check your PIN here, if it's wrong, append errors array
if ($validator->fails())
throw new ValidationFailed($validator->errors());
User::create([
'first_name' => $request->input('first_name'),
'last_name' => $request->input('last_name'),
'email' => $request->input('email'),
'phone' => $request->input('phone'),
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
]);
return ResponseJSON::make(trans('responses.registered'));
}
You can add a validation rule for validating the pin in the validator method like this :
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'pin' => [
'required',
Rule::exists('pins')->where(function ($query) {
$query->where('Some condition');
}),
],
]);
}
I assume you have a table for pins called pins
extend the register function in user class:
public function register(Request $request)
{
event(new \App\Events\NewUserRegistered(Auth::user()));
return redirect($this->redirectPath());
}
NewUserRegistered:
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\User;
class NewUserRegistered extends Event
{
use SerializesModels;
public $userID;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $userID)
{
//get whatever is massed in here's value in the id column
$this->id = $userID;
}
/**
* Get the channels the event should be broadcast on.
*
* #return array
*/
public function broadcastOn()
{
return [];
}
}
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 know this is the same quetion title like others, but i cant find the right answer for me. Here is my problem.
I am trying to insert data to the other table after users data inserted. But what i got is Call to a member function create() on null.
Here is my code of AuthController.php
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$user->usersbasics()->create([
'user_id' => $user->id,
]);
return $user;
}
Here is my User model
public function usersbasics()
{
$this->hasOne('App\UsersBasics');
}
Here is my UsersBasics model:
public function user()
{
$this->belongsTo('App\User');
}
I tried var_dumb($user->usersbasics()) and the result is NULL.
What is whrong with my code? because i use the same for my other work, and its fine. Please someone explain me and give me a solution. Please..
thank you,
This is a late answer, but I ran into the same problem.
You don't have the return keyword on both your relationships.
public function user()
{
return $this->belongsTo('App\User');
}
and
public function usersbasics()
{
return $this->hasOne('App\UsersBasics');
}
This fixxed it for me.
You need to set the parameters fillable in your User model.
class User extends Model
{
protected $fillable = ['name', 'email', 'password'];
}
Your controller method is protected instead of public.
If you look at the existing AuthController, we have function you mentioned:
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
This method is then called with postRegister (public, so it can be called from routes.php), where we pass only the input from Request as the parameter.
public function postRegister(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$user = $this->create($request->all());
return redirect($this->redirectPath());
}
I assume you tried to call the create() method directly from controller, which doesn't work because it's protected and can only be called from within the class itself. If you really want to make create() method work without postRegister, you would do something like this:
public function create(Request $request)
{
$user = User::create([
'name' => $request->input('name'),
'email' => $request->input('email'),
'password' => bcrypt($request->input('password')),
]);
$user->usersbasics()->create([
'user_id' => $user->id,
]);
return $user;
}
And then call this in your routes.php
Route::post('urlToPostMethod', 'Auth\AuthController#create');
The other, generally nicer way would be to call this instead
Route::post('urlToPostMethod', 'Auth\AuthController#postRegister');