Symfony & Doctrine - Complete profile after user registration - symfony

Symfony 4 app, and I am using the FOSUser bundle for my user system at present.
I would like to add a complete your profile feature after the user first registers their account. Perhaps it continues to appear until a condition is met or a time is expired.
To begin with what should I add to my User class to flag the user needs to be redirected to /profile/complete ?
ie. Is one extra field required ... completeProfile with methods setCompleteProfile and hasCompleteProfile?
How can I set a condition or time limit on when the user doesn't need to be flagged for complete profile?
ie. is this done in the controller? what symfony service can be used?

Do you have a $ registredAt and $ hasCompleteProfile property in your Entity App\Entity\User or something else?
You can do like this, in your Controller ...
public function yourController(User $user) {
if (!$user->getHasCompleteProfile()) { // Getters of $hasCompleteProfile inside App\Entity\User
$completeProfileLimt = 4; // In months or days, 4 is just and example
$now = new \Datetime();
$userRegistredDate = $user->getRegistredAt();
$elapsedTime = $now - $userRegistredDate; // You can not do it like this but it's an example
if ($elapsedTime > $completeProfileLimt) {
// Your action
}
// Your Response Return
}
// Your Response Return
}

Related

symfony extra user info to be persisted in session after authentication

I am trying to use a property called lastLoginOnReference in my User entity. This property is not linked to any column in my corresponding db table as I don't want to store it.
I use this property in order to display to the user some activity logs since the last time he logs in.
I also have a lastLoginOn property, also in the User entity, which has a dedicated column in my user table.
what am I doing at authentication is this one (in my GoogleAuthenticator class):
public function getUser($credentials, UserProviderInterface $userProvider)
{
$googleUser = $this->getGoogleClient()
->fetchUserFromToken($credentials);
$email = $googleUser->getEmail();
$user = $this->em->getRepository(User::class)
->findOneBy(['email' => $email]);
if ($user) {
$user->setLastLoginOnReference($user->getLastLoginOn());
$user->setLastLoginOn(new DateTime('now'));
} else {
(not important)
}
$this->em->persist($user);
$this->em->flush();
return $user;
}
so I store in my lastLoginOnReference property the value from the lastLoginOn User property.
I update the lastLoginOn User property in the User repository and it works.
But when in my controller I use
$this->getUser();
getLastLoginOnReference returns null
I read the documentation and found out the user is apparently refreshed between every request. So I suspect my user object to be refreshed and as the lastLoginOnReference not in db, it is set to null
Then I have implemented the Serializable interface in my User entity, specifying this property to be serialized as well but it doesn't change anything.
login/logout are working as expected, and I am using Symfony 5.1

Tow Registration form FosUserBundle Symfony

I work with symfony 2.8 and FOSUserBundle, I have two type of user in the same table in database , And I like to differenciate the registration form in the same page of registration like this :
*
the problem that I can't use two instance of the form in the same page, what can I do please?
The way I would go about this, is override FOSUserBundle and then extend the RegistrationController and likely the corresponding template.
In the registerAction you can reuse some parts of the original, but where the form is created you then create two different ones, maybe like this:
/** #var $formFactory FactoryInterface */
$clientFormFactory = $this->get('client_registration.form.factory');
$clientForm = $clientFormFactory->createForm();
$clientForm->setData($client);
/** #var $formFactory FactoryInterface */
$correspondentFormFactory = $this->get('correspondent_registration.form.factory');
$correspondentForm = $correspondentFormFactory->createForm();
$correspondentForm->setData($correspondent);
$clientForm->handleRequest($request);
$correspondentForm->handleRequest($request);
if ($clientForm->isSubmitted() && $clientForm->isValid()) {
// ...
} elseif ($correspondentForm->isSubmitted() && $correspondentForm->isValid()) {
// ...
}
return $this->render(
'#FOSUser/Registration/register.html.twig',
[
'clientForm' => $clientForm->createView(),
'correspondentForm' => $correspondentForm->createView(),
]
);
The part inside the if conditions will then probably look similar as to the original controller. You might have different UserManager's for each user type, you have to switch out, but other than that it's basically: dispatch pre-event, save user, dispatch post-event, redirect. It is important that you dispatch both events as other parts of FOSUserBundle will rely on them, e.g. sending a registration email.
In your template you then just render both forms in their tab. You might have to fiddle around with the form id's a bit, but that should be straightforward.

Sonata bundle, how can I get current logged in user?

Lets say I have a Car Entity. Other than typical Car properties I have updatedBy attribute
In sonata I created a CRUD admin page using AppBundle\Admin\CarAdmin.php
Inside the class CarAdmin I have the required methods like configureListFields, configureFormFields, etc...
I'm guessing I need to add updatedBy using the method prePersist($object) but I'm facing that $this->getUser() is not available
The question is, how can I get the logged in user to populate updateBy attribute?
You can get logged user using $this->getConfigurationPool()->getContainer()->get('security.context')->getToken()->getUser(). In your case you need to do something like this:
public function setUpdatedByAttribute($car)
{
$user = $this->getConfigurationPool()->getContainer()->get('security.context')->getToken()->getUser();
$car->setUpdatedBy($user);
}
public function prePersist($car)
{
$this->setUpdatedByAttribute($car);
}
public function preUpdate($car)
{
$this->setUpdatedByAttribute($car);
}

How to show symfony roles

I'm working with symfony 2.3. How I can show the user roles without (ROLE_). I want to change the view, but in the database are intact.
When I display the roles in the view I have this
ROLE_ADMIN
ROLE_CONSULTOR
and I want
Admin
Consultor
Role Entities just need to implement the RoleInterface. You can add your own custom fields such as the names you want.
http://api.symfony.com/2.3/Symfony/Component/Security/Core/Role/RoleInterface.html
Not clear what you want to achieve, but if there are few roles, may be 2-3, (e.g. ROLE_ADMIN, ROLE_CONSULTOR, ROLE_USER), just define a method which receives the system role, returns the human readable one. May be like this:
public function convertToHumanreadable($role)
{
$return = null;
switch ($role) {
case 'ROLE_ADMIN':
$return = 'Admin';
break;
...
}
return $return;
}
Or even like this:
public function convertToHumanreadable($role)
{
$roleParts = explode('_', $role)
return ucfirst(strtolower($roleParts[1]));
}
Or you can create your own role entity by implementing the RoleInterface as #DerickF mentioned.

Updating an entity through Doctrine while it's persisted by a backend process

I've got an issue with Doctrine being somehow nasty with automagical tracking of entities and changes. I've got a UserManager which gets data for a new user from a form and sends the data to the backend which creates the corresponding database entries. As the backend is only inserting some basic data like username, I want to persist everything else like a collection of user roles given through the form.
So my method should look like this:
public function create(User $user)
{
$this->createUserInBackend($user);
$this->em->merge($user);
$this->em->persist($user);
$this->em->flush();
}
My issue now is that Doctrine either drops the data from the given $user and replaces it with the database content. This way I lose the chosen user roles. Without the merge() Doctrine tries an INSERT which is clearly not what I want.
I tried everything coming to my mind from fetching a managed copy before the merge, cloning or whatever. In all cases the objects are linked so I lose the data from the form (although their spl_object_hash differ).
Some more simplified details as requested:
class User
{
// Username is tracked by backend
private $username;
// Fullname is only tracked by frontend/Doctrine
private $fullname;
}
Variant 1:
public function create(User $user)
{
// The user entity gets passed to the backend, which does some stuff
// and also inserts the entity in the database.
$this->createUserInBackend($user);
// The entitiy is in the database, but not managed by the EM.
// Therefore Doctrine does an INSERT.
$this->em->persist($user);
$this->em->flush();
}
Variant 2:
public function create(User $user)
{
// The user entity gets passed to the backend, which does some stuff
// and also inserts the entity in the database.
$this->createUserInBackend($user);
// Now there's an entity in the db with ID and username, but not the fullname
$user = $this->em->merge($user);
// The merge finds the entry in the database and refreshes its data.
// This leads to $user->fullname which was given in the form to be emptied. :(
$this->em->persist($user);
$this->em->flush();
}
I also tried to put the return value from merge into an $otherUser variable, but the objects are linked and fullname still gets dropped.
I'd just need something to tell Doctrine that the new entity is managed, but it should not touch its data. I've looked into the underlying UnitOfWork, but couldn't find a trick to solve this.

Resources