event für kundendaten änderung?

wir wollen alle kundenadressen vor speichern nach unserem regeln prüfen.

gibts es ein generelles backend event für “kunde hat neue addressdaten eingegeben”?

bzw. wo überall müssen wir events/hooks anhängen um alle address änderungen zu prüfen?

ok then, down the rabbit hole we go…

note: this is on shopware 5.2.27

we should first findout where all the client-address changes land on submit. with luck we can find one common place, and save duplicating code.

  • /checkout/confirm submit lands in engine/Shopware/Controllers/Frontend/Register.php::saveRegisterAction
  • /account submit lands in engine/Shopware/Controllers/Frontend/Register.php::saveRegisterAction
  • /address/edit/id/128625 submit lands in engine/Shopware/Controllers/Frontend/Address.php::editAction
  • /address/create submit lands in engine/Shopware/Controllers/Frontend/Address.php::createAction

sorry: above looks horrible, but this forum doesnt seem to have tables…

i hope thats all the places where we get new address data from the client. lets see if we can find a place they share, and can event/hook to:

  • $form->isValid() is used by editAction and createAction, but not by saveRegisterAction. dammit!
  • sidenote, i wonder why saveRegisterAction has $registerService = $this->get('shopware_account.register_service'); yet never uses that $registerService

nope, doesnt look like they share anything we could event/hook to, dammit!

as there are no events emitted, we can only hook to all these three functions. well then, lets see how we can get the client address data to our hooks, and give our error messages back to the client…

public function beforeSaveRegisterAction (\Enlight_Hook_HookArgs $args)
{
    // yay, we can get the client data
    $data = $args->getSubject()->Request()->getPost();
    var_dump($data);

    // but we cannot set errors from here, cause parent overwrites them...
    die();
}

so thanks to this code in saveRegisterAction:

$errors = [
    'personal' => $this->getFormErrors($customerForm),
    'billing' => $this->getFormErrors($billingForm),
    'shipping' => [],
];

the only possible function we can hook to in here is getFormErrors. but hey, the first code in getFormErrors is our old friend $form->isValid(). yay! we need to checkout how to extend that…

so the $form is a Symfony\Component\Form\FormInterface and according to composer.json its from symfony version „2.8.17“. so lets follow a new rabbit hole…

also found these when searching „validator“ in shopware forum:

ok, so in theory we simply create a symfony validator constraint and stick it in shopware logic?

almost there!

class FooAddressCheck extends Plugin
{
    public static function getSubscribedEvents ()
    {
        return [
            'Shopware_Form_Builder' => 'onFormBuild',
        ];
    }

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

        $builder = $event->getBuilder();

        $builder->add('zipcode', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [
            'constraints' => [
                new \Symfony\Component\Validator\Constraints\Email(['message' => 'PLZ muss eine E-Mailadresse sein :P'])
            ]
        ]);
    }
}

above test works nicely (at least for client register), but i cant for the life of me get my own validator constraint to work…

gotta do some more poking next week.

YAY! it works. i was loading the constraint validator, not the constraint in my plugin *doh*…

heres a working example:

namespace FooAddressCheck;

use Shopware\Components\Plugin;
use FooAddressCheck\Validator\Constraints\ZipIsNotIsland;

class FooAddressCheck extends Plugin
{
    public static function getSubscribedEvents ()
    {
        return [
            'Shopware_Form_Builder' => 'onFormBuild',
        ];
    }

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

        $builder = $event->getBuilder();

        $builder->add('zipcode', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [
            'constraints' => [
                new ZipIsNotIsland()
            ]
        ]);
    }
}

namespace FooAddressCheck\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

class ZipIsNotIsland extends Constraint
{
    public $message = "what {{ string }} ever";
}

namespace FooAddressCheck\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class ZipIsNotIslandValidator extends ConstraintValidator
{
    public function validate ($value, Constraint $constraint)
    {
        if ($value != "12345") { // TODO: compare to actual list from plugin options
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ string }}', $value)
                ->addViolation()
            ;
        }
    }
}

 

hmm, ich nehme an mit symfony validator contstraint kann man aber nicht die komplette adresse (stasse + plz + ort) prüfen, oder?

we actually can! in the validator:

$data = $this->context->getRoot()->getData();
$city = $data->getCity();
$street = $data->getStreet();
$zipcode = $data->getZipcode();

FWIW, i also found http://symfony.com/doc/2.8/reference/constraints/Callback.html

“The purpose of the Callback constraint is to create completely custom validation rules and to assign any validation errors to specific fields on your object”

sounds like a perfect fit to check complete address (eg. multiple fields: street + zip + city).

but looks like i’m too stupid to implement it, so had to rage quit… if anyone figures out how to use that from a shopware plugin, please do post!

here my fails: