1
votes

So I've been trying to get an edit user functionality working in my app, and I'm a little confused as to how to go about doing this with CakePHP 3. This is what I've got for my edit action in my UsersController.php:

public function edit() {
    $this->layout = 'dashboard';

    $user = $this->Users->get($this->Auth->user('id'));
    if ($this->request->is(['post', 'put'])) {
      $this->Users->patchEntity($user, $this->request->data);
      if ($this->Users->save($user)) {
        $this->Flash->success(__('Your account has been edited'));
        return $this->redirect(['controller' => 'Users', 'action' => 'edit']);
      }
      $this->Flash->error(__('Your account could not be edited. Please fix errors below.'));
    }
    $this->set(compact('user'));
}

And in my edit.ctp file:

<?php
    $this->Form->templates($form_templates['defaultBootstrap']);
    echo $this->Form->create($user);
?>
<fieldset>
    <legend><?php echo __('Edit Profile'); ?></legend>
    <?php
      echo $this->Form->input('email', [
        'label' => __('Email'),
            'placeholder' => __('Email'),
            'autofocus'
        ]);
        echo $this->Form->input('currency', [
          'label' => __('Default Currency'),
          'options' => [
             'CAD' => 'CAD',
             'USD' => 'USD'
           ]
        ]);
        echo $this->Form->input('password', array(
          'label' => __('Password'),
          'placeholder' => __('Password'),
          'value' => ''
        ));
        echo $this->Form->input('confirm_password', array(
          'label' => __('Confirm Password'),
          'placeholder' => __('Confirm Password'),
          'type' => 'password'
        ));
    ?>
</fieldset>
<?php
    echo $this->Form->submit(__('Edit'));
    echo $this->Form->end();
?>

The problem with this is that the password that gets attached to the form is hashed, so when I use patchEntity, it gets hashed again, because of this in the entity User.php:

protected function _setPassword($password) {
  return (new DefaultPasswordHasher)->hash($password);
}

I've also tried this by not grabbing the password when I set $user in my controller. But then when I use patchEntity it just hashes the blank value instead.

Maybe I'm going about this the entirely wrong way, I'm just looking for some direction on how to tackle this if anyone can help out.

1

1 Answers

2
votes

If you need to have the ability to change the password in the edit form, then you'll have to make sure that it is being dropped before it is being marshalled in case no data is being provided.

This can be achieved using the Model.beforeMarshal event in your Users table class.

http://book.cakephp.org/3.0/en/orm/saving-data.html#before-marshal

public function beforeMarshal(Event $event, \ArrayObject $data, \ArrayObject $options)
{
    if(isset($data['password']) && empty($data['password'])) {
        unset($data['password']);
    }
}

This is a very basic example, you may want to add some more strict checks, maybe remove whitespaces before testing the value for being empty, etc

You could also separate editing profile data and editing credentials into different actions/views/forms, and then use the fieldList option to restrict the fields that can be marshalled.

http://book.cakephp.org/3.0/en/orm/saving-data.html#avoiding-property-mass-assignment-attacks

Edit profile:

$this->Users->patchEntity($user, $this->request->data, [
    'fieldList' => ['currency']
]);

Edit credentials:

$this->Users->patchEntity($user, $this->request->data, [
    'fieldList' => ['email', 'password']
]);