A single value can't be persisted (only on test server?!)

Hey there,

I get a really strange error when I try to create a user from a registration object in Flow.
I hope I can explain it well enough.

The problem:
All fields are written to the database except for the field “activation_code” which is persisted as “NULL”.

It must have something to do with the fact that I set this field to the registration object first and after that I get it from the registration to write it to the user.
When I set the activation code to the user directly it is correctly persisted to the database.

Funnily enough I also pass the registration object to send a notification mail (via emitRegistrationSuccessful) which gets and sends the correct (!) activation code.

The strange:
Funnily, this behaviour happens only on my test server an NOT in my local environment (so debugging is rather difficult…).

I have already found out that is has got something to do with the setter of the registration object:
When I omit the registration setter and set the activation code directly to the user object, then everything works (but I want to pass the registration to the notification mail so I need this setter or I have to rewrite my notification).

/**
 * Creates a new user from a registration
 *
 * @param Registration $registration
 * @Flow\Validate(argumentName="registration", type="Acme.MyPackage:Registration")
 */
public function registerAction(Registration $registration) {
   $activationCode = strtolower(Algorithms::generateRandomString(32));
   $registration->setActivationCode($activationCode);

   $this->userFactory->createUserFromRegistration($registration);
   //this is used to send a mail with an activation url
   $this->emitRegistrationSuccessful($registration);

   //...
}

In the user factory:

/**
 * Creates a user from a registration
 *
 * @param Registration $registration
 * @return User
 */
public function createUserFromRegistration(Registration $registration) {
   //creates a new user object and adds it to the respective repository
   $user = $this->createUser($registration->getEmail(), $registration->getPassword());

   //theses fields are saved correctly!
   $user->setSalutation($registration->getSalutation());
   $user->setForename($registration->getForename());
   //..
   //the activation code is persisted as NULL (?!)
   $user->setActivationCode($registration->getActivationCode());
   //this prints the correct values:
   //
   //echo $user->getActivationCode();
   //echo $registration->getActivationCode();    

   return $user;
}

I suppose that it has something to do with the object lifecycle but as my local code works flawlessly I cannot even debug properly.

When I use the Production context on my local machine it works as well.
I have cleared the cache on my test server.
Both machines have PHP 5.6 but different minor versions (local machine 5.6.15, test server 5.6.18).

Any other ideas? Where (which keywords) can I search in the manual?

I still haven’t found a solution for my problem but there are extremely strange things happening:

First issue (see comments in the example code):

public function createUserFromRegistration(Registration $registration) {
    //...

    //when I exchange the two rows to that...
    $user->setCompanyName($registration->getActivationCode());
    $user->setActivationCode($registration->getCompanyName());
    //...both fields "company_name" and "activation_code" are persisted correctly (but exchanged, of course) to the database

    //...
}

Second issue:
By the above mentioned “emitRegistrationSuccessful” I trigger a notification that sends a mail with the confirmation url.

In my mail template I render the link like this:

<f:uri.action action="confirm" controller="account" package="Acme.MyPackage" subpackage="Frontend" arguments="{activationCode: registration.activationCode}">Confirm</f:uri.action>

The oddity:
When I rename “registration.activationCode” to “registration.bla” everything is persisted well (but I miss the confirmation code in my email, of course)…

I don’t understand the connection why the persistence layer is influenced by the access of a variable in an email template
(especially why this happens only on my test server but not in my local environment…).

Has anybody ever had a similar problem?

Hey @katharina_floh, really hard to say what happens there. Did you already check if

actually produces something?

I am not sure what to suggest, looks pretty straight forward to me. I guess I would need even more input to suggest something.

Hey @christianm, thank you for your answer.

The mentioned line (it’s TYPO3\Flow\Utility\Algorithms) generates a random string like this:

oadtnbby5gcgbueecjrho3lum2ywnchf

It’s also strange that this behaviour only happens on my test server and only for this particular property…

I will provide more context, but I don’t see anything that is far from normal.

This is the wiring of the signal slots:

class Package extends BasePackage {

   public function boot(Bootstrap $bootstrap) {
      $this->wireSignalSlots($bootstrap);
   }

   private function wireSignalSlots(Bootstrap $bootstrap) {
      $dispatcher = $bootstrap->getSignalSlotDispatcher();
      $dispatcher->connect('Acme\MyPackage\Frontend\Controller\AccountController', 'registrationSuccessful', 'Acme\MyPackage\Service\Notification', 'notifyCustomerAccountCreated');
   }
}

The notification class:

class Notification {
   /**
    * @Flow\Inject
    * @var AccountMailer
    */
   protected $accountMailer;

   /**
    * Sends all notifications after a successful customer registration
    *
    * @param Registration $registration
    */
   public function notifyCustomerAccountCreated(Registration $registration) {
      $this->accountMailer->sendMailCustomerAccountCreated($registration);
   }
}

The mailer:

class AccountMailer extends Mailer
{
   /**
    * Sends a mail that contains a confirmation url
    *
    * @param Registration $registration
    */
   public function sendMailCustomerAccountCreated(Registration $registration) {

      $view = $this->getView('CustomerAccountCreated');
      $view->assign('registration', $registration);

      $config = array(
         'to' => $registration->getEmail()
      );

      $this->sendMail($config, $view);
   }
}

Perhaps “$this->sendMail()” and “$this->getView()” may also be interesting:

abstract class Mailer {

    //...

    /**
     * Returns a mail view
     *
     * @param $name
     * @return StandaloneView
     */
    protected function getView($name) {
        $view = new StandaloneView();
        $view->setTemplatePathAndFilename('resource://Acme.MyPackage/Private/Templates/Mails/'.$name.'.html');
        return $view;
    }

    /**
     * Sends a mail
     *
     * @param array $config Possible options: see $this->getDefaultConfig()
     * @param StandaloneView|null $view if no view is given setBody() will be used
     * @return int number of recipients who were accepted for delivery
     * @throws \TYPO3\Fluid\View\Exception\InvalidSectionException
     */
    protected function sendMail(array $config, StandaloneView $view = null) {

        $this->overrideConfig($config);

        $mail = $this->getNewMail();

        foreach($this->config as $key => $value) {
            if(!is_null($value)) {
                call_user_func(array($mail, 'set'.ucfirst($key)), $value);
            }
        }

        if(!is_null($view)) {
            $subject = trim($view->renderSection('subject'));
            $html = trim($view->renderSection('html'));
            $plain = trim($view->renderSection('plain'));

            $mail->setSubject($subject);
            $mail->addPart($html,'text/html','utf-8');
            $mail->addPart($plain,'text/plain','utf-8');
        }

        $transport = $this->getTransport();
        $mailer = \Swift_Mailer::newInstance($transport);
        return $mailer->send($mail);
    }
}

The code looks very good, but that also means I am not able to spot any obvious problems. Seems really be a quirky bug.

Gripping straws here but you could try to get a userRepository and update the user after setting all the stuff. Another thing might be to check if the database schema on the other server is the one you would expect and the field is actually able to save the random hash.

I have already tried

$this->userRepository->update($user);

as you suggested but that doesn’t work either.

Persisting the hash manually works without problems, and as mentioned above, when I exchange the setters of companyName and activationCode BOTH values are persisted for whatever reason (just in the wrong columns, of course)…

What I have done now is throwing my registration object away for the confirmation mail and pass the user directly to it. I thought this should work… It doesn’t!

After that I removed this from my controller

$this->emitRegistrationSuccessful($registration);

and replaced it by calling the notification directly:

$this->notification->notifyCustomerAccountCreated($newCustomerUser);

Doesn’t work…

Lastly I changed my template again to use this non-existing variable

<f:uri.action action="confirm" controller="account" package="Acme.MyPackage" subpackage="Frontend" arguments="{activationCode: newCustomerUser.bla}">Confirm</f:uri.action>

and now it’s persisted to my database again (but not available for my mail…). :anguished:

I have found the solution!!!

It hasn’t got anything to do with Flow at all and my code seems to have been completely correct!


Now, this is how it happened:

The activation code actually GETS persisted correctly.
Meanwhile, the confirmation mail is on its way to my in-box and obviously passes some kind of spam filter or whatever which seems to visit the confirmation link in the mail (I could verify that via access.log).
So, by visiting the link my confirmation action is called and the activation code is unset.

Oh my…!

Now my task is to re-implement the confirmation page to include an additional button to be pressed to finally confirm the new account (also to be compliant to the requirement that a GET method should not change any data).

Thanks for your help anyway!

1 Like

\o/ Glad you figured it out, I didn’t really have any idea and your code looked good all along. So I was rather grasping for straws to find an explanation :wink:

Have fun!

That’s a hell of a strange spam filter… Good news: It will automatically unsubscribe you from any newsletter before you get it :wink:

That sounds like a good idea anyways!