Search code examples
frontendshopware

update customer attribute in frontend account profile form


I'm learning Shopware and I got to something I can't figure out how to solve.

I'm writing a test plugin that adds an attribute to the customer. I've added the correspondent field to the Registration form and it saves its value to the db automatically, like I read somewhere in the docs.

Now I wanted to let the attribute be editable in the account profile page, after the password field. I managed to put the input there, and even show the value from the db. But when I change the value and save, the value its not updated. I don't know if it is just a matter of getting the field name right, or do I need to override something else. Or is it just not possible? Any help on how to achieve this would be greatly appreciated.

Relevant code below:

plugin bootstrap

public function install(InstallContext $context)
{
    $service = $this->container->get('shopware_attribute.crud_service');
    $service->update('s_user_attributes', 'test_field', 'string');

    $metaDataCache = Shopware()->Models()->getConfiguration()->getMetadataCacheImpl();
    $metaDataCache->deleteAll();
    Shopware()->Models()->generateAttributeModels(['s_user_attributes']);

    return true;
}

register/personal_fieldset.tpl

{extends file="parent:frontend/register/personal_fieldset.tpl"}

{block name='frontend_register_personal_fieldset_password_description'}
{$smarty.block.parent}

<div class="register--test-field">
    <input autocomplete="section-personal test-field"
           name="register[personal][attribute][testField]"
           type="text"
           placeholder="Test Field"
           id="testfield"
           value="{$form_data.attribute.testField|escape}"
           class="register--field{if $errorFlags.testField} has--error{/if}"
            />
</div>
{/block}

account/profile.tpl

{extends file="parent:frontend/account/profile.tpl"}

{block name='frontend_account_profile_profile_required_info'}
<div class="profile--test-field">
    <input autocomplete="section-personal test-field"
           name="profile[attribute][testfield]"
           type="text"
           placeholder="Test Field"
           id="testfield"
           value="{$sUserData.additional.user.test_field|escape}"
           class="profile--field{if $errorFlags.testField} has--error{/if}"
    />
</div>

{$smarty.block.parent}
{/block}

Solution

  • The form type that it's used on registration isn't the same you have on profile. If you check \Shopware\Bundle\AccountBundle\Form\Account\PersonalFormType::buildForm, you can see

    $builder->add('attribute', AttributeFormType::class, [
                'data_class' => CustomerAttribute::class
            ]);
    

    That means the attributes are included on form and they will be persisted. That's why you can save the value on registration form.

    On profile you have \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType. And here the attribute isn't added to form builder.

    How to extend the ProfileUpdateFormType?

    1. Subscribe Shopware_Form_Builder on Bootstrap (or on a specific Subscriber class)

      $this->subscribeEvent('Shopware_Form_Builder', 'onFormBuild');

    2. Create the method onFormBuild to add your logic

      public function onFormBuild(\Enlight_Event_EventArgs $event) { if ($event->getReference() !== \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType::class) { return; } $builder = $event->getBuilder();

          $builder->add('attribute', AttributeFormType::class, [
              'data_class' => CustomerAttribute::class
          ]);
      }
      

    With this approach all attributes are available on your profile form.

    Other possibility you have is using the 'additional' property instead of 'attribute' and then subscribe a controller event or hook a controller action to handle your custom data.