Lifecycle callback postUpdate to send an email in Symfony - php

I'm trying to send an email when the entity is updating (and getVerified), I used the event Suscriber from doctrine, but I don't know exactly how to do that.
My MailEvent.php :
class MailEvent
{
// the entity listener methods receive two arguments:
// the entity instance and the lifecycle event
public function postUpdate(Candidature $candidature, Annonce $annonce, MailerInterface $mailer, LifecycleEventArgs $event): void
{
$change = $event->getEntityChangeSet();
if ($change instanceof Candidature and $candidature->getIsVerified()) {
$email = (new Email())
->from('trtconseilexample#gmail.com')
->to($annonce->getEmail())
->subject('Vous avez un nouveau candidat !')
->text("{$candidature->getNom()} {$candidature->getPrenom()} a postulé à votre annonce, vous trouverez son CV en pièce jointe !")
->attachFromPath("public/uploads/images/{$candidature->getCv()}");
;
$mailer->send($email);
}
}
If someone can help me understand, thank you.
EDIT: Problem solve! Thank you all !
MailEvent.php:
class MailEvent
{
// the entity listener methods receive two arguments:
// the entity instance and the lifecycle event
public function postUpdate(Candidature $candidature, LifecycleEventArgs $event)
{
$request = $this->requestStack->getCurrentRequest();
$entityManager = $this->entityManager;
$annonce = $entityManager->getRepository(Annonce::class)->findOneBy([
'id' => $request->get('id')
]);
if ($candidature->getIsVerified()) {
$email = (new Email())
->from('trtconseilexample#gmail.com')
->to($annonce->getEmail())
->subject('Vous avez un nouveau candidat !')
->text("{$candidature->getNom()} {$candidature->getPrenom()} a postulé à votre annonce, vous trouverez son CV en pièce jointe !")
->attachFromPath("public/uploads/images/{$candidature->getCv()}");
;
$this->mailer->send($email);
}
}
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly MailerInterface $mailer,
private readonly RequestStack $requestStack
){
}
}

You need to make sure you register your listener in your services.yaml
App\EventListener\MailEvent:
tags:
- name: 'doctrine.orm.entity_listener'
event: 'postUpdate'
entity: 'App\Entity\Candidature'
Then you need the postUpdate function inside your listener,
public function postUpdate(Candidature $candidature, LifecycleEventArgs $event): void
{
...
}
It can only take 2 parameters, if you need access to services you need to use the dependency injection in the constructor :
public function __construct(
private readonly AnnonceRepository $annonceRepository,
private readonly MailerInterface $mailer,
) {
}
Then you can use these inside your postUpdate function, I do not know how you get the related Annonce in your project but you could use the repository to go and fetch it from the database.
public function postUpdate(Candidature $candidature, LifecycleEventArgs $event): void
{
//maybe ?
$annonce = $this->annonceRepository->findOneBy["candidature" => $candidature];
//This has to be a Candidature entity, as defined in the services.yaml, so no need to check that, you also do not use the changeset at all so no need for that either
if ($candidature->getIsVerified()) {
$email = (new Email())
->from('trtconseilexample#gmail.com')
->to($annonce->getEmail())
->subject('Vous avez un nouveau candidat !')
->text("{$candidature->getNom()} {$candidature->getPrenom()} a postulé à votre annonce, vous trouverez son CV en pièce jointe !")
->attachFromPath("public/uploads/images/{$candidature->getCv()}");
;
$this->mailer->send($email);
}

Related

Invalid state parameter passed in callback URL. With Oauth2 in prod (symfony)

I have a problem with Oauth2. It returns me as error "Invalid state parameter passed in callback URL."
I'm on symfony 6
I don't know where this can come from, in localhost everything works but in production I have this error.
I looked everywhere for a solution without finding anything.
knpu_oauth2_client.yaml
knpu_oauth2_client:
clients:
azure:
type: azure
client_id: '%env(OAUTH_AZURE_CLIENT_ID)%'
client_secret: '%env(OAUTH_AZURE_CLIENT_SECRET)%'
redirect_route: connect_azure_check
redirect_params: {}
# scope: {}
tenant: '%env(AZURE_TENANT_ID)%'
security.yaml
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
users_in_memory: { memory: null }
my_provider:
entity: {class: App\Entity\User, property: uuid}
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: my_provider
custom_authenticators:
- App\Security\AzureAuthenticator
logout: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/connect/azure, role: PUBLIC_ACCESS }
- { path: ^/, roles: ROLE_USER}
AzureController.php
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class AzureController extends AbstractController
{
/**
* Cette fonction effectue la connexion avec Azure
* Ex: Si vous allez sur cette route, un formulaire microsoft vous demandera de vous connecter
*/
#[Route('/connect/azure', name: 'connect_azure', )]
public function connectAction(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('azure')
->redirect([
'openid', 'profile', 'email'
], []);
}
/**
* Cette fonction permet de savoir si l'authentification à réussi
* Ex: Après vous être connecté ci-dessus, vous serez rediriger sur cette route qui vous redirigera à son tour vers la route home
*/
#[Route('/connect/azure/check', name: 'connect_azure_check', schemes:['http'])]
public function connectCheckAction(Request $request, ClientRegistry $clientRegistry)
{
try {
return $this->redirectToRoute('home');
} catch (IdentityProviderException $e) {
return new JsonResponse(array('status' => false, 'message' => "User not found!", 'error' => $e->getMessage()));
}
}
}
AzureAuthenticator.php
<?php
namespace App\Security;
use App\Entity\User;
use League\OAuth2\Client\Provider\azureUser;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class AzureAuthenticator extends OAuth2Authenticator implements AuthenticationEntryPointInterface
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $entityManager;
private RouterInterface $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
}
/**
* Cette fonction renvoie true alors la fonction authenticate sera appelée
* #param Request $request
* #return bool|null
*/
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'connect_azure_check';
}
/**
* Cette fonction permet de traiter les données et de les utiliser. Elle vérifie également si l'utilisateur est déjà existant en base de donnée, si se n'est pas le cas elle l'ajoute.
* #param Request $request
* #return Passport
*/
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('azure');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
/** #var AzureUser $AzureUser */
$AzureUser = $client->fetchUserFromToken($accessToken);
// 1) have they logged in with Azure before? Easy!
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['uuid' => $AzureUser->getId()]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setUuid($AzureUser->getId());
$user->setNom($AzureUser->claim('family_name'));
$user->setPrenom($AzureUser->claim('given_name'));
$user->setEmail($AzureUser->claim('upn'));
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
/**
* Si l'authentification réussi, l'utilisateur sera renvoyé sur la route home
* #param Request $request
* #param TokenInterface $token
* #param string $firewallName
* #return Response|null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$targetUrl = $this->router->generate('home');
return new RedirectResponse($targetUrl);
}
/**
* Si l'authentification échoue, l'utilisateur sera informé avec un message d'erreur
* #param Request $request
* #param AuthenticationException $exception
* #return Response|null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
/**
* Cette fonction permet de rediriger l'utilisateur sur la route de connexion
* Dès que l'utilisateur sera sur une route qu'il n'a pas le droit d'avoir accès il sera rediriger à cet endroit (Dans le cas de notre application toutes les routes sont par défaut interdite)
* #param Request $request
* #param AuthenticationException|null $authException
* #return Response
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
'/connect/azure',
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
When I add use_state: true in parameter another error is returned: "Authentication failed! Did you authorize our app?"

Cannot use object of type Symfony\Component\HttpFoundation\Session\Session as array (Symfony 5.3.9)

I am trying to use sessions in Symfony version 5.3.9 with RequestStack because SessionInterface is deprecated.
I get the following error:
Cannot use object of type Symfony\Component\HttpFoundation\Session\Session as array
here:
if(isset($cart[$id])){ (in my addToCart function)
in symfony 5.2 it was ok
Thank you for your help
My CartController.php :
<?php
namespace App\Controller;
use App\Services\CartServices;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CartController extends AbstractController
{
/**
* #Route("/cart", name="cart")
*/
public function index(CartServices $cartServices): Response
{
$cartServices->addToCart(3);
dd($cartServices->getCart());
return $this->render('cart/index.html.twig', [
'controller_name' => 'CartController',
]);
}
}
My CartServices.php :
<?php
namespace App\Services;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
class CartServices
{
private $requestStack;
private $repoProduct;
public function __construct(RequestStack $requestStack, ProductRepository $repoProduct)
{
$this->requestStack = $requestStack;
$this->repoProduct = $repoProduct;
}
public function addToCart($id){
$cart = $this->getCart();
if(isset($cart[$id])){
$cart[$id]++;
}else{
$cart[$id] = 1;
}
$this->updateCart($cart);
}
$cart = $this->getCart():
public function getCart(){
return $this->requestStack->getSession('cart', []);
}
Thank you very much but I still have no results
My CartServices.php
<?php
namespace App\Services;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
class CartServices
{
private $requestStack;
private $repoProduct;
public function __construct(RequestStack $requestStack, ProductRepository $repoProduct)
{
$this->requestStack = $requestStack;
$this->repoProduct = $repoProduct;
}
public function addToCart($id){
$cart = $this->getCart();
if(isset($cart[$id])){
//produit déjà dans le panier on incrémente
$cart[$id]++;
}else{
//produit pas encore dans le panier on ajoute
$cart[$id] = 1;
}
$this->updateCart($cart);
}
public function deleteFromCart($id){
$cart = $this->getCart();
//si produit déjà dans le panier
if(isset($cart[$id])){
//si il y a plus d'une fois le produit dans le panier on décrémente
if($cart[$id] >1){
$cart[$id] --;
}else{
//Sinon on supprime
unset($cart[$id]);
}
//on met à jour la session
$this->updateCart($cart);
}
}
public function deleteAllToCart($id){
$cart = $this->getCart();
//si produit(s) déjà dans le panier
if(isset($cart[$id])){
//on supprime
unset($cart[$id]);
}
//on met à jour la session
$this->updateCart($cart);
}
public function deleteCart(){
//on supprime tous les produits (on vide le panier)
$this->updateCart([]);
}
public function updateCart($cart){
$this->requestStack->getSession('cart', $cart);
}
public function getCart(){
$session = $this->requestStack->getSession();
return $session->get('cart', []);
}
public function getFullCart(){
$cart = $this->getCart();
$fullCart = [];
foreach ($cart as $id => $quantity){
$product = $this->repoProduct->find($id);
if($product){
//produit récupéré avec succés
$fullCart[]=[
'quantity' => $quantity,
'product' => $product
];
}else{
//id incorrect
$this->deleteFromCart($id); //on ne met pas à jour la session car cette method le fait aussi (voir plus haut dans la fonction deleteFromCart)
}
}
}
}
My CartController.php
<?php
namespace App\Controller;
use App\Services\CartServices;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class CartController extends AbstractController
{
/**
* #Route("/cart/add/{id}")
*/
public function addToCart($id,CartServices $cartServices):Response
{
$cartServices->addToCart($id);
dd($cartServices->getCart(1));
return $this->render('cart/index.html.twig', [
'controller_name' => 'CartController',
]);
}
}
the method getSession of RequestStack return an object of SessionInterface, so your code is not correct, bellew the body of the method :
/**
* Gets the current session.
*
* #throws SessionNotFoundException
*/
public function getSession(): SessionInterface
{
if ((null !== $request = end($this->requests) ?: null) && $request->hasSession()) {
return $request->getSession();
}
throw new SessionNotFoundException();
}
So, you should update your method getCart like this :
public function getCart(){
$session = $this->requestStack->getSession();
return $session->get('cart', []);
}
public function getCart(){
return $this->requestStack->getSession('cart', []);
}

How usinghow can I access a session from twig?

How can I access {{ app.session.get('user') }}
I don't understand much about controllers I only have this is the one I use to control that template
//accedemos a las dependencias de Twig
require_once './vendor/autoload.php';
use Symfony\Component\HttpFoundation\Session\Session;
//accedemos a la clase LoginModel para realizar operaciones a la base de datos
require_once './model/LoginModel.php';
require_once './core/ControladorBase.php';
class LoginController
{
private $loader;
private $twig;
private $controladorBase;
public function __construct(){
$this->controladorBase = new ControladorBase();
$this->loader = new \Twig\Loader\FilesystemLoader('./view');
$this->twig = new \Twig\Environment($this->loader);
}
/**
* Muestra la página principal y el listado de empleados dados de alta
* #throws \Twig\Error\LoaderError
* #throws \Twig\Error\RuntimeError
* #throws \Twig\Error\SyntaxError
*/
public function index(){
$session = new Session();
$session->set('user', 'juan' );
echo $this->twig->render('/login/index.html.twig');
}
}

I can not change the URL of my new page during a redirect

On symfony 3.4 I would like to call another function from my controller. The called function will have to redirect to a new page. I would like the URL of the page to be modified.
However, with the method I used, the page changes well, but not the URL.
The scheme was as follows:
Controller => Function => Display login form (twig)
Login form => Validation (POST METHOD)=> Controller => Function => Call other function => Display other page (and modification of the URL).
The URL is not changed, I do not understand.
Here is my code :
Controller:
<?php
namespace Site\PagesBundle\Controller;
use Site\PagesBundle\Entity\User;
use Site\PagesBundle\Entity\Information;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* Information controller.
*
* #Route("accueil")
*/
class DefaultController extends Controller
{
/**
* Accueil
*
* #Route("/", name="connexion_index")
* #Method({"GET", "POST"})
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager(); //Récupération du manager
$listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 1"); //Récupération d'une liste d'informations
$user = new User(); //Initialisation de l'objet User
$form = $this->createForm('Site\PagesBundle\Form\ConnexionType', $user); //Formulaire de création
$form->handleRequest($request);
//Traitement si le formulaire est soumis ( Ajout du package dans la BDD )
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$valide = $this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->authentifier($user->getIdentifiant(),$user->getPassword());
dump($valide);
if($valide == 1)
{
return $this->accueil($request);
}
else
{
return $this->render('#Pages/Default/connexion.html.twig',array(
'user' => $user,
'form' => $form->createView(),
'listeInfos' => $listeInfos,
));
}
// On ajoute un package, donc on offre un téléchargement supplémentaire aux utilisateurs concernés
$this->getDoctrine()->getManager()->getRepository('PagesBundle:User')->updateNbDDLAll("inc");
//return $this->redirectToRoute('paquets_index'); // Redirection page de gestion de packages
}
return $this->render('#Pages/Default/connexion.html.twig',array(
'user' => $user,
'form' => $form->createView(),
'listeInfos' => $listeInfos,
));
}
/**
* #Route("/index", name="accueil")
* #Method("GET")
*/
public function accueil(Request $request)
{
$em = $this->getDoctrine()->getManager(); //Récupération du manager
$listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 2"); //Récupération d'une liste d'informations
return $this->render('#Pages/Default/accueil.html.twig',array(
'listeInfos' => $listeInfos,
));
}
}
The change must happen here:
if($valide == 1)
{
return $this->accueil($request);
}
To
/**
* #Route("/index", name="accueil")
* #Method("GET")
*/
public function accueil(Request $request)
{
$em = $this->getDoctrine()->getManager(); //Récupération du manager
$listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 2"); //Récupération d'une liste d'informations
return $this->render('#Pages/Default/accueil.html.twig',array(
'listeInfos' => $listeInfos,
));
}
Thanks for your help
You are not redirecting to another path, but only returning a view within a controller indexAction. As the current url already corresponds to this action no change is made in the url. If you want to navigate to another path you must tell Symfony to do so.
if($valide == 1)
{
return $this->redirectToRoute('accueil');
}
Your accueil function must then also be an action.
/**
* #Route("/index", name="accueil")
* #Method("GET")
*/
public function accueilAction(Request $request)
{
$em = $this->getDoctrine()->getManager(); //Récupération du manager
$listeInfos = $em->getRepository('PagesBundle:Information')->getInformationsZone("Zone 2"); //Récupération d'une liste d'informations
return $this->render('#Pages/Default/accueil.html.twig',array(
'listeInfos' => $listeInfos,
));
}

Upload file : change extensions of files

I'm actually making upload for any files. First, i tried to upload picture files (.png, .jpg, ...) and it's worked! But when I tried to upload video files like .ogv, .mov, .mp4, the file is uploaded but the extension of the file is changed .ogx for .ogv and .mp4 and .qt for .mov. I don't know where is the error because the code is coming from Symfony website.
Here's my code:
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// le chemin absolu du répertoire où les documents uploadés doivent être sauvegardés
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// on se débarrasse de « __DIR__ » afin de ne pas avoir de problème lorsqu'on affiche
// le document/image dans la vue.
return 'uploads/documents';
}
// propriété utilisé temporairement pour la suppression
private $filenameForRemove;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
$this->path = $this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// vous devez lancer une exception ici si le fichier ne peut pas
// être déplacé afin que l'entité ne soit pas persistée dans la
// base de données comme le fait la méthode move() de UploadedFile
$this->file->move($this->getUploadRootDir(), $this->id.'.'.$this->file->guessExtension());
unset($this->file);
}
/**
* #ORM\PreRemove()
*/
public function storeFilenameForRemove()
{
$this->filenameForRemove = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($this->filenameForRemove) {
unlink($this->filenameForRemove);
}
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path;
}
Thanks in advance !
PokeR

Categories