SwiftMailer calling an undefined method - php

I have an error trying to use SwiftMailer with Symfony.
I'm following a tutorial from here: http://symblog.site90.net/docs/validators-and-forms.html
The error code is:
FatalErrorException: Error: Call to undefined method Swift_Message::setMessage() in C:\xampp\htdocs\TP\src\TP\MainBundle\Controller\DefaultController.php line 327
My action is:
public function newAction()
{
$contacto = new Contacto();
$form = $this->createForm(new ContactoType(), $contacto);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$message = \Swift_Message::newInstance()
->setFrom('enquiries#myweb.com.ar')
->setTo($this->container->getParameter('tp_main.emails.contact_email'))
//this is the line 327 related with the error
->setMessage($this->renderView('TPMainBundle:Default:contactEmail.txt.twig', array('contacto' => $contacto)));
//
$this->get('mailer')->send($message);
$this->get('session')->setFlash('blogger-notice', 'Your contact enquiry was successfully sent. Thank you!');
return $this->redirect($this->generateUrl('contacto_new'));
}
}
return $this->render('TPMainBundle:Default:contact.html.twig', array(
'form' => $form->createView(),));}
The error says it's undefined but,
my method setMessage() is defined in my class Contacto.php with getter and setter :
/**
* #var string
*
* #ORM\Column(name="message", type="text")
*/
private $message;
/**
* Set message
*
* #param string $message
* #return Contacto
*/
public function setMessage($message)
{
$this->message = $message;
return $this;
}
/**
* Get message
*
* #return string
*/
public function getMessage()
{
return $this->message;
}
According on what I read trying to solve it myself, could the problem be related to the versions of SwiftMailer ?
Thanks

The method "setMessage" refers indeed to your Contacto entity, but to set the content of a message with SwiftMailer, you have to use setBody();
Example :
$mail = \Swift_Message::newInstance();
$mail->setFrom('me#mail.com')
->setTo('you#mail.com')
->setSubject('Email subject')
->setBody('email body, can be swift template')
->setContentType('text/html');
$this->get('mailer')->send($mail);
EDIT : save the email content in database and send it to Twig template
I added a bit of code in your function, to actually save the email content in database. It's not mandatory but I imagine you'll want to retrieve this information later in your application. Anyway, I didn't test this, but it should work : your controller set the SwiftMailer message body as a Twig template. Your template should be able to parse the "contacto" entity.
public function newAction()
{
$contacto = new Contacto();
$form = $this->createForm(new ContactoType(), $contacto);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
// set your "contacto" entity
$contacto->setMessage($form->get('message')->getData());
// do the rest of $contact->set{......}
// save the message in database (if you need to but I would do it)
$em = $this->getDoctrine()->getManager();
$em->persist($contacto);
$em->flush();
// SEND THE EMAIL
$message = \Swift_Message::newInstance();
$message->setFrom('enquiries#myweb.com.ar')
->setTo($this->container->getParameter('tp_main.emails.contact_email'))
->setSubject('YOUR SUBJECT')
->setBody($this->renderView('TPMainBundle:Default:contactEmail.txt.twig', array('contacto' => $contacto)))
->setContentType('text/html');
$this->get('mailer')->send($message);
$this->get('session')->setFlash('blogger-notice', 'Your contact enquiry was successfully sent. Thank you!');
return $this->redirect($this->generateUrl('contacto_new'));
}
}
return $this->render('TPMainBundle:Default:contact.html.twig', array('form' => $form->createView()));
}

Related

Symfony 4 - How to valid my token in controller?

in a function of my controller, I initialize a form that I pass in parameter to a view. The form must then redirect to another action of my controller, like this:
Controller : index()
/**
* #Route("/validation/absences", name="validation_index")
*/
public function index(PaginatorInterface $paginator, Request $request, AbsenceService $absenceService)
{
$refusAbsence = new Absence();
$formRefus = $this->createForm(RefusAbsenceType::class, $refusAbsence);
$formRefus->handleRequest($request);
return $this->render('validation/index.html.twig', [
"formRefus" => $formRefus->createView(),
]);
My form action goes to this function :
/**
* Refuser une demande d'absence
*
* #Route("validation/absences/refuser/{id}", name="validation_refuser")
*
* #param Absence $absence
* #return void
*/
public function refuser(Request $request, Absence $absence)
{
$token = $request->get('refus_absence')['_token'];
if (!$this->isCsrfTokenValid('refus_absence__token', $token)) {
throw new \Symfony\Component\Security\Core\Exception\AccessDeniedException('Accès interdit');
}
$commentaire = $request->get('refus_absence')['commentaire'];
dd($commentaire);
}
I get my token back with the request, but I can not get it to be validated. I still have the mistake.
Yet on Symfony's documentation, they say:
if ($this->isCsrfTokenValid('token_id', $submittedToken)) {
// ... do something, like deleting an object
}
And in my HTML, I've :
<input type="hidden" id="refus_absence__token" name="refus_absence[_token]" value="7bbockF5tz3r7Ne9f6dQB7Y5YMcwd1QRES4vHrhQEQE">
in your receiving function, just recreate the form:
$form = $this->createForm(RefusAbsenceType::class, new Absence());
$form->handleRequest($request);
// also checks csrf, it is enabled globally, otherwise, recreate parameters
// in the createForm call.
if($form->isSubmitted() && $form->isValid()) {
$absence = $form->getData();
// do whatever ... persist and stuff ...
}

Address in mailbox given [Doctrine\ORM\Query_state] does not comply with RFC 2822, 3.6.2

I'm developing a blog under symfony and to send emails I use swiftMailer.
I use this code in my controller to send to all my users to warn them that a new article was added but the problem is that it shows me an error message.
My controller:
/**
* #Route("/admin/ajout")
* #param Request $request
* #return Response
* #throws \Exception
*/
public function add(Request $request, \Swift_Mailer $mailer): Response {
$article = new Articles();
$addArticle = $this->createForm(ArticlesType::class, $article);
$addArticle->handleRequest($request);
$info = $this->getDoctrine()->getRepository(InfoMore::class)->findByInfo(2);
$em = $this->get('doctrine.orm.entity_manager');
$dql = "SELECT email FROM App:user";
$query = $em->createQuery($dql);
if($addArticle->isSubmitted() && $addArticle->isValid()) {
$article = $addArticle->getData();
$manager = $this->getDoctrine()->getManager();
$manager->persist($article);
$manager->flush();
$message = (new \Swift_Message('Un nouvelles articles est publier'))
->setFrom('contact#al-houria.com')
->setTo($query)
->setBody(
$this->renderView(
'email/article_publish.html.twig'
),
'text/html'
)
;
$mailer->send($message);
$this->addFlash('article', 'L\'article a bien étais ajouter');
return $this->redirectToRoute('app_backoffice_admin');
}
return $this->render('backOffice/CRUD/add.html.twig', [
'title' => 'Ajouter un article a votre blog',
'info' => $info,
'addArticle' => $addArticle->createView()
]);
}
and the message error:
Address in mailbox given [Doctrine\ORM\Query_state] does not comply with RFC 2822, 3.6.2
It tells me that the address is not valid and in this case how to add multiple email addresses?
Thank you for your help!
Assuming your User entity is in App\Entity\User, you can select this:
$emails = $em
->createQuery('SELECT u.email FROM App\Entity\User u')
->getResult()
;
This will return an array of email addresses from your users. Then you just pass that to your setTo() method:
$message = (new \Swift_Message('Un nouvelles articles est publier'))
->setFrom('contact#al-houria.com')
->setTo($emails)
// ...
;
See the Doctrine DQL SELECT Examples for further help. If you wanted to be a bit fancier, you could add the user's name to their email addresses and send that out. Let's assume you have a getName() function in your User entity. You could instead generate your email addresses like so:
$emails = [];
$users = $this->getDoctrine()->getRepository(User::class)->findAll();
foreach ($users as $user) {
$emails[$user->getEmail()] = $user->getName();
}
Then when you called ->setTo($emails) it would have both their name and email address.
One last note, I would use $em = $this->getDoctrine()->getManager(); rather than $this->get('doctrine.orm.entity_manager');

Symfony 3, populating token and refreshing user

repository with issue
I have a form for entity User with email field:
->add('email', EmailType::class, [
'constraints' => [
new NotBlank(),
new Email([
'checkMX' => true,
])
],
'required' => true
])
when i'm editing email to something like test#gmail.com1 and submit form it shows me error "This value is not a valid email address." THat's ok, but after that symfony populate wrong email into token and when i'm going to any other page or just reload page, i'm getting this:
WARNING security Username could not be found in the selected user
provider.
i think question is: why symfony populate wrong Email that failed validation into token and how i could prevent it?
controller:
public function meSettingsAction(Request $request)
{
$user = $this->getUser();
$userUnSubscribed = $this->getDoctrine()->getRepository('AppBundle:UserUnsubs')->findOneBy(
[
'email' => $user->getEmail(),
]
);
$form = $this->createForm(UserSettingsType::class, $user);
$form->get('subscribed')->setData(!(bool)$userUnSubscribed);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/**
* #var $user User
*/
$user = $form->getData();
/** #var UploadedFile $avatar */
$avatar = $request->files->get('user_settings')['photo'];
$em = $this->getDoctrine()->getManager();
if ($avatar) {
$avatar_content = file_get_contents($avatar->getRealPath());
$avatarName = uniqid().'.jpg';
$oldAvatar = $user->getPhoto();
$user
->setState(User::PHOTO_STATE_UNCHECKED)
->setPhoto($avatarName);
$gearmanClient = $this->get('gearman.client');
$gearmanClient->doBackgroundDependsOnEnv(
'avatar_content_upload',
serialize(['content' => $avatar_content, 'avatarName' => $avatarName, 'oldAvatar' => $oldAvatar])
);
}
$subscribed = $form->get('subscribed')->getData();
if ((bool)$userUnSubscribed && $subscribed) {
$em->remove($userUnSubscribed);
} elseif (!(bool)$userUnSubscribed && !$subscribed) {
$userUnSubscribed = new UserUnsubs();
$userUnSubscribed->setEmail($form->get('email')->getData())->setTs(time());
$em->persist($userUnSubscribed);
}
$user->setLastTs(time());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->get('user.manager')->refresh($user);
return $this->redirectToRoute('me');
}
return $this->render(
':user:settings.html.twig',
[
'form' => $form->createView(),
]
);
}
UPD:
it works fine if i change in OAuthProvider:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getName());
}
to:
/**
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*
* #return \Symfony\Component\Security\Core\User\UserInterface
*/
public function refreshUser(UserInterface $user)
{
return $this->userManager($user->getId());
}
but it seems to be dirty hack.
Thanks.
Your user token seems to be updated by the form, even if the email constraint stop the flush.
Can you check if your form past the isValid function ?
You can maybe try to avoid it with an event listener or a validator.
With an event SUBMIT you should be able to check the email integrity, and then add a FormError to avoid the refreshUser.
This is a tricky one, thanks to the repository it was easier to isolate the problem. You are binding the user object form the authentication token to the createForm() method. After the
$form->handleRequest($request)
call the email off the token user object is updated.
I first thought to solve this by implementing the EquatableInterface.html in the User entity but this did not work, as the compared object already had the wrong email address set.
It may also be useful to implement the EquatableInterface interface, which defines a method to check if the user is equal to the current user. This interface requires an isEqualTo() method.)
Than I thought about forcing a reload of the user from the db and resetting the security token, but in the it came to my mind, that it might be sufficient to just refresh the current user object from the database in case the form fails:
$this->get('doctrine')->getManager()->refresh($this->getUser());`
In your controller, this would solve your issue.
/**
* #Route("/edit_me", name="edit")
* #Security("has_role('ROLE_USER')")
*/
public function editMyselfAction(Request $request) {
$form = $this->createForm(User::class, $this->getUser());
if ($request->isMethod(Request::METHOD_POST)) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
} else {
$this->get('doctrine')->getManager()->refresh($this->getUser());
}
}
return $this->render(':security:edit.html.twig',['form' => $form->createView()]);
}
Alternative solution
The issue at the Symfony repository resulted in some valuable input about Avoiding Entities in Forms and
Decoupling Your Security User which provides a more complex approach for a solution to your problem.

sending email with symfony2

I'm trying to create a function that gives the opportunity for every user to send an email for another user. This is the basic controller of Swiftmailer:
public function contactAction($id)
{
$enquiry = new Enquiry();
$form = $this->createForm(new EnquiryType(), $enquiry);
$from=$this->container->get('security.context')->getToken()->getUser()->getemail();
$em=$this->getDoctrine()->getManager();
$user=$em->getRepository('PfeUserBundle:User')->findBy(array('id'=>$id));
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$message = \Swift_Message::newInstance()
->setSubject('Intello')
->setFrom($from)
->setTo($user->getemail())
->setBody($this->renderView('PfeUserBundle:Contact:contactEmail.txt.twig', array('enquiry' => $enquiry)));
$this->get('mailer')->send($message);
$this->get('session')->getFlashBag()->add('blogger-notice','Ton message a été envoyé avec succès');
// Redirect - This is important to prevent users re-posting
// the form if they refresh the page
return $this->redirect($this->generateUrl('pfe_intello_contact_mail'));
}
}
i have this error
Error: Call to a member function getemail() on a non-object
$user isn't an object because you are getting an array.
Change:
$user=$em->getRepository('PfeUserBundle:User')->findBy(array('id'=>$id));
Into:
$user=$em->getRepository('PfeUserBundle:User')->findOneBy(array('id'=>$id));

Sending an email on edit at SonataAdminBundle

So in my UsersAdmin I want to send an email to that user if I confirm his account.(in my case, making Enabled = true). I do this in the configureListFields function
/**
* {#inheritdoc}
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('username')
->add('email')
->add('groups')
->add('enabled', null, array('editable' => true)) //here
->add('locked', null, array('editable' => true))
->add('createdAt')
;
}
By reading the documentation I think i need to use the batchAction function yes? So I made this:
public function getBatchActions()
{
// retrieve the default batch actions (currently only delete)
$actions = parent::getBatchActions();
$container = $this->getConfigurationPool()->getContainer();
$user = //how to get the user that i am editing right now?
if ($this->hasRoute('edit') && $this->isGranted('EDIT')) {
$body = $container->get('templating')->render('MpShopBundle:Registration:registrationEmail.html.twig', array('user'=> $user));
$message = Swift_message::newInstance();
$message->setSubject($container->get('translator')->trans('registration.successful'))
->setFrom($container->getParameter('customer.care.email.sender'))
->setTo('email#contact.lt')
->setBody($body, 'text/html');
$container->get('mailer')->send($message);
}
return $actions;
}
Now I am stuck with two unclear thing with this function:
How can I get the current user data hat I want to edit?
Am I even going in the right direction? Do I need to override edit or maybe some other function?
THE SOLUTION
The best way is to do your login in the postUpdate event, so that everytime you update an object it initiates the functions you want.
public function postUpdate($user)
{
if($user->getEnabled() == true) {
$container = $this->getConfigurationPool()->getContainer();
$body = $container->get('templating')->render('MpShopBundle:Registration:registrationEmail.html.twig', array('user' => $user));
$message = Swift_message::newInstance();
$message->setSubject($container->get('translator')->trans('registration.successful'))
->setFrom($container->getParameter('customer.care.email.sender'))
->setTo('email#contact.lt')
->setBody($body, 'text/html');
$container->get('mailer')->send($message);
}
}
you can use Saving hooks.
public function postUpdate($user)
{
//code to check if enabled
// code to send email
}

Categories