Model binding from input form - php

I'm working with Laravel and it seems in examples that they decide to implement validation inside controller, and I don't like it at all. What I want to ask is if there is some kind of bind method that can bind posted input fields to object that I created so that I can make sure my controllers are not messy.
I will try to explain what I want in code, I think it will be much clearer.
What I have
public function postRegister() {
$validation = Validator::make(Input::all(), array(
'email' => 'required|email',
'password' => 'required|min:6',
'name' => 'required|alpha',
'gender' => 'required|in:male,female'
));
if ($validation->fails()) {
Input::flashExcept('password');
return Redirect::to('register')->withErrors($validation)->withInput();
}
// Register user...
}
What I want to have
class UserRegisterDto {
public $email;
public $password;
public $name;
public $gender;
protected $errors;
public function isValid() {
// Validate it here, set errors if there are some
return $validator->isValid();
}
public function getErrors() {
return $this->errors;
}
}
public function postRegister() {
$user = Input::bind('UserRegisterDto'); // This is made-up function, I wonder if something like this exists
if ($user->isValid()) {
// Register user...
}
}

Ardent can help you to keep the controllers clean: https://github.com/laravelbook/ardent
"Ardent models use Laravel's built-in Validator class. Defining validation rules for a model is simple and is typically done in your model class as a static variable"

Related

Conditional validation on Custom form request

on my controller I have:
public function store(ProductRequest $request)
The request:
class ProductRequest extends Request
{
public function rules()
{
return [
'name' => 'required|min:3',
'perTypeTime' => 'sometimes|required',
'per_type_id' => 'required'
];
}
}
I want to change the perTypeTime rule above to be conditional depending on if per_type_id field == 1.
If I initiated the validator in the controller I believe I could do something like the below:
$v = Validator::make($data, [
'per_type_id' => 'required|email'
]);
$v->sometimes('perTypeTime', 'required|max:500', function($input)
{
return $input->per_type_id == 1;
});
Is there a way to do this, while keeping my custom request. I like how this approach keeps the controller cleaner.
I can't seem to access the validator on the request object. Is there a way to specify this condition inside the request itself, or to access the validator from the request object, or is there another way?
You can do that
I want to change the perTypeTime rule above to be conditional depending on if per_type_id field == 1.
within your rules() method in your ProductRequest.
For details see required_if:anotherfield,value in the Laravel documentation validation rules.
public function rules()
{
return [
'name' => 'required|min:3',
'perTypeTime' => 'required_if:per_type_id,1',
'per_type_id' => 'required'
];
}
Laravel 5.3, in your request file you can add:
use Illuminate\Validation\Factory;
...
public function validator(Factory $factory)
{
$validator = $factory->make($this->input(), $this->rules());
$validator->sometimes('your_sometimes_field', 'your_validation_rule', function($input) {
return $input->your_sometimes_field !== null;
});
return $validator;
}
Actually this answer https://stackoverflow.com/a/41842050/3922975 is not the best.
We don't have to replace default validator with our own (because we are not changing anything). In that solution we hope validation factory will always require only two attributes ($this->input(), $this->rules()) which is actually not true even in time of writing.
This is a default validator used by Laravel:
$factory->make(
$this->validationData(),
$this->container->call([$this, 'rules']),
$this->messages(),
$this->attributes()
);
As you can see it is much different from that Artem Verbo used.
Better solution is to create withValidator method in your ProductRequest class:
use Illuminate\Contracts\Validation\Validator;
...
public function withValidator(Validator $validator)
{
$validator->sometimes('your_sometimes_field', 'your_validation_rule', function ($input) {
return $input->your_sometimes_field !== null;
});
}

Laravel custom redirection after validation errors

Can I ask what have I done wrong in my LoginRequest.php where I've set a condition to redirect to a custom login page if there is any sort of error in the login process? I have my codes as below:
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class LoginRequest 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',
'login_password' => 'required'
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'login_password.required' => 'Password cannot be blank'
];
}
public function redirect()
{
return redirect()->route('login');
}
}
The code is supposed to redirect users who login from a nav bar login form to the main login page, if there are any errors, but it doesn't seem to redirect.
if you want to redirect to a specific url, then use protected $redirect
class LoginRequest extends Request
{
protected $redirect = "/login#form1";
// ...
}
or if you want to redirect to a named route, then use $redirectRoute
class LoginRequest extends Request
{
protected $redirectRoute = "session.login";
// ...
}
If you do not want to use the validate method on the request, you may create a validator instance manually using the Validator facade. The make method on the facade generates a new validator instance: Refer to Laravel Validation
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
Found a solutions. All I need to do is to override the initial response from
FormRequest.php
like such and it works like a charm.
public function response(array $errors)
{
// Optionally, send a custom response on authorize failure
// (default is to just redirect to initial page with errors)
//
// Can return a response, a view, a redirect, or whatever else
if ($this->ajax() || $this->wantsJson())
{
return new JsonResponse($errors, 422);
}
return $this->redirector->to('login')
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
This works in Lara 7
Add an anchor to jump to the comment form if validation fails
protected function getRedirectUrl()
{
return parent::getRedirectUrl() . '#comment-form';
}
If you are using the validate() method on the Controller
$this->validate($request, $rules);
then you can overwrite the buildFailedValidationResponse from the ValidatesRequests trait present on the base Controller you extend.
Something along this line:
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->route('login');
}
Variations on this answer have already been offered, but overriding the getRedirectUrl() method in a custom request can enable you to define the route parameters, rather than just the name that the $redirectRoute property offers.

Unset child object static variable from abstract parent class php

I am using Laravel and it's Validators.
I have the following code in my controller:
class ResellerController extends BaseController{
public function add() {
//some code before
$userValidator = new App\Services\Validators\UserCreateValidator();
//HERE I WANT TO REMOVE THE company KEY FROM THE RULES IN THE USERS CREATE VALIDATOR
$userValidator->removeRule('company');
//code execution continues
}
}
The UserCreateValidator extends a parent Validator class:
namespace App\Services\Validators;
class UserCreateValidator extends Validator {
public static $rules = array(
'firstName' => 'required',
'lastName' => 'required',
'email' => 'required|email|unique:users',
'company' => 'required'
);
}
And here is the base Validator class:
namespace App\Services\Validators;
abstract class Validator {
/**
* Validation rules
* #var array
*/
public static $rules;
//THIS CODE DOES NOT WORK IN THE CONTROLLER UP
public static function removeRule($ruleKey){
if(is_array($ruleKey))
{
foreach($ruleKey as $key)
{
if(!array_key_exists($key, static::$rules)) continue;
unset(static::$rules[$key]);
}
return true;
}
if(!array_key_exists($ruleKey, static::$rules)) //return false;
unset(static::$rules['company']);
return true;
}
}
The unsettting of the static::$rules[$key] in ResellerController does not work.
I can see in a XDEBUG session (after this line gets executed) that the static::$rules['company'] is still present in the UserCreateValidator as property.
I thought that Late Static Binding should solve this problem?
What is wrong?
The problem is solved. It was in the commented part in the:
if(!array_key_exists($ruleKey, static::$rules)) //return false;
The unsetting is working fine after I uncomment the return false.
Silly mistake :)

PhpSpec return always null on mocks

I'm working with PhpSpec and for some reason when I mock my dependencies and call them the willReturn method of PhpSpec give me a null value instead of the value passed.
This is the method that I'm trying to describe
/**
* Register an User
*
* #param array $infoUser
* #return User
*/
public function register(array $infoUser)
{
$user = $this->user->create($infoUser);
$this->raise(new UserRegistered($user));
return $user;
}
My Spec
class BaseAuthSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Core\Auth\BaseAuth');
}
function let(AuthManager $guard,UserAuthRepository $user)
{
$this->beConstructedWith($guard,$user);
}
function it_register_an_user(UserAuthRepository $useRepo)
{
$user = [
'username' => 'fabri',
'email' => 'test#test.com',
'password' => 'password',
'repeat_password' => 'password'
];
$userModel = new User($user);
// this line return null instead the $userModel
$useRepo->create($user)->shouldBeCalled()->willReturn($userModel);
$this->raise(new UserRegistered($userModel))->shouldReturn(null);
$this->register($user)->shouldReturn($userModel);
}
}
I'm stuck with this issue, any suggest will be appreciated.
Arguments are matched by name. The user repository passed to your let() method, is not the same as passed to the it_register_an_user() method. To fix your issue, simply give it the same name.
There're other issues in your spec.
It's not possible to mock nor stub a method on the class you're speccing. This is not going to work:
$this->raise(new UserRegistered($userModel))->shouldReturn(null);
I'm not sure what's going on in the raise() method, but you should deal with it properly in your example, so either stub or mock any collaborators (or leave them alone if there's no return values relevant to the current example).
Another thing is that you use mocks when what you really need is stubs. I'd rewrite your example to be:
class BaseAuthSpec extends ObjectBehavior
{
function let(AuthManager $guard, UserAuthRepository $userRepo)
{
$this->beConstructedWith($guard, $user);
}
function it_registers_a_user(UserAuthRepository $userRepo, User $userModel)
{
$user = [
'username' => 'fabri',
'email' => 'test#test.com',
'password' => 'password',
'repeat_password' => 'password'
];
$userRepo->create($user)->willReturn($userModel);
$this->register($user)->shouldReturn($userModel);
}
}
The raise method should be covered by seperate examples.

Proper implementation for multiple validation

Here's my code:
I'm trying to implement it on ServiceProvider but I don't have any luck.
//Contact.php
class Contact extends \Eloquent {
protected $fillable = array('email', 'name', 'subject', 'msg');
public static $rules = array(
'email' => 'required|email',
'name' => 'required',
'subject' => 'required',
'msg' => 'required'
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
//Registration .php
class Registration extends \Eloquent {
protected $fillable = array('name', 'address', 'birthdate', 'gender', 'civil_status', 'nationality', 'contact_number', 'email', 'invited');
protected $guarded = array('id');
public static $rules = array(
"name" => "required|alpha_spaces",
"address" => "required",
"contact_number" => "required|numeric",
"email" => "required|email|unique:registrations"
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
class HomeController extends BaseController
{
public function postContactForm()
{
return Contact::validate(Input::all());
}
public function postRegistrationForm()
{
return Registration ::validate(Input::all());
}
}
Is the a way that I can implement it like this?
$this->validate-check(Input::all());
I'm trying to refactor my code and also still new using laravel 4 as well.
Thanks,
Aldren,
I think a Service Provider is a bit overkill for this task. You can create something like a Validation Service. Let me explain:
Say you put your custom files under app/src and use composer to autoload the classes there.
Create an abstract Validator class. This way you can extend this class for every model you need to validate:
<?php namespace Foo\Services\Validation;
abstract class Validator {
protected $errors;
public function check($validator)
{
if ($validator->fails()) {
$this->errors = $validator->messages();
return false;
}
return true;
}
public function isValidForCreation($input)
{
$validator = \Validator::make($input, static::$insertRules);
return $this->check($validator);
}
public function isValidForUpdate($input)
{
$validator = \Validator::make($input, static::$updateRules);
return $this->check($validator);
}
public function errors()
{
return $this->errors;
}
}
Now, lets say you want to validate your Contact model input, right ? You can then create a ContactValidator class that extends our Validator abstract class:
<?php namespace Foo\Services\Validation;
class ContactValidator extends Validator
{
static $insertRules = [
'name' => 'required'
];
static $updateRules = [
'name' => 'required'
];
}
All right, so now we have our boilerplate done. Now lets go to ContactsController to implement our new ContactValidator.
First of all, we need to inject our validator inside the controller. IMHO the best way to do it is in the controllers constructor.
So, lets go:
<?php
use Foo\Services\Validation\ContactValidator as Validator;
class ContactsController extends \BaseController {
protected $validator;
function __construct(Validator $validator)
{
$this->validator = $validator;
}
Great! Now we have it injected. Next, we have to make sure our ContactValidator is invoked when I try to store a Contact. Lets say your method is called store().
public function store()
{
if(!$this->validator->isValidForCreation(Input::all()))
{
return Redirect::back()->withErrors($this->validator->errors())->withInput();
}
else
{
//store your data here.
}
}
You can use either $this->validator->isValidForCreation or $this->validator->isValidForUpdate to check your input against the Validator Service.
I hope you can understand everything and if you have any doubts please let me know.
Cheers and good coding :D
Thanks for the input GustavoR, I get what you want to explain right here. But is it possible to implement the $this->validator in one controller?
use Foo\Services\Validation\MainValidator as Validator;
class HomeController extends BaseController
{
protected $validator;
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
public function postContactForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
public function postRegistrationForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
}
Again, thanks for the input :)

Categories