Laravel Required Field on Model Update - php

I have a Users system which allows account creation, login and logout procedures.
I'm currently working on an Edit Profile, which allows editing of the full name, username etc.
For this procedure, I would like the password to be not required, however, it needs to be required for the create and login procedures.
Is there a way to support this in Laravel?
I thought about detecting the presence of an Input::get('password') and if not, passing through the original password, however, this would re-hash
So, to summarise, I would like the password field to be required on create, but not on update procedures. Alternatively, if there's another way to achieve the same end, then I'm open to it.
For reference, I'm using JeffreyWay/Laravel-Model-Validators which handles the validation within the save procedure.

Probably you cannot do this using this package. However you can create such functionality manually, for example creating 2 methods that chooses required fields for validation, a piece of User class:
protected static $rules
= [
'name' => 'required|min:3|max:60',
'password' => [
'required',
'min:10'
],
];
public static function validateRegistration($data, $translation)
{
return self::validate($data, $translation);
}
public static function validateUpdate($data, $translation)
{
$rules = self::$rules;
if (trim($data['password']) == '') {
unset($rules['password']); // unsetting unwanted password rule
}
return self::validate($data, $translation, $rules);
}
public static function validate($data, $translation, $rules = null)
{
if (is_null($rules)) {
$rules = self::$rules;
}
$v = Validator::make($data, $rules, $translation);
if ($v->fails()) {
self::$errors = $v->messages()->toArray();
return false;
}
return true;
}

Related

Laravel Custom Validation Method

I'm trying to develop a PHP game with Laravel, and so far a user - with enough gold and not part of a guild - can create a guild using a simple form with one text field. The issue is that currently I'm using Laravel's dd() function in order to show that they failed to have the gold or were already in a guild.
As such, I went looking for a way to give it a more baked-in feel by seeing if I could put this behavior into a custom rule/validator, but I'm unsure as to how to go about this. Examples would be preferred... here's my current function.
public function store(Request $request)
{
$request->validate([
'name' => 'required|min:4|alpha_dash|unique:guilds'
]);
$char = Auth::user()->character;
$cost = config('game.create-guild-cost');
$guild = new Guild;
if($char->gold < $cost) {
dd('Not enough money');
}
if($char->guild != null) {
dd('You cannot already be in a guild.');
}
$guild->name = request('name');
$guild->leader_id = $char->id;
$guild->save();
$char->gold = $char->gold - $cost;
$char->guild_id = $guild->id;
$char->save();
return redirect()->route('guilds.show', ['guild' => $guild]);
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:4|alpha_dash|unique:guilds'
]);
if ($validator->fails()) {
return redirect()
->back() //please double check this but you got the idea
->withErrors($validator)
->withInput();
}
// Do your stuff here....
}
So basically Laravel provides you to put your error messages in session behind the scene and then go to your desired page get the errors from the session and show them nicely in your view files.

Hash password before saving with Laravel Backpacker

A simple question: how do I modify (hash) the request value before saving it with Laravel Backpacker CRUD admin?
As far as i understand, it should be done somewhere before these methods are executed in the crud controller:
public function store(StoreRequest $request)
{
return parent::storeCrud();
}
public function update(UpdateRequest $request)
{
return parent::updateCrud();
}
but I have no idea how to do it correctly.
Edit: the request is not a Request object, but rather StoreRequest or UpdateRequest that looks something like this:
Fix:
public function update(UpdateRequest $request)
{
// Hash password before save
if (!empty($request->password)) {
$request->offsetSet('password', Hash::make($request->password));
}
return parent::updateCrud($request); // <-- Pass the modified request, otherwise the CRUD reads it again from post data
}
You can update $request values using the offsetSet method
$request->offsetSet('name', $newName);
Edit: To update user password you can do something like this:
public function update_password(Request $request)
{
$user = User::find(Auth::user()->id);
if (Hash::check($request->old_password, $user->password)) {
$user->fill([
'password' => Hash::make($request->password)
])->update();
return redirect()->back()->with('message' => 'Your password has been updated.');
}
else {
return redirect()->back()->with('message' => 'The password entered do not match our records.');
}
}
I did not check the code but it should work. Now update it to your needs.
If you're asking about how to modify data in $request variable, you can just do this:
$request->property = 'New value';
Also, you can add data to reuqest itself (not into variable):
request()->request->add(['key' => 'value']);

How can I update mapped fields only when a condition is satisfied?

In my Symfony2 application I created a custom form to edit objects of a User class. Users have a password property which contains a hash of the user's password. For obvious reasons, I do not want to echo this property's value into a field. However, I want a password field on my form so that when editing a user it is possible to change the user's password.
This password field should behave as follows:
If the user has a password set, then the field should contain ********.
If the user has no password set, then the field should be empty.
(It turns out the previous two points are impossible to achieve with my current architecture, so instead I am going for:) When the page is loaded, the field should be empty, regardless of whether the user has a password set.
If the field is posted with content, then the user's password should be set to the hashed value of the field.
If the field is posted empty, the user's password should not be changed and, more importantly, not cleared.
I thought of implementing this with a custom data transformer. However, the data transformer does not provide me with a way to skip updating the user's password property when the password field is posted empty.
Where do I need to extend the framework to add custom logic deciding which fields should be updated?
UPDATE
This is the legacy code I am trying to replace:
/* SomeController.php */
$pass = $request->get('password');
if (strlen($pass) >= 5 && strlen($pass) <= 16) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($pass, $user->getSalt());
$user->setPassword($password);
}
I can live with removing the string length checks. All I want to check is whether something has been entered.
As you can see, I can not simply move this code to a data transformer as it needs to access the $user which is the user we are currently editing. I don't think it is a good idea to create a service providing this value.
Just insert a control directly into your entity method and use data transformer (as you have insight)
So your entity will be
class User
{
//some properties and methods here
public function setPassword($pwd = null) {
if (null !== $pwd) {
$this->password = //do actions here like hash or whatever
}
//don't update the password
}
}
If you want to take advantage only of DataTransformers, you could still do what you need that way
use Symfony\Component\DependencyInjection\ContainerInterface;
class PasswordTransformer implements DataTransformerInterface
{
private $ci;
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
//...
public function reverseTransform($form_password) {
if (!$form_password) {
//return password already stored
return $this->ci->get('security.context')
->getToken()
->getUser()
->getPassword();
}
}
}
Of course you need to inject service_container service into you data transformer (or better, you should inject it into your form type's selector and pass to DataTransformer constructor as follows:
services:
your.bundle.password_selector_type:
class: Your\Bundle\Form\Type\PasswordSelectorType
arguments: ["#security.context"]
tags:
- { name: form.type, alias: password_selector_type }
For the form part, you should take a look a this widget.
http://symfony.com/doc/current/reference/forms/types/repeated.html
It provides an easy way to ask and treat confirmation on a field (it also hide values with stars when you set type to password).
$builder->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password')));
It will check first and second options to be equal. If that's the case then your form will be considered as valid. Otherwise, invalid_message will be displayed and first field will set to the content typed by user while confirmation field (second option) will be emptied.
You can add some logic afterwards like hashing the password to finally persist your entity. (Extracting it in a form handler would be a good practice).
Here is what I came up with for now but I am not happy with the solution as it involves custom form processing in the controller. However, so far this is the only way I have found to make it work.
My form class adds an unmapped field for the user's password:
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('username')
->add('displayName')
->add('password', 'password', ['mapped' => false]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'MyCompany\MyBundle\Entity\User'));
}
public function getName() {
return 'user';
}
}
This field is then processed manually in my controller class:
class UserAdminController extends Controller {
public function editUserAction($userId, Request $request) {
$user = $this->getDoctrine()->getRepository('MyCompanyMyBundle:User')->findOneById($userId);
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if ($form->isValid()) {
$newPassword = $form['password']->getData();
if ($newPassword !== "") {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($newPassword, $user->getSalt());
$user->setPassword($password);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
return $this->render(
"MyCompanyMyBundle:Admin/Management/User:Edit.html.php",
[
"form" => $form->createView()
]
);
}
}

Forms in Yii: how to send data back, if verification has failed

I got this code:
public function actionJoin() {
$user = new RUser;
if (isset($_POST['RUser']))
$user->attributes = $_POST['RUser'];
$this->render('join',
array(
'user' => $user
)
);
}
Which will not yet allow user to register. What I want to know is how to send data back to user. I mean, if user form haven't passed verification I have to send some data back, so there is no need for user to re-enter it again.
I can do so with this:
$user->mail = $_POST['RUser']['mail'];
But it's looks like dropping back to plain PHP and not using powers of the framework here.
Addition. Publishing RUser class, if needed:
class RUser extends CFormModel
{
public $mail;
public $alias;
public function safeAttributes()
{
return array(
'mail', 'alias'
);
}
}
Which version of Yii you use.
In Yii 1.1, there are no safeAttributes. You use the followings,
public function rules()
{
return array(
array('mail, alias', 'safe'),
);
}

Laravel 4 Validation

I use the following rules for validation on creating a new user:
protected $rules= [
'name' => 'required',
'email' => [
'required',
'unique:user',
'email'
]
];
When updating an existing user I use the same ruleset as shown above
but don't want a validation error if the user didn't change his email at all.
I currently resolve this by using the following:
if (!User::changed('email')) {
unset($user->email);
}
It feels like a dirty workaround to me so I was wondering if there are better alternatives.
Also note that the changed method is something I wrote myself. Does anyone know if there
is a native Laravel 4 method for checking whether a model property has changed?
Thanks!
The unique validation rule allows to ignore a given ID, which in your case is the ID of the data set you are updating.
'email' => 'unique:users,email_address,10'
http://four.laravel.com/docs/validation#rule-unique
One approach is to create a validation function in the model and call it with the controller passing in the input, scenario and id (to ignore).
public function validate($input, $scenario, $id = null)
{
$rules = [];
switch($scenario)
{
case 'store':
$rules = [
'name' => 'required|min:5|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:4|confirmed'
];
break;
case 'update';
$rules = [
'name' => 'required|min:5|unique:users' .',name,' . $id,
'email' => 'required|email|unique:users' .',email,' . $id,
'password' => 'min:4|confirmed'
];
break;
}
return Validator::make($input, $rules);
}
Then in the controller:
$input = Input::all();
$validation = $user->validate($input, 'update', $user->id);
if ($validation->fails())
{
// Do stuff
}
else
{
// Validation passes
// Do other stuff
}
As others mentioned, the 3rd parameter of the unique rule specifies an id to ignore. You can add other cases, such as 'login' to reuse the validation function.
Alternatively, Jeffrey Way at Tuts Premium has a great series of lessons in "What's New In Laravel 4" which includes a couple of other approaches to handling validation using services and listeners.
See the documentation on http://four.laravel.com/docs/validation#rule-unique
You can exclude the users own id
protected $rules= [
'name' => 'required',
'email' => [
'required',
'unique:user,email,THE_USERS_USER_ID',
'email'
]
];
As of 2014-01-14, you can use sometimes attribute, I believe Taylor added them 2 days ago to Laravel 4.1
$v = Validator::make($data, array(
'email' => 'sometimes|required|email',
));
sometimes only validate input if it exists. this may or may not suit your exact scenario, if you don't have a default value for insert.
http://laravel.com/docs/validation#conditionally-adding-rules
I handle this sort of thing in my validator function. My validators array is setup as a class variable. I then do something like this:
public function validate()
{
//exclude the current user id from 'unqiue' validators
if( $this->id > 0 )
{
$usernameUnique = 'unique:users,username,'.$this->id;
$emailUnique = 'unique:users,email,'.$this->id;
$apiUnique = 'unique:users,api_key,'.$this->id;
}
else
{
$usernameUnique = 'unique:users,username';
$emailUnique = 'unique:users,email';
$apiUnique = 'unique:users,api_key';
}
$this->validators['username'] = array('required', 'max:32', $usernameUnique);
$this->validators['email'] = array('required', 'max:32', $emailUnique);
$this->validators['api_key'] = array('required', 'max:32', $apiUnique);
$val = Validator::make($this->attributes, $this->validators);
if ($val->fails())
{
throw new ValidationException($val);
}
}
I have solved this by having different rules for update and create on models that need to do so, like Users.
I have a Model class that extends Eloquent, where I define the validation, and then all child models that extend the Model can have have both the $rules and $update_rules defined. If you define only $rules, it will be used both for create and update.
class Model extends Eloquent {
protected $errors;
protected static $rules = array();
protected $validator;
public function __construct(array $attributes = array(), Validator $validator = null) {
parent::__construct($attributes);
$this->validator = $validator ?: \App::make('validator');
}
protected static function boot() {
parent::boot();
# call validatie when createing
static::creating(function($model) {
return $model->validate();
});
# call validatie when updating with $is_update = true param
static::updating(function($model) {
return $model->validate(true);
});
}
public function validate($is_update = false) {
# if we have $update_rules defined in the child model, and save is an update
if ($is_update and isset(static::$update_rules)) {
$v = $this->validator->make($this->attributes, static::$update_rules);
}
else {
$v = $this->validator->make($this->attributes, static::$rules);
}
if ($v->passes()) {
return true;
}
$this->setErrors($v->messages());
return false;
}
protected function setErrors($errors) {
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
public function hasErrors() {
return ! empty($this->errors);
}
}

Categories