8768 sujets

Développement web côté serveur, CMS

Bonjour,

J'ai une application avec un espace "Mon Profil" pour utilisateur connecté, je suis en train de mettre en place la possibilité de modifier les informations personnelles, photo et mot de passe. Et je n'utilise pas FosUserBundle.

Voici mon entité User
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields="email", errorPath="/connexion",message="Un autre utilisateur s'est déjà inscrit avec cette adresse email, merci de la modifier")
 * @UniqueEntity(fields="userName", message="Désolé {{ value }} quelqu'un utilise déjà ce nom ")
 * @Vich\Uploadable
 */
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var string $email
     *
     * @ORM\Column(name="email", type="string", length=124, unique=true)
     *? @Assert\NotBlank()
     *? @Assert\Email(message="Veille à renseigner un email valide")
     */
    private $email;

    /**
     *
     * @ORM\Column(type="string", length=64, unique=true)
     *? @Assert\NotBlank()
     */
    private $userName;

     /**
     * @var string
     *
     * @Gedmo\Slug(fields={"userName"})
     *
     * @ORM\Column(type="string", length=255, nullable=false)
     */
    private $slug;

    /*
    *!ajouter le @Assert\Regex ("/^(?=\S*?[a-zA-Z])(?=\S*?[0-9]).{5,}\S/")
    */

    /**
     * @Assert\Regex ("/^(?=\S*?[a-zA-Z])(?=\S*?[0-9]).{5,}\S/"),
     * @ORM\Column(type="string", length=255),
     *? @Assert\NotBlank(groups={"registration"})
     *
     */
    private $password;

    /**
     * @var string le token qui servira lors de l'oubli de mot de passe
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $resetToken;

    /**
     * @Assert\File(mimeTypes={ "image/png", "image/jpeg" }),
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $avatar;

    /**
     * @Vich\UploadableField(mapping="avatar_image", fileNameProperty="avatar")
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $avatarFile;

    /**
     * @ORM\Column(type="boolean")
     */
    private $isActif;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $presentation;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\AppRole", inversedBy="users")
     * @ORM\JoinColumn(nullable=false)
     */
    private $appRole;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\UserCrew", mappedBy="user", orphanRemoval=true)
     */
    private $userCrews;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\IsLike", mappedBy="user")
     */
    private $isLikes;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Statistic", mappedBy="user", orphanRemoval=true)
     */
    private $statistics;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Quizz", mappedBy="author")
     */
    private $quizzs;

    public function __construct()
    {
        $this->userCrews = new ArrayCollection();
        $this->isLikes = new ArrayCollection();
        $this->statistics = new ArrayCollection();
        $this->quizzs = new ArrayCollection();
        $this->createdAt = new \DateTime();

    }

    public function getId() : ? int
    {
        return $this->id;
    }

    public function getUserName() : ? string
    {
        return $this->userName;
    }

    public function setUserName(string $userName) : self
    {
        $this->userName = $userName;

        return $this;
    }

    public function getPassword() : ? string
    {
        return $this->password;
    }

    public function setPassword(string $password) : self
    {
        $this->password = $password;

        return $this;
    }

    public function getEmail() : ? string
    {
        return $this->email;
    }

    public function setEmail(string $email) : self
    {
        $this->email = $email;

        return $this;
    }

    public function getAvatar()
    {
        return $this->avatar;
    }

    /**
     *
     * @param File|UploadedFile $image
     */
    public function setAvatar($avatar)
    {
        $this->avatar = $avatar;
        return $this->avatar;
    }

    public function getIsActif() : ? bool
    {
        return $this->isActif;
    }

    public function setIsActif(bool $isActif) : self
    {
        $this->isActif = $isActif;

        return $this;
    }

    public function getPresentation() : ? string
    {
        return $this->presentation;
    }

    public function setPresentation(? string $presentation) : self
    {
        $this->presentation = $presentation;

        return $this;
    }

    public function getCreatedAt() : ? \DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeInterface $createdAt) : self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getAppRole() : ? AppRole
    {
        return $this->appRole;
    }

    public function setAppRole(? AppRole $appRole) : self
    {
        $this->appRole = $appRole;

        return $this;
    }

    /**
     * @return Collection|UserCrew[]
     */
    public function getUserCrews() : Collection
    {
        return $this->userCrews;
    }

    public function addUserCrew(UserCrew $userCrew) : self
    {
        if (!$this->userCrews->contains($userCrew)) {
            $this->userCrews[] = $userCrew;
            $userCrew->setUser($this);
        }

        return $this;
    }

    public function removeUserCrew(UserCrew $userCrew) : self
    {
        if ($this->userCrews->contains($userCrew)) {
            $this->userCrews->removeElement($userCrew);
            // set the owning side to null (unless already changed)
            if ($userCrew->getUser() === $this) {
                $userCrew->setUser(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|IsLike[]
     */
    public function getIsLikes() : Collection
    {
        return $this->isLikes;
    }

    public function addIsLike(IsLike $isLike) : self
    {
        if (!$this->isLikes->contains($isLike)) {
            $this->isLikes[] = $isLike;
            $isLike->setUser($this);
        }

        return $this;
    }

    public function removeIsLike(IsLike $isLike) : self
    {
        if ($this->isLikes->contains($isLike)) {
            $this->isLikes->removeElement($isLike);
            // set the owning side to null (unless already changed)
            if ($isLike->getUser() === $this) {
                $isLike->setUser(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Statistic[]
     */
    public function getStatistics() : Collection
    {
        return $this->statistics;
    }

    public function addStatistic(Statistic $statistic) : self
    {
        if (!$this->statistics->contains($statistic)) {
            $this->statistics[] = $statistic;
            $statistic->setUser($this);
        }

        return $this;
    }

    public function removeStatistic(Statistic $statistic) : self
    {
        if ($this->statistics->contains($statistic)) {
            $this->statistics->removeElement($statistic);
            // set the owning side to null (unless already changed)
            if ($statistic->getUser() === $this) {
                $statistic->setUser(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Quizz[]
     */
    public function getQuizzs() : Collection
    {
        return $this->quizzs;
    }

    public function addQuizz(Quizz $quizz) : self
    {
        if (!$this->quizzs->contains($quizz)) {
            $this->quizzs[] = $quizz;
            $quizz->setAuthor($this);
        }

        return $this;
    }

    public function removeQuizz(Quizz $quizz) : self
    {
        if ($this->quizzs->contains($quizz)) {
            $this->quizzs->removeElement($quizz);
            // set the owning side to null (unless already changed)
            if ($quizz->getAuthor() === $this) {
                $quizz->setAuthor(null);
            }
        }

        return $this;
    }

    //implémentation de UserInterface => à modifier lorsqu'on mettra en place les différents ROLES
    public function eraseCredentials()
    {
    }

    public function getSalt()
    {
    }

    public function getRoles()
    {
        return [$this->appRole->getCode()];
    }

    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->userName,
            $this->password,
            // see section on salt below
            // $this->salt,
        ));
    }

    public function unserialize($serialized)
    {
        list(
            $this->id,
            $this->userName,
            $this->password,
            // see section on salt below
            // $this->salt
        ) = unserialize($serialized, array('allowed_classes' => false));
    }


    public function setAvatarFile(? File $avatar = null) : void
    {
        $this->avatarFile = $avatar;

        if (null !== $avatar) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updatedAt = new \DateTimeImmutable();
        }
    }


    public function getAvatarFile() : ? File
    {
        return $this->avatarFile;
    }

    public function __toString()
    {
        return $this->getUserName();
    }

    /**
     * @return string
     */
    public function getResetToken(): string
    {
        return $this->resetToken;
    }

    /**
     * @param string $resetToken
     */
    public function setResetToken(?string $resetToken): void
    {
        $this->resetToken = $resetToken;
    }

    /**
     * Get the value of slug
     *
     * @return  string
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /**
     * Set the value of slug
     *
     * @param  string  $slug
     *
     * @return  self
     */
    public function setSlug(string $slug)
    {
        $this->slug = $slug;

        return $this;
    }
}


Ensuite j'ai mon AccountController pour la modif

<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\AccountType;
use App\Service\FileUploader;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class AccountController extends AbstractController
{
    /**
     * @Route("/mon-compte/profil", name="account_profile")
     */
    public function profileEdit(Request $request, ObjectManager $manager, FileUploader $fu) : Response
    {
        $user = $this->getUser();

        $form = $this->createForm(AccountType::class, $user);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            //dump($form->getData());exit;
            if ($user->getAvatar() !== null) {
                $file = $user->getAvatar();
                $user->setAvatarFile($file);
            }

            $manager->persist($user);
            $manager->flush();

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

        return $this->render('user/profileEdit.html.twig', [
            'form'=> $form->createView()
        ]);
    }
}



Et j'ai fait un FormType pour la modif des infos personnelles ainsi que la photo
<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AccountType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email')
            ->add('userName')
            ->add('avatar', null, [
                'data_class' => null,
            ])
            ->add('presentation')
            ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}


Si je change la photo et les infos cela marche bien. Cela ne marche plus du moment où je ne souhaite pas modifier la photo, je souhaite garder la photo de base, là j'ai une belle erreur :
upload/1549050703-72266-capturedu2019-02-0120-49-50.png

Alors est-ce dû à VichUploader, je tourne en rond, j'essaye de trouver des solutions mais pas évident... je câle
Quelqu'un aurait une piste ?

Merci.
Alors j'ai commencé à trouver un début de solution mais ce n'est pas encore cela.
J'ai modifié dans l'entité User le fait que avatarFile était mappé donc cela s'enregistrait en BDD avec deux champs différents avatar et avatarFile :
Voici la modification :

/**
     * @Assert\File(mimeTypes={ "image/png", "image/jpeg" }),
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $avatar;

    /**
     * @Vich\UploadableField(mapping="avatar_image", fileNameProperty="avatar")
     */
    private $avatarFile;


Ensuite dans le User j'ai bien aussi la partie à renseigner comme dans la documentation de VichUploader :
public function setAvatarFile(? File $avatar = null) : void
    {
        $this->avatarFile = $avatar;
 
        if (null !== $avatar) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updatedAt = new \DateTimeImmutable();
        }
    }
 
 
 public function getAvatarFile() : ? File
 {
        return $this->avatarFile;
 }
 
 
/*****************************************/
 
 public function getAvatar()
    {
        return $this->avatar;
    }
 
    /**
     *
     * @param File|UploadedFile $image
     */
    public function setAvatar($avatar)
    {
        $this->avatar = $avatar;
        return $this->avatar;
    }


Dans la partie AccountController pour l'édition du profil j'ai ceci qui marche en partie :
/**
     * @Route("/mon-compte/profil", name="account_profile")
     */
    public function profileEdit(Request $request, ObjectManager $manager, FileUploader $fu) : Response
    {
        $user = $this->getUser();
        //dump($user);exit;

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

        if ($form->isSubmitted() && $form->isValid()) {

            if ($user->getAvatar() !== null) {
                $file = $user->getAvatar();
                $fileAvatar = $user->getAvatarFile();
                $fileName = md5(uniqid()) . "." . $file->guessExtension();
                $file->move($this->getParameter('avatar_directory'), $fileName);
                $user->setAvatar($fileName);
                //$user->setAvatarFile($file);
            }

            $manager->persist($user);
            //dump($form->getData());exit;
            $manager->flush();

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

        return $this->render('user/profileEdit.html.twig', [
            'form'=> $form->createView(),
            'user'=>$user
        ]);
    }


Là où cela bloque c'est si je veux faire une modification sur le profil sans changer la photo, je voudrais garder celle qu'il y a déjà, et bien ça ne marche pas. Les modifications sur les infos s'enregistrent bien mais il n'y plus de photo de profil.
Pourtant dans mon dump lors je suis sur le formulaire d'édition je récupère bien le user courant avec ces infos et son avatar.

Une idée de comment je pourrais arriver à solutionner cette erreur ?
Après quelques what the ***** ck !!!! J'ai enfin réussi !!!

Je vous mets la solution que j'ai trouvé au cas où ça puisse servir à quelqu'un on ne sait jamais.

Voici le AccountController rectifié :
<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\AccountType;
use App\Service\FileUploader;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Repository\UserRepository;
use Symfony\Component\HttpFoundation\File\File;

class AccountController extends AbstractController
{
    /**
     * @Route("/mon-compte/profil", name="account_profile")
     */
    public function profileEdit(Request $request, ObjectManager $manager, UserRepository $user) : Response
    {
        $user = $this->getUser();
        $currentAvatar = $user->getAvatar();

        if(!empty($currentAvatar)){

            $avatarPath = ($this->getParameter('avatar_directory') . DIRECTORY_SEPARATOR . $user->getAvatar());
            //$user->setAvatar(new File($avatarPath));
        }

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

        dump($currentAvatar);
        if ($form->isSubmitted() && $form->isValid()) {

            $avatar = $user->getAvatar();

            if(!is_null($avatar)){
                $file = $user->getAvatar();
                $fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();

                $file->move($this->getParameter('avatar_directory'), $fileName);
                $user->setAvatar($fileName);

            } else{
                $user->setAvatar($currentAvatar);
            }

            $manager->persist($user);
            $manager->flush();
            return $this->redirectToRoute('home');
        }

        return $this->render('user/profileEdit.html.twig', [
            'form'=> $form->createView(),
            'user'=>$user,
            'avatar'=>$currentAvatar
        ]);
    }

    /**
     * @return string
     */
    private function generateUniqueFileName()
    {
        // md5() reduces the similarity of the file names generated by
        // uniqid(), which is based on timestamps
        return md5(uniqid());
    }
}


Le AccountType

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;

class AccountType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email')
            ->add('userName')
            ->add('avatar', FileType::class, [
                'data_class' => null,
                'required'=>null
            ])
            ->add('presentation')
            ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}


Et pour finir la vue Twig

{% extends 'base.html.twig' %}
{% block title %}Modification du profil utilisateur{% endblock %}
{% block body %}
    <div class="container bubble mt-3">
        <div class="container">
        <h2 class="bubble-title">Modification de ton Profil
        <br/>
        {{ user.username }}</h2>
    </div>
    {% if user.avatar %}
    <img alt="photo de {{ user.username }}" class=" brad" src=" {{  vich_uploader_asset(app.user, 'avatarFile') | imagine_filter('medium_square') }}">
    {% endif %}
    {{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }}
    {{ form_widget(form) }}
    <div class="form-group text-center">
        <button class="btn btn-lg btn-success mt-2 ml-0" type="submit">Enregistrer les modifications !</button>
        {{ form_end(form) }}
        </div>
    </div>
{% endblock %}



Tout fonctionne nickel, après quelques recherches et prises de tête cela soulage d'avoir trouver.
Donc au cas où si ça peut servir à quelques personnes !
Merci ! Smiley cligne