Symfony 3 - Some difficulties to use templating service to send mail - php

I have a Mail.php file that contains a sendMail function that will be used by several of my controllers.
I got to have to use the "templating" service. But I have problems putting it in place.
My Services.yml:
email_management:
class: Site\PagesBundle\Utils\Mails
arguments: ['#templating']
public: true
My Mail.php:
<?php
namespace Site\PagesBundle\Utils;
use Site\PagesBundle\Entity\User;
use Site\PagesBundle\Entity\UserCas;
class Mails
{
private $templating;
public function __construct(EngineInterface $templating)
{
$this->templating = $templating;
}
public function sendMail($user,$raisonMail)
{
$transport = \Swift_SmtpTransport::newInstance();
$mailer = new \Swift_Mailer($transport);
// Entête
$message = \Swift_Message::newInstance()
->setFrom(array('############' => '############'))
//->setTo($user->getEmail());
->setTo("############")
->setCharset('utf-8')
->setContentType('text/html');
switch($raisonMail)
{
case 'formulaireInscription':
dump($user);
// (1) Confirmation de demande d'inscription
$message->setSubject("subject")
->setBody($this->templating->render("#Pages/swiftmail/CreationCompte/DemandeCreationCompte.html.twig",array(
'prenom'=>$user->getPrenom(),
'nom'=>$user->getNom(),
)));
break;
//... other cases
In my controller :
$templating = new EngineInterface;
$mail = new Mail($templating);
$mail->get('email_management')->sendEmail($user,$motif);
But now I've this error :
You must set a loader first.
Can someone help me please ? Thanks !

Assuming that the intention is to go for the service based option. Please note in general that the service class is intended to be moved into different folder in the project (to be under PagesBundle/Service folder).
services.yml (please note the changed path)
email_management:
class: Site\PagesBundle\Service\EmailManagementService
arguments: ['#templating']
public: true
EmailManagementService.php (please note the changed location & namespace)
<?php
namespace Site\PagesBundle\Service;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Site\PagesBundle\Entity\User;
use Site\PagesBundle\Entity\UserCas;
class Mails
{
private $templating;
public function __construct(EngineInterface $templating)
{
$this->templating = $templating;
}
...
}
Usage in controller:
$this->get('email_management')->sendMail($user,'formulaireInscription');

Related

Dependency injection from service to another service not working symfony 5

I am trying to configure dependency injection for a "Newuser" service. In order not to depend on mysql in the future, what is done is to create a "mysqlService" service that implements an interface with the "persist" method.
From the controller I instantiate the use case "NewUser" that in its constructor by injecting the interface of "DatabaseServiceInterface" and another service "UserPasswordEncoderInterface".
It doesn't work properly since symfony complains because "NewUser doesn't receive anything as parameter" (When the service should be automatically injected).
Files are:
DatabaseServiceInterface:
<?php
namespace App\Application\Infraestructure\DatabaseService;
Interface DatabaseServiceInterface
{
public function persist(Object $ormObject):void;
}
MysqlService:
<?php
namespace App\Application\Infraestructure\DatabaseService;
use Doctrine\ORM\EntityManagerInterface;
class MysqlService implements DatabaseServiceInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function persist(Object $ormObject):void{
$this->entityManager->persist($ormObject);
$this->entityManager->flush();
}
}
RegistrationController:
<?php
namespace App\Controller;
use App\Application\AppUseCases\User\NewUser\NewUserRequest;
use App\Application\Domain\User\User;
use App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface;
use App\Application\Infraestructure\DatabaseService\MysqlService;
use App\Form\RegistrationFormType;
use App\Application\Infraestructure\User\UserAuthenticator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\
UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use App\Application\AppUseCases\User\NewUser\NewUser;
class RegistrationController extends AbstractController
{
/**
* #Route("/register", name="app_register")
*/
public function register(Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler, UserAuthenticator
$authenticator): Response
{
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$newUserRequest = new NewUserRequest();
$newUserRequest->email = $form->get('email')->getData();
$newUserRequest->user = $user;
$newUserRequest->password = $form->get('plainPassword')-
>getData();
$newUser = new NewUser();
$newUser->execute($newUserRequest);
// do anything else you need here, like send an email
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}
}
Usecas NewUser
<?php
namespace App\Application\AppUseCases\User\NewUser;
use App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface;
use Symfony\Component\Security\Core\Encoder\
UserPasswordEncoderInterface;
class NewUser {
private $databaseService;
private $passwordEncoder;
public function __construct(
DatabaseServiceInterface $databaseService,
UserPasswordEncoderInterface $passwordEncoder
) {
$this->databaseService = $databaseService;
$this->passwordEncoder = $passwordEncoder;
}
public function execute(NewUserRequest $userRegisterRequest) {
//Encode the plain password
$userRegisterRequest->user->setPassword(
$this->passwordEncoder->encodePassword(
$userRegisterRequest->user,
$userRegisterRequest->password
)
);
$userRegisterRequest->user->setEmail($userRegisterRequest->email);
$userRegisterRequest->user->setRoles(array_unique(['ROLE_USER']));
//crear servicio para mysql
$this->databaseService->persist($userRegisterRequest->user);
}
}
Services.yaml
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where
the app is deployed
#https://symfony.com/doc/current/best_practices/
configuration.html#application-related-configuration
parameters:
locale: en
availableLocales:
- es
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your
services.
autoconfigure: true # Automatically registers your services as
commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified
class name
App\:
resource: '../src/*'
exclude:
'../src/{DependencyInjection,Entity,
Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller
#class
App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface:
App\Application\Infraestructure\DatabaseService
Although symfony does not throw any errors because it seems that the configuration is fine it still does not work. The error it throws when executing the use case is the following:
Too few arguments to function App\Application\AppUseCases\User\NewUser\NewUser::__construct(), 0 passed in /var/www/symfony/src/Controller/RegistrationController.php on line 33 and exactly 2 expected
You are not retrieving the NewUser class from the container, but instancing it manually, so Dependency Resolution is not happening and the service is not reciving any of its dependencies. You should inject the service into the controller for dependency resolution to occur, or pass the arguments explicitly when instancing it.
public function register(Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler,
UserAuthenticator $authenticator,
NewUser $newUser): Response
{
//...
$newUserRequest = new NewUserRequest();
//...
// $newUser = new NewUser(); // Not passing The Database or PasswordEncoder dep
$newUser->execute($newUserRequest);
//...
}

function getDoctrine won't work in one specific function of a controller

EDIT: now trying to use an EventSubscriber as b.enoit.be advised me. But when I send a mail, nothing happens. I don't get any error, but the mail isn't stored in my database either. And I tested What did I do wrong?
SentMailsListener.php:
<?php
namespace Fidelise\SignUpBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Fidelise\SignUpBundle\Entity\EmailsHistory;
use Swift_Events_SendEvent;
class SentMailsListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [Swift_Events_SendEvent::RESULT_SUCCESS => 'onMailSent'];
}
public function onMailSent(Swift_Events_SendEvent $event)
{
$em = $this->getDoctrine()->getManager();
$message = $event->getMessage();
$email = new EmailsHistory();
$email->setRecipient(key($message->getTo()));
$email->setSubject($message->getSubject());
$email->setBody($message->getBody());
$email->setSender(key($message->getFrom()));
$em->persist($email);
$em->flush();
}
}
services.yml:
services:
mail_sent_subscriber:
class: Fidelise\SignUpBundle\EventListener\SentMailsListener
tags:
- { name: kernel.event_subscriber }
Make sure your driver extends from AbstractController. Anyway, without more code, it is difficult to give you a concrete answer.

How to use renderView and twig templating in a Symfony 4 Service

I’m creating a Emailer Service in my new Symfony 4 application.
I have tried a million things but no luck. I could only find a few resources on this topic for S4 at the moment. Any help is appreciated.
This what I’m trying to achieve. I understand I have to use different services inside of my Emailer service but no luck.
<?php
namespace App\Mailer;
class Emailer
{
public function sendWelcome($email): \Swift_Mailer
{
$message = (new \Swift_Message('P****** - Welcome In!'))
->setFrom('no-reply#p****n.com')
->setTo($email)
->setBody(
$this->renderView(
// templates/emails/registration.html.twig
'emails/registration.html.twig',
array('name' => $user->getUsername())
),
'text/html'
)
->setCharset('utf-8');
$mailer->send($message);
return true;
}
}
First you need to get your templating service injected into your class (constructor injection) and then you can use it to render template.
In the code you can see it that we type-hint it in constructor so Symfony Dependency injection know what we need. Then we just use it. Same will be with your $mailer service.
<?php
namespace App\Mailer;
use Symfony\Component\Templating\EngineInterface;
class Emailer
{
/**
* #var EngineInterface
*/
private $templating;
/**
* TestTwig constructor.
*/
public function __construct(EngineInterface $templating)
{
$this->templating = $templating;
}
public function sendWelcome($email): \Swift_Mailer
{
$message = (new \Swift_Message('P****** - Welcome In!'))
->setFrom('no-reply#p****n.com')
->setTo($email)
->setBody(
$this->templating->render(
// templates/emails/registration.html.twig
'emails/registration.html.twig',
array('name' => $user->getUsername())
),
'text/html'
)
->setCharset('utf-8');
$mailer->send($message);
return true;
}
}
#miles-m a use statement is not the same as injection. A use statement just makes the class accessible with the class name as an alias. Dependency Injection is a pattern that decouples your classes from each other which facilitates better testing and debugging (you can swap out your injected objects for mock objects etc).
One way to inject the Swift_Mailer would be as a constructor parameter, i.e.
class Emailer
{
/** #var \Swift_Mailer $mailer */
private $mailer;
public function __construct(
EngineInterface $templating,
\Swift_Mailer $mailer <== mailer will be injected here
) : \Swift_Mailer
{
//...
$this->mailer->send($message);
}
}
class CallingClass
{
//...
$emailer = new Emailer(
//EngineInterface instance
//\Swift_Mailer instance <== injecting
);
$emailer->sendWelcome('email#example.com');
}
Other questions
$mailer->send($message)
Where is your $mailer instance defined?
public function sendWelcome($email): \Swift_Mailer
return true;
Is true a valid instance of Swift_Mailer?

Symfony Twig Extension breaks other service - Is templating done before security?

I am working on a Symfony 2.7 WebApp. One of the bundles I created includes a service that offer some user related stuff, e.g. userHasPurchases().
Problem is, that including a Twig Extesion breaks another service:
AppShopService
namespace AppShopBundle\Service;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...
class AppShopService {
protected $user;
public function __construct(TokenStorageInterface $tokenStorage, ...) {
$this->user = $tokenStorage->getToken() ? $tokenStorage->getToken()->getUser() : null;
...
}
public function userHasPurchases(User $user) {
$user = $user ? $user : $this->user;
$result = $user...
return result;
}
}
AppShopBundle\Resources\config\services.yml
services:
app_shop.service:
class: AppShopBundle\Service\AppShopService
arguments:
- "#security.token_storage"
- ...
So far everything works fine: The AppShopServices is created with the current user and userHasPurchases() work as expected.
Now I have add a Twig Extension to be able to use userHasPurchases() within my templates:
Twig Extension
namespace AppShopBundle\Twig;
use AppShopBundle\Service\AppShopService;
class AppShopExtension extends \Twig_Extension {
private $shopService;
public function __construct(AppShopService $shopService) {
$this->shopService = $shopService;
}
public function getName() {
return 'app_shop_bundle_extension';
}
public function getFunctions() {
$functions = array();
$functions[] = new \Twig_SimpleFunction('userHasPurchases', array(
$this,
'userHasPurchases'
));
return $functions;
}
public function userHasPurchases($user) {
return $this->shopService->userHasPurchases($user);
}
}
Including Extension in AppShopBundle\Resources\config\services.yml
services:
app_shop.service:
class: AppShopBundle\Service\AppShopService
arguments:
- "#security.token_storage"
- ...
app_shop.twig_extension:
class: AppShopBundle\Twig\AppShopExtension
arguments:
- "#app_shop.service"
tags:
- { name: twig.extension }
After icluding the Twig Extension, AppShopService and its method userHasPurchases does not work any more. Problem is, that the constructor of AppShopService does not set user anymore since $tokenStorage->getToken() now returns null.
How is this possible? I have changed nothing except including the Twig Extension. As soon as I remove the Twig Extension from services.yml everything works correctly again.
My only guess is, that the creation fo the Twig Extension is done before any security. But why?
Any idea what might be wrong here?
don't interact with the tokenStorage in the constructor but only in the userHasPurchases method.
namespace AppShopBundle\Service;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...
class AppShopService {
protected $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage, ...) {
$this->tokenStorage = $tokenStorage;
}
public function userHasPurchases(User $user) {
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
$result = $user...
return result;
}
}
Hope this help

Error: Call to a member function get() on a non-object

I am trying to send mail with Swift_Message however when I go to send the data it will not send and I get an error of
FatalErrorException: Error: Call to a member function get() on a
non-object in
/vagrant/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
line 252
Here is the Email Controller that I am using.
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
class EmailController extends Controller{
public function createMessage($subject, $from, $from_name, $to, $to_name, $body){
// Create the message
$message = \Swift_Message::newInstance()
// Give the message a subject
->setSubject($subject)
// Set the From address with an associative array
->setFrom(array($from => $from_name))
// Set the To addresses with an associative array
->setTo(array($to => $to_name))
// Give it a body
->setBody($body, 'text/html');
return $message;
}
public function sendEmail($message, $urlAlias){
$this->get('mailer')->send($message);
return $this->redirect($this->generateUrl($urlAlias));
}
}
I understand that its unable to access the object which I think is part of the container class but I can seem to get it to pull up. I have tried using $this->container->get(...
but that also does not work. What am I missing. This seems like it should be really straight forward.
I am calling this function from a different bundle using an action to call the current controller. I don't know if that makes a difference.
Ok so when looking in /vagrant/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
The line it errors on is
/**
* Gets a service by id.
*
* #param string $id The service id
*
* #return object The service
*/
public function get($id)
{
return $this->container->get($id);
}
}
Which makes me feel like 'mailer; is not a good $id but it is used in Symfony's examples and in a lot of other private examples.
Don't know if this helps or not but figured it was worth mentioning.
Could this be because of the swiftmailer: setting inside of my config.yml file?
routing.yml file
fuel_form_homepage:
pattern: /hello/{name}
defaults: { _controller: FuelFormBundle:Default:index }
referral_form:
pattern: /form/referral/{hash}
defaults: { _controller: FuelFormBundle:Form:referralForm }
referral_result:
pattern: /form/referral/result
defaults: { _controller: FuelFormBundle:Form:referralResult }
user_form:
pattern: /form/user
defaults: { _controller: FuelFormBundle:Form:userForm }
home:
pattern: /
defaults: { _controller: FuelFormBundle:Default:home}
This is the function that calls
public function userFormAction(request $request){
$user = new User();
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if($form->isValid()){
$user->setTimeCreated();
$user->setTimeUpdated();
$date = $user->getTimeCreated();
$timestamp = $date->format("U");
$hash = $user->getFirstName() . $user->getLastName() . $timestamp ;
$user->setUserHash(md5($hash));
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
print_r($user);
//TODO: #Email: #Body: make sure to replace with correct information.
//Calls a service named email_bundle_controller
$emailController = $this->get('email_bundle_controller');
$fullName = $user->getFirstName() . $user->getLastName();
$body = "please visit the following url to start referring! <a href='http://localhost:8080/app_dev.php/form/referral/" . $user->getUserHash() . "'>Your URL</a>";
$message = $emailController->createMessage('Welcome to Fuel PRM References', 'bsaverino#gmail.com', 'Brad Saverino', $user->getEmail(), $fullName, $body);
$emailController->sendEmail($message, 'user_form');
}
return $this->render('FuelFormBundle:Default:mainForm.html.twig', array('form' => $form->createView(),));
}
This is the service that allows me to call on the other bundle.
services:
fuel_form.form.type.referral:
class: Fuel\FormBundle\Form\Type\ReferralType
tags:
- { name: form.type, alias: referral}
fuel_form.form.type.user:
class: Fuel\FormBundle\Form\Type\UserType
tags:
- { name: form.type, alias: user}
email_bundle_controller:
class: Fuel\EmailBundle\Controller\EmailController
This is the FuelEmailBundle.php
namespace Fuel\EmailBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerInterface;
class FuelEmailBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}
These are the changes that were made to the sendEmail function
public function sendEmail($message, $urlAlias){
$container = FuelEmailBundle::getContainer();
$mailer = $container->get('mailer');
$mailer->send($message);
return $this->redirect($this->generateUrl($urlAlias));
}
As Cerad had mentioned above, you are getting the error as container is not set. One way of fixing this issue would be to pass a container instance to your bundle so that you can call the container from anywhere in your project.
Edit the class corresponding to your bundle(BundleName.php) to include two methods setContainer and getContainer. See the example below.
namespace Venom\CoreBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerInterface;
class VenomCoreBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}
Use the appropriate namespaces.
Then, use the namespace for the bundle in classes where you need the container.
You may call the container by
$container = VenomCoreBundle::getContainer();
Then, call the mailer
$mailer = $container->get('mailer');

Categories