/** * Merge second user into first one and returns it * @param User $userByEmail user to be kept * @param User $userByIdentity user to be deleted * @param Identity $identity identity to be moved from the deleted user to kept user * @return User */ private function mergeUser(User $userByEmail, User $userByIdentity, Identity $identity) { // Gather all references to the user that we are going to delete $rsm = new \Doctrine\ORM\Query\ResultSetMapping(); $rsm->addScalarResult('TABLE_NAME', 'TABLE_NAME'); $rsm->addScalarResult('COLUMN_NAME', 'COLUMN_NAME'); $qb = $this->getEntityManager()->createNativeQuery("\n SELECT TABLE_NAME, COLUMN_NAME\n FROM `information_schema`.`KEY_COLUMN_USAGE`\n WHERE\n REFERENCED_TABLE_SCHEMA = :database\n AND REFERENCED_TABLE_NAME = 'user'\n AND REFERENCED_COLUMN_NAME = 'id'", $rsm); $database = $this->getEntityManager()->getConnection()->getDatabase(); $qb->setParameters(['database' => $database]); $records = $qb->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY); // Update all references from the old user to the user we're going to keep foreach ($records as $r) { $table = $r['TABLE_NAME']; $field = $r['COLUMN_NAME']; $query = "UPDATE `{$table}` SET `{$field}` = :newUser WHERE `{$field}` = :oldUser"; $this->getEntityManager()->getConnection()->executeUpdate($query, ['newUser' => $userByEmail->getId(), 'oldUser' => $userByIdentity->getId()]); } // This is not strictly necessary since the DB just has been update a // few lines before, but just to keep the model in memory up to date we do it "again" $identity->setUser($userByEmail); // Delete the duplicated user $this->getEntityManager()->remove($userByIdentity); return $userByEmail; }
/** * Create or update a user according to its social identity (coming from Facebook, Google, etc.) * @param string $provider * @param \Hybridauth\User\Profile $profile * @return User */ public function createOrUpdate($provider, Profile $profile) { // First, look for pre-existing identity $identityRepository = $this->getEntityManager()->getRepository(\Application\Model\Identity::class); $identity = $identityRepository->findOneBy(['provider' => $provider, 'providerId' => $profile->identifier]); $userByIdentity = $identity ? $identity->getUser() : null; $userByEmail = $profile->email ? $this->findOneByEmail($profile->email) : null; if ($userByIdentity && $userByEmail && $userByEmail !== $userByIdentity) { $user = $this->mergeUser($userByEmail, $userByIdentity, $identity); } elseif ($userByIdentity) { $user = $userByIdentity; } else { $user = $userByEmail; } // If we still couldn't find a user yet, create a brand new one if (!$user) { $user = new User(); $this->getEntityManager()->persist($user); $country = $this->getEntityManager()->getRepository(\Application\Model\Country::class)->findOneByCode($profile->country); $user->fromProfile($profile, $country); } // Also create an identity if we couldn't find one at the beginning if (!$identity) { $identity = new \Application\Model\Identity(); $identity->setUser($user); $identity->setProvider($provider); $identity->setProviderId($profile->identifier); $this->getEntityManager()->persist($identity); } // Mark as logged in $user->setLastLogin(new \DateTimeImmutable()); if (!$user->getFirstLogin()) { $user->setFirstLogin(new \DateTimeImmutable()); } return $user; }