Problem/Motivation

When registering new user via Commerce Login Pane, the profile shown on the user registration form is created but is assigned UID 0. When registering via user/register everything is correct.

Steps to reproduce

Create New ProfileType shown on the user registration form. Allow to register new user on Checkout Login Pane. Register new account on Checkout. Profile is created but assigned to Anonymous User.

Comments

dunjincan created an issue.

dunjincan’s picture

Update: The problem is the lack of permissions for the user to create profiles. At the same time, there is no setting to Create Only Own Profiles. The user/register form does not care about this and creates the user and then assigns him a profile. But form on Commerce New User Pane checks if the user has permissions and creates a profile but assigns it to user UID 0.

The solution is to grant profile creation privileges to the user.

Suggested solution: Add 'Create Own Profile' permission.

sébastien-fr’s picture

How did you solve the issue ?

You simply grant to you custom profile creation to anonymous user ?

I don't use Commerce Login Pane but a custom form mode used to register user with a particular profile. The form works with Drupal fields. User is created and fields are saved and associated to the new created user. But profile created have UID 0 like you. I'm pretty sure it is the same problem (if I set the correct UID directly in DB, the profile is correct).

drupal_josh’s picture

Permissions didn't have an effect for us.

The issue seems to be that \Drupal\profile\Plugin\Field\FieldWidget\ProfileFormWidget::saveProfiles isn't getting called by \Drupal\Core\Form\FormSubmitter::executeSubmitHandlers.
And even if it did get called, Commerce's form is an instance of \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\MultistepDefault which doesn't implement \Drupal\Core\Entity\EntityFormInterface which this Profile module is expecting since it calls ->getEntity() on this line:

$account = $form_state->getFormObject()->getEntity()

Not sure if this is the Profile module's issue or Commerce's, but it needs fixing either way!

So what I did was add a custom submit handler and just tweak that a little so it works with Commerce's \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\MultistepDefault form object.

This seems to work so far :

use Drupal\Core\Form\FormStateInterface;
use Drupal\profile\Entity\ProfileInterface;

function MYMODULE_form_commerce_checkout_flow_multistep_default_alter(&$form, FormStateInterface $form_state, $form_id)
{
    if ($form['#step_id'] === 'login') {
        // fixes profile uid getting set to 0 for commerce registration form
        $form['#submit'][] = function (array $form, FormStateInterface $form_state) {
            $uid = $form_state->get('logged_in_uid');

            if (!$uid) {
                return;
            }

            $profiles = $form_state->get('profiles');

            foreach ($profiles as $profile) {
                assert($profile instanceof ProfileInterface);
                $profile->setOwnerId($uid);
                $profile->setPublished();
                $profile->save();
            }
        };
    }
grimreaper’s picture

Hi,

I confirm the bug, on Profile 1.11.0. And that it is not a permission problem.

Thanks a lot for comment #4 which guided me to an updated workaround. Because proposition in comment 4 is no more usable.

First serialization of closure: needs to declare a proper function. Then the structure of what is gathered is different. Here is my poc:


function MY_MODULE_form_commerce_checkout_flow_multistep_default_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form['#step_id'] === 'login') {
    // fixes profile uid getting set to 0 for commerce registration form
    $form['#submit'][] = 'foo';
  }
}

function foo (array $form, FormStateInterface $form_state) {
  $uid = $form_state->get('logged_in_uid');

  if (!$uid) {
    return;
  }

  $profiles_by_fields = $form_state->get('profiles');
  foreach ($profiles_by_fields as $profiles_by_field) {
    if (is_array($profiles_by_field)) {
      foreach ($profiles_by_field as $profile) {
        if ($profile instanceof ProfileInterface) {
          $profile->setOwnerId($uid);
          $profile->setPublished();
          $profile->save();
          return;
        }
      }
    }
  }
};

Also, like mentioned in comment 4, in app/modules/contrib/profile/src/Plugin/Field/FieldWidget/ProfileFormWidget.php, saveProfiles is not triggered because in:

  /**
   * Process callback: Adds the widget's submit handler.
   */
  public static function attachSubmit(array $form, FormStateInterface $form_state) {
    $form['actions']['submit']['#submit'][] = [static::class, 'saveProfiles'];
//    $form['register']['#submit'][] = [static::class, 'saveProfiles'];
    return $form;
  }

$form['actions']['submit']['#submit'] does not exist in in commerce checkout, it is 'register', and like mentioned, then you get a fatal error because:

  public static function saveProfiles(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Session\AccountInterface $account */
    $account = $form_state->getFormObject()->getEntity();

This does not apply in the login pane.