Recently I have been learning Laravel and came accross validator problem that would be solved by using validator rule required_unless from Laravel 5.2:
$validator = Validator::make(
array(
'social_id' => $social_id,
'login_by' => $login_by
), array(
'social_id' => 'required_unless:login_by,manual',
'login_by' => "in:manual,google,facebook, stack_exchange, myspace"
)
);
Problem is that I use Laravel 4.2 and this validation rule is not implemented jet.
Is there any other validating rule I could use or any other way?
If not, how would I write a custom validation rule and where would I put it?
Edit: I could do:
$validator = Validator::make(
array(
'social_id' => $social_id,
'login_by' => $login_by
), array(
'social_id' => 'required_if:login_by,google,facebook, stack_exchange, myspace',
'login_by' => "in:manual,google,facebook, stack_exchange, myspace"
)
);
...but this is just a workaround not an elegant permanent solution.
You can simply extend the Validator with the extend method.
Something like this
Validator::extend('required_unless', function ($attribute, $value, $parameters) {
// Implement your version of required_unless here
});
And even steal a bit of logic from L5.2 here
You can see the doc on extend here
Related
I am using the Silex / Symfony security service and try to implement a automatic login when the specific parameters are passed in the request query.
I've looked into the modules and also search on the internet for a solution and always found something like the following:
$user = (new \Portal\UserProvider($app['databases']['read']))->loadUserByUsername($subscriber_id);
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, $user->getPassword(), 'secured', $user->getRoles());
$app['security.token_storage']->setToken($token);
Unfortunately, this does not work for my app. I don't know whats wrong but the security module keeps redirecting me to /login/ as specified in the registration process:
/**
* Registers the security firewall.
*/
private function registerSecurity()
{
$this->register(new \Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/(login/|terms|imprint|animation|error)',
),
'secured' => array(
'pattern' => '^/',
'form' => array(
'login_path' => '/login/',
'check_path' => '/login_check'
),
'logout' => array(
'logout_path' => '/logout'
),
'users' => $this->share(function () {
return new \Portal\UserProvider($this['databases']['read']);
}),
),
'unsecured' => array(
'anonymous' => true
),
),
'security.encoder.digest' => $this->share(function () {
return new \Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder('sha1', false, 1);
}),
'security.access_rules' => array(
array('^/login', 'ROLE_GUEST'),
),
'security.role_hierarchy' => $this->share(function () {
return array();
})
));
$this->boot();
}
Is there anything I have to consider about
reloading
order of registering the SecurityServiceProvider, SessionServiceProvider
this manual token setting
?
You're using the 'form' authentication provider, but this won't work (or maybe I'm not understanding you correctly?). In order to be able to:
try to implement a automatic login when the specific parameters are passed in the request query
You need to hook into the Security service. In order to do that you have to create a Listener and register it. You'll also need a Provider
This is not an easy path as the security component works with many concepts.
You can see a working example in this repo (which implements an OAuth service)
If your security flow is easy and you don't need roles, you can just use a before middleware (and forget about the security component) like so:
<?php
$app->before(function (Request $request, Application $app) {
$session = $request->getSession();
if (false === $session->get('logged', false)) {
if (null !== $request->get('blah', null)) {
$session->set('logged', true);
}
else {
return new RedirectResponse('/login-url');
}
}
});
You could use Silex's guard. It works well with get Request. And standard form could be use as complement.
In your secured Security.firwall, add the guard parameter :
"guard" => array ("authenticator" => array("app.myauthenticator") )
And create your custom class, to validate login.
Just read the Silex cookbook.
I'm using the dwightwatson/validating package to create validation rules in the model.
I particularly like the custom rulesets you can create for different routes.
Model
protected $rulesets = [
'set_up_all' => [
'headline' => 'required|max:100',
'description' => 'required'
],
'set_up_property' => [
'pets' => 'required'
],
'set_up_room' => [
'residents_gender' => 'required',
'residents_smoker' => 'required'
],
'set_up_roommate' => [
'personal_gender' => 'required',
'personal_smoker' => 'required'
]
];
Controller
$post = new Post(Input::all());
if($post->isValid('set_up_all', false)) {
return 'It passed validation';
} else {
return 'It failed validation';
}
In the above example, it works well in validating against the set_up_all ruleset. Now I would like to combine several rulesets and validate against all of them together.
According to the documentation, the package offers a way to merge rulesets. I just can't figure out how to integrate the example provided into my current flow.
According to the docs, I need to implement this line:
$mergedRules = $post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property');
This was my attempt, but it didn't work:
if($mergedRules->isValid()) { ...
I get the following error:
Call to a member function isValid() on array
I also tried this, but that didn't work either:
if($post->isValid($mergedRules)) { ...
I get the following error:
array_key_exists(): The first argument should be either a string or an integer
Any suggestions on how I would implement the merging rulesets?
From what I can see - mergeRulesets() returns an array of rules.
So if you do this - it might work:
$post = new Post(Input::all());
$post->setRules($post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property'));
if($post->isValid()) {
///
}
I've released an update version of the package for Laravel 4.2 (0.10.7) which now allows you to pass your rules to the isValid() method to validate against them.
$post->isValid($mergedRules);
The other answers will work, but this syntax is nicer (and won't override the existing rules on the model).
I want to validate two date fields in a form which is from_date and end_date. Need to check from_date is less than end_date.
$rules = array('from_date' => array('sometimes','date_format:"Y-m-d"', 'before:'.Input::get('to_date') ),
'to_date' => array('sometimes','date_format:"Y-m-d"', 'after:'.Input::get('from_date') ) );
This is what i tried. But that does not work. If i give the to_date as empty value it will through the error.
I know that it is a question about Laravel 4 BUT if you are using Laravel 5.3 now you can use something like:
$rules = array(
'date_start' => 'required|date_format:Y-m-d|before_or_equal:date_end',
'date_end' => 'required|date_format:Y-m-d|after_or_equal:date_start'
);
$validator = Validator::make($request->all(), $rules);
Please use the below code in laravel 5.2 and it works fine for validating start and end date.
$this->validate($request, [
'start_date' => 'required|before:end_date',
'end_date' => 'required',
]);
Laravel 5:
Here is more extensive approach that follows modern principles and is more Laravel-like. This is a little more complex but still easy to follow and the end results is much cleaner.
Let's start by changing a few things. Let's reduce this to the specific problem, use newer array syntax and apply formatting.
$rules = [
'from_date' => [
'before:'.Input::get('to_date') // This is what we will learn to do
],
'to_date' => [
'after:'.Input::get('from_date') // Do this one on your own
]
];
Now let's create a new Request with php artisan make:request StoreWhateverRequest. This will create the App/HTTP/Request/StoreWhateverRequest.php file. Open that and place your rules in the return array of the rules() function.
return [
'from_date' => 'date',
'to_date' => 'date|after_field:from_date'
];
This will not work yet because after_field isn't available to use yet. Let's create that. We need a new class that extends validator. You can place it in app/Services. We need something similar to:
<?php namespace App\Services;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
class AfterFieldValidator extends Validator {
public function validateAfterField($attribute, $value, $parameters)
{
return Carbon::parse($value) > Carbon::parse($this->data[$parameters[0]]);
}
}
In the above we have: $attribute which is the name of the field we are checking (to_date), $value is the value of the field we are checking and $parameters is the parameters we passed to the Validator(from_date) seen in 'to_date' => 'date|afterField:from_date'. We also need the other data fields passed to the Validator, we can get these with $this->data. Then we just have to preform the logic appropriately. You really don't even need Carbon here but be sure to parse the string so we don't do string comparison.
Now we need to load this into the application. To do this put the below code inside the boot() function in app/Providers/AppServiceProviders.php.
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new afterFieldValidator($translator, $data, $rules, $messages);
});
The final step is easiest. Just inject and instance of StoreWhateverRequest into our Controller.
...
public function store(StoreWhateverRequest $request)
{
...
All done. I feel this is a pretty SOLID way to solve the problem.
Just came across this and thought I'd share an answer I found: Compare attributes in validation
Basically, you'd create a new Validator that extends Illuminate\Validation\Validator and write a custom rule in there:
public function validateEndAfter($attribute, $value, $parameters) {
$start_date = $this->getValue($parameters[0]); // get the value of the parameter (start_date)
return (strtotime($value) > strtotime($start_date));
}
then in your validator use the new rule:
$rules = [
'start_date' => 'required',
'end_date'=> 'required|end_after:start_date',
]
Anyhow,
I did as like this. So that even if any date in the form is empty that will auto fill and check the validation
$inputs = Input::all();
if(!empty($inputs))
{
$default_date_arr = array('from_date' => date('Y-m-d', strtotime('-1 days')), 'to_date' => date('Y-m-d'));
$inputs = $inputs+$default_date_arr;
}
$rules = array('from_date' => array('sometimes','date_format:"Y-m-d"', 'before:'.$to_date) ,
'to_date' => array('sometimes','date_format:"Y-m-d"', 'after:'.$from_date ) );
$validator = Validator::make($inputs,$rules);
if($validator->fails())
{ ... }
This may not be the answer for what i asked. But i needed just a way to finish this. May be will helpful for others.
I am trying to write a simple validation rule in laravel framework.
$rules = array(
'from_account' => 'required',
'to_account' => 'required|same:from_account',
'amount' => 'required|numeric',
'description' => 'required'
);
Now as you can see the validation rule same:from_account will check and must require to be to_account exactly same as from_account I am looking to validate for exactly opposite, so that, to_account can't be same as from_account.
Is there any way to tell this negation check inside the rule or do I have to check it manually?
Why not check the docs here.
And use this:
'to_account' => 'required|different:from_account',
or even (for funzies):
'from_account' => 'required|different:to_account',
'to_account' => 'required|different:from_account',
Use the different rule:
$rules = array(
'from_account' => 'required',
'to_account' => 'required|different:from_account',
);
How can I configure Zend Framework 2 route to only to allow POST request type?
I've gone through the documentation but no avail.
EDIT
Here is the portion of my DI code
'cv-create' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/profile/cv/:type/create',
'defaults' => array(
'controller' => 'Application\Controller\ProfileController',
'action' => 'cv_create',
),
),
),
My actual question is, is there any way to inject the request control as a constraint to this route?
You could extend the Zend\Mvc\Router\Http\Segment class and create a slightly modified version of match(). Since match() has the $request parameter it should be as simple as follows:
public function match(Request $request, $pathOffset = null)
{
if (!$request->isPost())
{
return null;
}
return parent::match($request, $pathOffset);
}
Then use this modified class instead of the standard Segment in the routing configuration.
An alternative approach could be to use the AbstractRestfulController in this case.
http://framework.zend.com/apidoc/2.1/classes/Zend.Mvc.Controller.AbstractRestfulController.html
Only implement the create() method.
in class Zend_Controller_Request_Http
You can disable the other request type .