Laravel8 tdd: session is missing expected key errors - php

I just want to test an easy input field but I get this error!
/** #test */
public function email_must_be_a_valid_email()
{
$response = $this->post('/api/contacts', array_merge($this->data(), ['email' => 'NOT AN EMAIL']));
$response->assertSessionHasErrors('email');
}
private function data()
{
return [
'name' => 'Test Name',
'email' => 'test#hotmail.com',
'birthday' => '05/14/1988',
'company' => 'ABC String'
];
}
Thses are the Controller and the request rule.
I hope u can help me with it.
class StoreController extends Controller
{
public function store(ContactsRequest $request)
{
$data = $request->validated();
Contact::create($data);
}
}
public function rules()
{
return [
'name' => 'required',
'email' => 'required|email',
'birthday' => 'required',
'company' => 'required',
];
}
I hope u can help me with it.

If you are using an api route then you should use
$response->assertJsonValidationErrors(['email']);
or
$response->assertInvalid(['email']);
which works for both JSON and session errors.
https://laravel.com/docs/8.x/http-tests#assert-invalid

Thanks for your tips. I've solved the problem! But this kind of error message does not help at all to find the solution quickly.
The solution is that I need to be logged in to create contact and then validate form data. But the error message says something completely different!
protected $user;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
$this->user->createToken(Str::random(30))->plainTextToken;
}
/** #test */
public function email_must_be_a_valid_email()
{
$this->actingAs($this->user);
$response = $this->post('/api/contacts', array_merge($this->data(), ['email' => 'NOT AN EMAIL']));
$response->assertSessionHasErrors('email');
}

Related

PHP how to stop the code when a called method use return

I have made a main method which calls a validation method to check if everything is correct. But when the validation method find a problem and want to return the error and show it to the user the main method will continue with the rest of the code. How could you prevent this from happening? Here is my current code:
public function postUpdateSettings(Request $request)
{
$this->validateSettings($request);
$this->updateSettings($request);
return redirect()->back()->with('succes', 'Your settings has been changed!');
}
private function validateSettings(Request $request)
{
$this->validate($request, [
'email' => 'required|string|email|max:255|unique:users,email,'.Auth::user()->id.'',
'first_name' => 'required|string|max:50|alpha',
'insertion' => 'nullable|max:25|alpha_spaces',
'last_name' => 'required|string|max:50|alpha',
'job' => 'required',
'message' => 'nullable|max:500',
]);
$jobs = $this->getJob();
if(in_array($request->input('job'), $jobs) === false){
return redirect()->route('home');
}
}

Returning custom validation error in Laravel user validation/creation

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!",

Reuse laravel validation rules from another controller

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

Call to a member function create() on null

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

Laravel: Validation unique on update

I know this question has been asked many times before but no one explains how to get the id when you're validating in the model.
'email' => 'unique:users,email_address,10'
My validation rule is in the model so how do I pass the ID of the record to the validation rule.
Here is my models/User
protected $rules_update = [
'email_address' => 'required|email|unique:users,email_address,'.$id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
models/BaseModel
protected $rules = array();
public $errors;
/*
* #data: array, Data to be validated
* #rules: string, rule name in model
*/
public function validate($data, $rules = "rules") {
$validation = Validator::make($data, $this->$rules);
if($validation->passes()) {
return true;
}
$this->errors = $validation->messages();
return false;
}
Just a side note, most answers to this question talk about email_address while in Laravel's inbuilt auth system, the email field name is just email. Here is an example how you can validate a unique field, i.e. an email on the update:
In a Form Request, you do like this:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user->id,
];
}
Or if you are validating your data in a controller directly:
public function update(Request $request, User $user)
{
$request->validate([
'email' => 'required|email|unique:users,email,'.$user->id,
]);
}
Update:
If you are updating the signed in user and aren't injecting the User model into your route, you may encounter undefined property when accessing id on $this->user. In that case, use:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user()->id,
];
}
A more elegant way since Laravel 5.7 is:
public function rules()
{
return [
'email' => ['required', 'email', \Illuminate\Validation\Rule::unique('users')->ignore($this->user()->id)]
];
}
P.S: I have added some other rules, i.e. required and email, in order to make this example clear for newbies.
One simple solution.
In your Model
protected $rules = [
'email_address' => 'sometimes|required|email|unique:users',
..
];
In your Controller, action:update
...
$rules = User::$rules;
$rules['email_address'] = $rules['email_address'] . ',id,' . $id;
$validationCertificate = Validator::make($input, $rules);
There is an elegant way to do this. If you are using Resource Controllers, your link to edit your record will look like this:
/users/{user}/edit OR /users/1/edit
And in your UserRequest, the rule should be like this :
public function rules()
{
return [
'name' => [
'required',
'unique:users,name,' . $this->user
],
];
}
Or if your link to edit your record look like this:
/users/edit/1
You can try this also:
public function rules()
{
return [
'name' => [
'required',
'unique:users,name,' . $this->id
],
];
}
From Laravel 5.7, this works great
use Illuminate\Validation\Rule;
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
Forcing A Unique Rule To Ignore A Given ID:
Test below code:
'email' => 'required|email|unique:users,email_address,'. $id .'ID'
Where ID is the primary id of the table
If i understand what you want:
'email' => 'required|email|unique:users,email_address,'. $id .''
In model update method, for exemple, should receive the $id with parameter.
Sorry my bad english.
Here is the solution:
For Update:
public function controllerName(Request $request, $id)
{
$this->validate($request, [
"form_field_name" => 'required|unique:db_table_name,db_table_column_name,'.$id
]);
// the rest code
}
That's it. Happy Coding :)
The Best Option is here try just once no need more code when unique validation on updating data
'email' => 'unique:users,email_address,' . $userId,
hereemailis field name and users is table name and email_address is table attribute name which you want unique and $userid is updating row id
public function rules()
{
switch($this->method())
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'name' => 'required|unique:permissions|max:255',
'display_name' => 'required',
];
}
case 'PUT':
case 'PATCH':
{
return [
'name' => 'unique:permissions,name,'.$this->get('id').'|max:255',
'display_name' => 'required',
];
}
default:break;
}
}
an even simpler solution tested with version 5.2
in your model
// validator rules
public static $rules = array(
...
'email_address' => 'email|required|unique:users,id'
);
You can try this.
protected $rules_update = [
'email_address' => 'required|email|unique:users,email_address,'. $this->id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
In Laravel 8.x you can use Rule::unique method as well
Forcing A Unique Rule To Ignore A Given ID:
use Illuminate\Validation\Rule;
public function update(Request $request, Post $post)
{
$validatedData = $request->validate([
'name' => ['required', 'max:60', Rule::unique('posts')->ignore($post->id)],
]);
$post->update($validatedData);
return redirect(route('posts.index'))->with('status', 'post updated successfully');
}
Do One step in controller
Works Fine with Laravel 9
$request->validate([
'name'=>'required|unique:categories,name,'.$id,
]);
If you have a separate rules method. You can use easier the following syntax.
public function rules()
{
return [
'email' => "required|unique:users,email,{$this->id}"
];
}
$rules = [
"email" => "email|unique:users, email, '.$id.', user_id"
];
In Illuminate\Validation\Rules\Unique;
Unique validation will parse string validation to Rule object
Unique validation has pattern: unique:%s,%s,%s,%s,%s'
Corresponding with: table name, column, ignore, id column, format wheres
/**
* Convert the rule to a validation string.
*
* #return string
*/
public function __toString()
{
return rtrim(sprintf('unique:%s,%s,%s,%s,%s',
$this->table,
$this->column,
$this->ignore ?: 'NULL',
$this->idColumn,
$this->formatWheres()
), ',');
}
There is a simple and elegant way to do this. If you are passing the user_id in a body request or through a query parameter.
e.g
/update/profile?user_id=
Then in your request rules
public function rules(Request $request)
{
return [
'first_name' => 'required|string',
'last_name' => 'required|string',
'email' => ['required','email', 'string', Rule::unique('users')->ignore($request->user_id )],
'phone_number' => ['required', 'string', Rule::unique('users')->ignore($request->user_id )],
];
}
Better Still, you can pass in auth->id() in place of $request->user_id to get the login user id.
Found the easiest way, working fine while I am using Laravel 5.2
public function rules()
{
switch ($this->method()) {
case 'PUT':
$rules = [
'name' => 'required|min:3',
'gender' => 'required',
'email' => 'required|email|unique:users,id,:id',
'password' => 'required|min:5',
'password_confirmation' => 'required|min:5|same:password',
];
break;
default:
$rules = [
'name' => 'required|min:3',
'gender' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'password_confirmation' => 'required|min:5|same:password',
];
break;
}
return $rules;
}
i would solve that by doing something like this
public function rules()
{
return [
'name' =>
'required|min:2|max:255|unique:courses,name,'.\Request::get('id'),
];
}
Where you get the id from the request and pass it on the rule
You can also use model classpath, if you don't want to hard code the table name.
function rules(){
return [
'email' => ['required','string',
Rule::unique(User::class,'email')->ignore($this->id)]
];
}
Here $this->id is either 0 or the record Id to be updated.
Use for Laravel 6.0
use Illuminate\Validation\Rule;
public function update(Request $request, $id)
{
// Form validation
$request->validate([
'category_name' => [
'required',
'max:255',
Rule::unique('categories')->ignore($id),
]
]);
}
After researching a lot on this laravel validation topic including unique column, finally got the best approach. Please have a look
In your controller
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class UserController extends Controller
{
public function saveUser(Request $request){
$validator = Validator::make($request->all(),User::rules($request->get('id')),User::$messages);
if($validator->fails()){
return redirect()->back()->withErrors($validator)->withInput();
}
}
}
saveUser method can be called for add/update user record.
In you model
class User extends Model
{
public static function rules($id = null)
{
return [
'email_address' => 'required|email|unique:users,email_address,'.$id,
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
}
public static $messages = [
'email_address.required' => 'Please enter email!',
'email_address.email' => 'Invalid email!',
'email_address.unique' => 'Email already exist!',
...
];
}
This is what I ended up doing. I'm sure there is a more efficient way of doing this but this is what i came up with.
Model/User.php
protected $rules = [
'email_address' => 'sometimes|required|email|unique:users,email_address, {{$id}}',
];
Model/BaseModel.php
public function validate($data, $id = null) {
$rules = $this->$rules_string;
//let's loop through and explode the validation rules
foreach($rules as $keys => $value) {
$validations = explode('|', $value);
foreach($validations as $key=>$value) {
// Seearch for {{$id}} and replace it with $id
$validations[$key] = str_replace('{{$id}}', $id, $value);
}
//Let's create the pipe seperator
$implode = implode("|", $validations);
$rules[$keys] = $implode;
}
....
}
I pass the $user_id to the validation in the controller
Controller/UserController.php
public function update($id) {
.....
$user = User::find($user_id);
if($user->validate($formRequest, $user_id)) {
//validation succcess
}
....
}
While updating any Existing Data Write validator as following:
'email' => ['required','email', Rule::unique('users')->ignore($user->id)]
This will skip/ignore existing user's id's unique value matching for the specific column.
Test below code:
$validator = Validator::make(
array(
'E-mail'=>$request['email'],
),
array(
'E-mail' => 'required|email|unique:users,email,'.$request['id'],
));
Since you will want to ignore the record you are updating when performing an update, you will want to use ignore as mentioned by some others. But I prefer to receive an instance of the User rather then just an ID. This method will also allow you to do the same for other models
Controller
public function update(UserRequest $request, User $user)
{
$user->update($request->all());
return back();
}
UserRequest
public function rules()
{
return [
'email' => [
'required',
\Illuminate\Validation\Rule::unique('users')->ignoreModel($this->route('user')),
],
];
}
update: use ignoreModel in stead of ignore
Very easy to do it ,
Write it at your controller
$this->validate($request,[
'email'=>['required',Rule::unique('yourTableName')->ignore($request->id)]
]);
Note : Rule::unique('yourTableName')->ignore($idParameter) , here $idParameter you can receive from get url also you can get it from hidden field.
Most important is don't forget to import Rule at the top.
If a login user want to update the email then auth() helper function will give us the login user id auth()->user()->id
Laravel helpers#method-auth
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore(auth()->user()->id),
],
]);
if Admin want to change the specific user information from User list then validation will be like this :
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($request->user),
],
Laravel validation#rule-unique
$request object contain the current route related model objects. Which gives the model.
Try dd($request)
Most answers to this question refer to email_address, but in Laravel's inbuilt authentication system, the email field name is just email. Here is an example of validating a unique field, i.e. an email on the update:
Form Requests look like this:
public function rules()
{
return [
'email' => [ 'required','email', Rule::unique('users')->ignore($this->id ?? 0)]];
}
?? 0 If you use this then if hare id does not exist this request will not give you an error
Save
Whenever you access the id property of $this->user, you may encounter an undefined property if you haven't injected the User model into your route. If that is the case, use:
public function rules()
{
return [
'email' => 'required|email|unique:users,email,'.$this->user()->id ?? 0,
];
}
?? 0 If you use this then if hare id does not exist this request will not give you an error
My solution:
$rules = $user->isDirty('email') ? \User::$rules : array_except(\User::$rules, 'email');
Then in validation:
$validator = \Validator::make(\Input::all(), $rules, \User::$messages);
The logic is if the email address in the form is different, we need to validated it, if the email hasn't changed, we don't need to validate, so remove that rule from validation.
For unique rule in the controller - which obviously will be different for the store method and the update method, I usually make a function within the controller for rules which will return an array of rules.
protected function rules($request)
{
$commonRules = [
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6"
];
$uniqueRules = $request->id
//update
? ['email_address' => ['required', 'email', 'unique:users,email' . $request->get('id')]]
//store
: ['email_address' => ['required', 'email', 'unique:users,email']];
return array_merge($commonRules, $uinqueRules);
}
Then in the respective store and update methods
$validatedData = $request->validate($this->rules($request));
This saves from defining two different rule sets for store and update methods.
If you can afford to compromise a bit on readability, it can also be
protected function rules($request)
{
return [
'first_name' => "required",
'last_name' => "required",
'password' => "required|min:6|same:password_confirm",
'password_confirm' => "required:min:6|same:password",
'password_current' => "required:min:6",
'email_address' => ['required', 'email', 'unique:users,email' . $request->id ?: null]
];
}

Categories