Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/AppBundle/Controller/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/
public function listAction()
{
return $this->render('user/list.html.twig', ['users' => $this->getDoctrine()->getRepository('AppBundle:User')->findAll()]);
return $this->render('user/list.html.twig', [
'users' => $this->getDoctrine()->getRepository('AppBundle:User')->findAll()
]);
}

/**
Expand All @@ -28,8 +30,11 @@

$form->handleRequest($request);

if ($form->isValid()) {
// Check if the form is submitted and valid before processing
if ($form->isSubmitted() && $form->isValid()) {

Check warning on line 34 in src/AppBundle/Controller/UserController.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/AppBundle/Controller/UserController.php#L34

Implicit true comparisons prohibited; use === TRUE instead
$em = $this->getDoctrine()->getManager();

// Encode the plain text password provided in the form
$password = $this->get('security.password_encoder')->encodePassword($user, $user->getPassword());
$user->setPassword($password);

Expand All @@ -49,21 +54,36 @@
*/
public function editAction(User $user, Request $request)
{
$form = $this->createForm(UserType::class, $user);
// Store the existing hashed password in case the password field is left blank
$oldPassword = $user->getPassword();

$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);

if ($form->isValid()) {
$password = $this->get('security.password_encoder')->encodePassword($user, $user->getPassword());
$user->setPassword($password);
// Check if the form is submitted and valid before processing
if ($form->isSubmitted() && $form->isValid()) {

// Check if a new password has been filled in the form
if (!empty($user->getPassword())) {

Check warning on line 67 in src/AppBundle/Controller/UserController.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/AppBundle/Controller/UserController.php#L67

Operator ! prohibited; use === FALSE instead
// Encode and set the new password
$password = $this->get('security.password_encoder')->encodePassword($user, $user->getPassword());
$user->setPassword($password);
} else {
// Fallback to the original hashed password to prevent overwriting with null
$user->setPassword($oldPassword);
}

// The CallbackTransformer in UserType automatically updates the roles array here
$this->getDoctrine()->getManager()->flush();

$this->addFlash('success', "L'utilisateur a bien été modifié");

return $this->redirectToRoute('user_list');
}

return $this->render('user/edit.html.twig', ['form' => $form->createView(), 'user' => $user]);
return $this->render('user/edit.html.twig', [
'form' => $form->createView(),
'user' => $user
]);
}
}
56 changes: 55 additions & 1 deletion src/AppBundle/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,105 +12,159 @@
* @ORM\Table(name="user")
* @ORM\Entity
* @UniqueEntity(fields="email", message="Cette adresse email est déjà utilisée.")
* @UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé.")
*/
class User implements UserInterface
{
/**
* @var int|null
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string|null
*
* @ORM\Column(type="string", length=25, unique=true)
* @Assert\NotBlank(message="Vous devez saisir un nom d'utilisateur.")
*/
private $username;

/**
* @var string|null
*
* @ORM\Column(type="string", length=64)
*/
private $password;

/**
* @var string|null
*
* @ORM\Column(type="string", length=60, unique=true)
* @Assert\NotBlank(message="Vous devez saisir une adresse email.")
* @Assert\Email(message="Le format de l'adresse n'est pas correcte.")
*/
private $email;

/**
* @var array
*
* @ORM\Column(type="json_array")
*/
private $roles = [];

/**
* @var \Doctrine\Common\Collections\Collection|\AppBundle\Entity\Task[]
*
* @ORM\OneToMany(targetEntity="Task", mappedBy="user")
*/
private $tasks;

/**
* User constructor.
*/
public function __construct()
{
$this->tasks = new ArrayCollection();
}

/**
* @return int|null
*/
public function getId()
{
return $this->id;
}

/**
* @return string|null
*/
public function getUsername()
{
return $this->username;
}

/**
* @param string $username
* @return void
*/
public function setUsername($username)
{
$this->username = $username;
}

/**
* @return string|null
*/
public function getSalt()
{
return null;
}

/**
* @return string|null
*/
public function getPassword()
{
return $this->password;
}

/**
* @param string $password
* @return void
*/
public function setPassword($password)
{
$this->password = $password;
}

/**
* @return string|null
*/
public function getEmail()
{
return $this->email;
}

/**
* @param string $email
* @return void
*/
public function setEmail($email)
{
$this->email = $email;
}

/**
* @return array
*/
public function getRoles()
{
$roles = $this->roles;

// Guarantee every user at least has ROLE_USER
if (empty($roles)) {
if (!in_array('ROLE_USER', $roles, true)) {

Check warning on line 149 in src/AppBundle/Entity/User.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/AppBundle/Entity/User.php#L149

Operator ! prohibited; use === FALSE instead
$roles[] = 'ROLE_USER';
}

return array_unique($roles);
}

/**
* @param array $roles
* @return void
*/
public function setRoles(array $roles)
{
$this->roles = $roles;
}

/**
* @return void
*/
public function eraseCredentials()
{
}
Expand Down
30 changes: 30 additions & 0 deletions src/AppBundle/Form/UserType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\CallbackTransformer;

class UserType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
* @return void
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
Expand All @@ -23,6 +30,29 @@
'second_options' => ['label' => 'Tapez le mot de passe à nouveau'],
])
->add('email', EmailType::class, ['label' => 'Adresse email'])
->add('roles', ChoiceType::class, [
'label' => "Rôle de l'utilisateur",
'choices' => [
'Utilisateur' => 'ROLE_USER',
'Administrateur' => 'ROLE_ADMIN',
],
'required' => true,
'multiple' => false, // Set to false for a single selection (UI choice)
'expanded' => true, // Set to true for radio buttons
])
;

// Model transformer to handle translation between User::$roles (array) and the form widget (string)
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesAsArray) {
// Convert the entity array (e.g. ['ROLE_USER']) to a single string to auto-select the form option
return count($rolesAsArray) ? $rolesAsArray[0] : null;

Check warning on line 50 in src/AppBundle/Form/UserType.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/AppBundle/Form/UserType.php#L50

Implicit true comparisons prohibited; use === TRUE instead
},
function ($roleAsString) {
// Convert the selected string back into an array (e.g. ['ROLE_USER']) to store it in the database
return [$roleAsString];
}
));
}
}
55 changes: 55 additions & 0 deletions tests/AppBundle/Controller/UserControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Tests\AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use AppBundle\Entity\User;

class UserControllerTest extends WebTestCase
{
/**
* Test that an administrator can update another user's role to ROLE_ADMIN.
*
* @return void
*/
public function testAdminCanChangeUserRole()
{
// 1. Simulate logging in as an admin
$client = static::createClient([], [
'PHP_AUTH_USER' => 'Mike',
'PHP_AUTH_PW' => 'password123',
]);

$container = $client->getContainer();
$em = $container->get('doctrine')->getManager();

// 2. Retrieve the user we want to modify (SimpleUser)
/** @var User $userToModify */
$userToModify = $em->getRepository('AppBundle:User')->findOneBy(['username' => 'Mike']);
$this->assertNotNull($userToModify, 'The fixture user "SimpleUser" is missing.');

// 3. Request the edit page
$crawler = $client->request('GET', '/users/' . $userToModify->getId() . '/edit');
$this->assertEquals(200, $client->getResponse()->getStatusCode());

// 4. Select and submit the form, shifting the role to ROLE_ADMIN
$form = $crawler->selectButton('Modifier')->form();

// For choice fields use assignment. If the field accepts multiple values provide an array.
$form['user[roles]'] = 'ROLE_ADMIN';

$client->submit($form);

// 5. Assert successful redirect to the list page
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$client->followRedirect();
$this->assertContains("utilisateur a bien été modifié", $client->getResponse()->getContent());

// 6. Force Doctrine to fetch updated data from the database
$em->clear();
$updatedUser = $em->getRepository('AppBundle:User')->find($userToModify->getId());

// 7. Assert that the role has been successfully modified in DB
$this->assertContains('ROLE_ADMIN', $updatedUser->getRoles());
}
}
37 changes: 37 additions & 0 deletions tests/AppBundle/Entity/UserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Tests\AppBundle\Entity;

use AppBundle\Entity\User;
use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
/**
* Test that a newly created user has at least ROLE_USER by default.
*
* @return void
*/
public function testDefaultRole()
{
$user = new User();

$this->assertContains('ROLE_USER', $user->getRoles());
$this->assertCount(1, $user->getRoles());
}

/**
* Test that setting custom roles updates the array correctly and always includes ROLE_USER.
*
* @return void
*/
public function testSetRoles()
{
$user = new User();
$user->setRoles(['ROLE_ADMIN']);

$this->assertContains('ROLE_ADMIN', $user->getRoles());
$this->assertContains('ROLE_USER', $user->getRoles());
$this->assertCount(2, $user->getRoles());
}
}
Loading