I'm using laravel breeze as auth scaffolding package I want to know How can I
create two diffirent registration form for two User Types here is a simple explanation of hwat I want to achieve:
resources/auth/developer :
developer-register.blade.php
resources/auth/designer :
designer-register.blade.php
if the Visitor choose to register as "developer" it will display a diffirent form. and same thing for if the Visitor choose to register as "designer" it will display a diffirent form with fields.
I wish you understand what I want to achieve with this easy explanation.
Ok, so i've not used laravel/breeze myself (yet) but it shouldn't be much different from doing it in standard Laravel!
Views
By default, it looks like the breeze scaffolding is going to hit a create() method on the RegisteredUserController which will return a single view like so:
RegisteredUserController.php
/**
* Display the registration view.
*
* #return \Illuminate\View\View
*/
public function create()
{
return view('auth.register');
}
You have a few options here:
Replace this view with another
Add some logic to change the view which is returned based on the request being made (you can inject a Request object into the route like any other)
public function create(Request $request)
{
if ($request->has('developer')) {
return view('auth.developer-register');
} else {
return view('auth.designer-register');
}
}
Keep the original auth.register view and handle the logic in the blade template.
Registration
The forms on each of your registration pages will have an action that points to a controller route. This will likely be the RegisteredUserController within which you will find a store() method that handles the creation of a User model.
RegisteredUserController.php
/**
* Handle an incoming registration request.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*
* #throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|confirmed|min:8',
]);
Auth::login($user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]));
event(new Registered($user));
return redirect(RouteServiceProvider::HOME);
}
As you can see, this store() method is handling the creation of a User model and then authenticating it before redirecting the user to the home route.
What you could do, is check the request for the the requested user type and then use a switch statement to change the type of use being created.
switch ($request->get('user_type'))
case 'developer':
$user = Developer::create([ /* add details here */ ]);
break;
case 'designer':
$user = Designer::create([ /* add details here */ ]);
break;
Auth::login($user);
I hope this will at least inspire you with your own solution!
i have example object with fields
name => John
surname => Dow
job => engineer
and output form with placeholders. some required, some not.
what is best practice for check if it requred and show error with null fields?
There are multiple ways actually you can do that inside of controller method or make use of Laravels Request Classes for me I prefer to use Request Classes
look below I will list the two examples
Validate inside the controller's method
public function test(Request $request){
if($request->filled('name){
/*filled will check that name is set on the current
request and not empty*/
//Do your logic here
}
}
Second way is by using the Validator Facade inside your controller
use Validator;
class TestController{
public function test(Request $request){
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
/*continue with your logic here if the request failed on
validator test Laravel will automatically redirect back
with errors*/
}
}
Third way my favorite one personally
you can generate a Request class using this command
php artisan make:request AddBookRequest
that will generate the request class under "app/Http/Requests/AddBookRequest" , inside of any generated request class you will find two methods authorize() and rules()
in the authorized method you have to return truthy or falsy value this will detect if the current user making the request has authorization to fire this request inside of the rules method you do pretty much as you did in the Validator in the second way check the example
public function authorize(){
return true;
}
public function rules(){
return [
'title' => 'required|string',
'author_id' => 'required|integer'
];
}
then simply in your controller you can use the generated request like this
use App\Http\Requests\AddBookRequest;
public function store(AddBookRequest $request){
/* do your logic here since we uses a request class if it fails
then redirect back with errors will be automatically returned*/
}
Hope this helps you can read more about validation at
https://laravel.com/docs/5.6/validation
I think "simple is the best", just through object and check if properties exists
Ref: property_exists
Example:
if (property_exists($object, 'name')) {
//...do something for exists property
} else {
//...else
}
I am trying to unit test various custom FormRequest inputs. I found solutions that:
Suggest using the $this->call(…) method and assert the response with the expected value (link to answer). This is overkill, because it creates a direct dependency on Routing and Controllers.
Taylor’s test, from the Laravel Framework found in tests/Foundation/FoundationFormRequestTest.php. There is a lot of mocking and overhead done there.
I am looking for a solution where I can unit test individual field inputs against the rules (independent of other fields in the same request).
Sample FormRequest:
public function rules()
{
return [
'first_name' => 'required|between:2,50|alpha',
'last_name' => 'required|between:2,50|alpha',
'email' => 'required|email|unique:users,email',
'username' => 'required|between:6,50|alpha_num|unique:users,username',
'password' => 'required|between:8,50|alpha_num|confirmed',
];
}
Desired Test:
public function testFirstNameField()
{
// assertFalse, required
// ...
// assertTrue, required
// ...
// assertFalse, between
// ...
}
public function testLastNameField()
{
// ...
}
How can I unit test (assert) each validation rule of every field in isolation and individually?
I found a good solution on Laracast and added some customization to the mix.
The Code
/**
* Test first_name validation rules
*
* #return void
*/
public function test_valid_first_name()
{
$this->assertTrue($this->validateField('first_name', 'jon'));
$this->assertTrue($this->validateField('first_name', 'jo'));
$this->assertFalse($this->validateField('first_name', 'j'));
$this->assertFalse($this->validateField('first_name', ''));
$this->assertFalse($this->validateField('first_name', '1'));
$this->assertFalse($this->validateField('first_name', 'jon1'));
}
/**
* Check a field and value against validation rule
*
* #param string $field
* #param mixed $value
* #return bool
*/
protected function validateField(string $field, $value): bool
{
return $this->validator->make(
[$field => $value],
[$field => $this->rules[$field]]
)->passes();
}
/**
* Set up operations
*
* #return void
*/
public function setUp(): void
{
parent::setUp();
$this->rules = (new UserStoreRequest())->rules();
$this->validator = $this->app['validator'];
}
Update
There is an e2e approach to the same problem. You can POST the data to be checked to the route in question and then see if the response contains session errors.
$response = $this->json('POST',
'/route_in_question',
['first_name' => 'S']
);
$response->assertSessionHasErrors(['first_name']);
I see this question has a lot of views and misconceptions, so I will add my grain of sand to help anyone who still has doubts.
First of all, remember to never test the framework, if you end up doing something similar to the other answers (building or binding a framework core's mock (disregard Facades), then you are doing something wrong related to testing).
So, if you want to test a controller, the always way to go is: Feature test it. NEVER unit test it, not only is cumbersome to unit test it (create a request with data, maybe special requirements) but also instantiate the controller (sometimes it is not new HomeController and done...).
They way to solve the author's problem is to feature test like this (remember, is an example, there are plenty of ways):
Let's say we have this rules:
public function rules()
{
return [
'name' => ['required', 'min:3'],
'username' => ['required', 'min:3', 'unique:users'],
];
}
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HomeControllerTest extends TestCase
{
use RefreshDatabase;
/*
* #dataProvider invalid_fields
*/
public function test_fields_rules($field, $value, $error)
{
// Create fake user already existing for 'unique' rule
User::factory()->create(['username' => 'known_username']);
$response = $this->post('/test', [$field => $value]);
$response->assertSessionHasErrors([$field => $error]);
}
public function invalid_fields()
{
return [
'Null name' => ['name', null, 'The name field is required.'],
'Empty name' => ['name', '', 'The name field is required.'],
'Short name' => ['name', 'ab', 'The name must be at least 3 characters.'],
'Null username' => ['username', null, 'The username field is required.'],
'Empty username' => ['username', '', 'The username field is required.'],
'Short username' => ['username', 'ab', 'The username must be at least 3 characters.'],
'Unique username' => ['username', 'known_username', 'The username has already been taken.'],
];
}
}
And that's it... that is the way of doing this sort of tests... No need to instantiate/mock and bind any framework (Illuminate namespace) class.
I am taking advantage of PHPUnit too, I am using data providers so I don't need to copy paste a test or create a protected/private method that a test will call to "setup" anything... I reuse the test, I just change the input (field, value and expected error).
If you need to test if a view is being displayed, just do $response->assertViewIs('whatever.your.view');, you can also pass a second attribute (but use assertViewHas) to test if the view has a variable in it (and a desired value). Again, no need to instantiate/mock any core class...
Have in consideration this is just a simple example, it can be done a little better (avoid copy pasting some errors messages).
One last important thing: If you unit test this type of things, then, if you change how this is done in the back, you will have to change your unit test (if you have mocked/instantiated core classes). For example, maybe you are now using a FormRequest, but later you switch to other validation method, like a Validator directly, or an API call to other service, so you are not even validating directly in your code. If you do a Feature Test, you will not have to change your unit test code, as it will still receive the same input and give the same output, but if it is a Unit Test, then you are going to change how it works... That is the NO-NO part I am saying about this...
Always look at test as:
Setup minimum stuff (context) for it to begin with:
What is your context to begin with so it has logic ?
Should a user with X username already exist ?
Should I have 3 models created ?
Etc.
Call/execute your desired code:
Send data to your URL (POST/PUT/PATCH/DELETE)
Access a URL (GET)
Execute your Artisan Command
If it is a Unit Test, instantiate your class, and call the desired method.
Assert the result:
Assert the database for changes if you expected them
Assert if the returned value matches what you expected/wanted
Assert if a file changed in any desired way (deletion, update, etc)
Assert whatever you expected to happen
So, you should see tests as a black box. Input -> Output, no need to replicate the middle of it... You could setup some fakes, but not fake everything or the core of it... You could mock it, but I hope you understood what I meant to say, at this point...
Friends, please, make the unit-test properly, after all, it is not only rules you are testing here, the validationData and withValidator functions may be there too.
This is how it should be done:
<?php
namespace Tests\Unit;
use App\Http\Requests\AddressesRequest;
use App\Models\Country;
use Faker\Factory as FakerFactory;
use Illuminate\Routing\Redirector;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;
use function app;
use function str_random;
class AddressesRequestTest extends TestCase
{
public function test_AddressesRequest_empty()
{
try {
//app(AddressesRequest::class);
$request = new AddressesRequest([]);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
} catch (ValidationException $ex) {
}
//\Log::debug(print_r($ex->errors(), true));
$this->assertTrue(isset($ex));
$this->assertTrue(array_key_exists('the_address', $ex->errors()));
$this->assertTrue(array_key_exists('the_address.billing', $ex->errors()));
}
public function test_AddressesRequest_success_billing_only()
{
$faker = FakerFactory::create();
$param = [
'the_address' => [
'billing' => [
'zip' => $faker->postcode,
'phone' => $faker->phoneNumber,
'country_id' => $faker->numberBetween(1, Country::count()),
'state' => $faker->state,
'state_code' => str_random(2),
'city' => $faker->city,
'address' => $faker->buildingNumber . ' ' . $faker->streetName,
'suite' => $faker->secondaryAddress,
]
]
];
try {
//app(AddressesRequest::class);
$request = new AddressesRequest($param);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
} catch (ValidationException $ex) {
}
$this->assertFalse(isset($ex));
}
}
I'm learning Laravel 5 and trying to validate if an email exists in database yet then add some custom message if it fails. I found the After Validation Hook in Laravel's documentation
$validator = Validator::make(...);
$validator->after(function($validator) use ($email) {
if (emailExist($email)) {
$validator->errors()->add('email', 'This email has been used!');
}
});
if ($validator->fails()) {
return redirect('somewhere')
->withErrors($validator);
}
but I don't really understand what this is. Because I can simply do this:
//as above
if (emailExist($email)) {
$validator->errors()->add('email', 'This email has been used!');
}
//redirect as above
It still outputs the same result. When should I use the 1st one to validate something instead of the 2nd one?
The point of the first method is just to keep everything contained inside of that Validator object to make it more reusable.
Yes, in your case it does the exact same thing. But imagine if you wanted to validate multiple items.
foreach ($inputs as $input) {
$validator->setData($input);
if ($validator->fails()) { ... }
}
In your case you will have to add that "if" check into the loop. Now imagine having to run this validation in many different places (multiple controllers, maybe a console script). Now you have this if statement in 3 different files, and next time you go to modify it you have 3x the amount of work, and maybe you forget to change it in one place...
I can't think of many use cases for this but that is the basic idea behind it.
By the way there is a validation rule called exists that will probably handle your emailExist() method
$rules = [
'email' => 'exists:users,email',
];
http://laravel.com/docs/5.1/validation#rule-exists
There may be many scenarios where you may feel it's requirement.
Just assume that you are trying to build REST api for a project. And you have decided that update request method will not have any required rule validation for any field in request (as there maybe many parameters and you do not want to pass them all just to change one column or maybe you do not have all the columns because you aren't allowed access to it) .
So how will you handle this validation in UpdatePostRequest.php class where you have put all the validation rules in rules() method as given in code.
Further more there may be requirement that sum of values of two or more request fields should be greater or less than some threshold quantity. Then what?
I agree that you can just check it in controller and redirect it from there but wouldn't it defeat the purpose of creating a dedicated request class if we were to do these checks in controllers.
What I feel is controllers should be clean and should not have multiple exit points based on validation. These small validation checks can be handled in request class itself by creating a new Rule or extending your own custom validation or creating after validation hooks and all of them have their unique usage in Laravel.
Therefore what you may want to to do here is create a validation hook where it's assigned is to check whether request is empty or not like the example given below
public function withValidator($validator)
{
$validator->after(function ($validator) {
if (empty($this->toArray())) {
$validator->errors()->add('body', 'Request body cannot be empty');
}
if (!$this->validateCaptcha()) {
$validator->errors()->add('g-recaptcha-response', 'invalid');
}
});
}
And here is the full example for it.
<?php
namespace App\Http\Requests\Posts;
use App\Helpers\General\Tables;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdatePostRequest extends FormRequest
{
public function authorize()
{
return auth()->user()->can('update-post', $this);
}
public function rules()
{
return [
'name' => ['string', 'min:3', 'max:255'],
'email' => ['string', 'email', 'min:3', 'max:255'],
'post_data' => ['string', 'min:3', 'max:255'],
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
if (empty($this->toArray())) {
$validator->errors()->add('body', 'Request body cannot be empty');
}
});
}
}
Thanks..
passedValidation method will trigger if the validation passes in FormRequest class. Actually this method is rename of afterValidation method. See: method rename Commit
So you can do like
class RegistrationRequest extends FormRequest
{
/**
* Handle a passed validation attempt.
*
* #return void
*/
protected function passedValidation()
{
$this->merge(
[
'password' => bcrypt($this->password),
]
);
}
}
I've created a simple contact form in Laravel 5, using the Request object and a Validator object to check my input for errors.
The form in my view is coded in HTML, rather than the Laravel Form object, which isn't included by default in Laravel 5.
I need to set up my form so that if a validation rule fails, the user's input is flashed to the session so it doesn't disappear when the page redirects. I was able to accomplish this by putting a $request->flash() in the POST controller, before the validation code.
However, I do not want the data to be flashed (i.e, the form should be reset) if the validation passes and the form is successfully emailed. There's no apparent way for me to accomplish this in the $this->validate block, since Laravel helpfully handles the redirects automatically.
How can I tell Laravel to flash the form data ONLY if there is a validation error?
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class ContactController extends Controller
{
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
return view('contact');
}
/**
* Store a newly created resource in storage.
*
* #param Request $request
* #return Response
*/
public function store(Request $request)
{
// Flash current input in case the validator fails and redirects
$request->flash();
// Validate the form request, redirect on fail
$this->validate($request, [
'name' => 'required',
'email' => 'required|email',
'subject' => 'required',
'message' => 'required|min:5',
]);
// Generate email from template and send
\Mail::send('emails.feedback',
array(
'name' => $request->get('name'),
'email' => $request->get('email'),
'user_message' => $request->get('message'),
'subject' => $request->get('subject')
), function ($message) use ($request) {
$message->from(\Config::get('site.from_email'));
$message->to(\Config::get('site.contact_email'), 'Admin');
$message->subject(\Config::get('site.name') . ': ' . $request->get('subject'));
});
// Redirect to Contact route with success message
return \Redirect::route('contact')
->with('message', 'Thanks for contacting us!');
}
}
?>
If anybody like me didn't know how to get the old values and end up here somehow, here it goes:
<input value="{{ old('var_name') }}">
I don't see you needing to access the session data when Laravel provides you helpers like this.
Hope to have helped, have a nice day. =)
Just remove the following line of code:
$request->flash();
Laravel will take care of that for you by flashing the data on failed validation. The following method gets called on failed validation:
/**
* Create the response for when a request fails validation.
*
* #param \Illuminate\Http\Request $request
* #param array $errors
* #return \Illuminate\Http\Response
*/
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->ajax() || $request->wantsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->to($this->getRedirectUrl())
->withInput($request->input()) // <-- Flashes inputs
->withErrors($errors, $this->errorBag()); // <-- Flashes errors
}
This is the trait used in your controller for validating the request and it's located at Illuminate/Foundation/Validation, name is ValidatesRequests. Check it to clarify yourself.
Alternatively, you may do it manually if you want for any reason, check the documentation.
To restore the old value if validation fails the entered data will display the view
value="{{ (old('title')) ? old('title') : $data->title}}