Email ako komponenta alebo klasika

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
SontoEremo
Člen | 341
+
0
-

Rád by som sa Vás spýtal ako riešite zasielanie mailu?
či to máte ako komponentu alebo si odosielanie mailu vkladáte tam kde to potrebujete?

jarda256
Člen | 130
+
+1
-

Ahoj, předem říkám, že netvrdím, že moje řešení je správné, ale mám to takto. Používám tedy mailgun, ale i bez něj se to dá použít. V abstraktní třídě (v mém případě AbstractBaseFacade) mám protected $mailSender a funkci setMailSender. V konfigu pak nastavený decorator pro AbstractBaseFacade. Potom všichni potomci třídy AbstractBaseFacade mohou využít funkci sendMail

$mail = new Message();
$mail->addTo($person->email);
$params = [
	'a' => $a,
	'b' => $b,
	'c' => $c,
];
$mail->setSubject('Potvrzení registrace');
$this->mailSender->sendEmail($mail, 'confirmRegistration.latte', $params);
decorator:
    App\Model\Facades\AbstractBaseFacade:
        setup:
            - setEntityManager
            - setMailSender
<?php

namespace App\Model\Facades;

use App\MailSender;
use Kdyby\Doctrine\EntityManager;
use Nette\Object;

/**
 * Základní fasáda pro všechny ostatní fasády aplikace.
 * Předává přístup pro práci s entitami.
 * @package App\Model\Facades
 */
abstract class AbstractBaseFacade extends Object
{
    /** @var EntityManager */
    protected $em;

    /** @var MailSender */
    protected $mailSender;

    public function setEntityManager(EntityManager $em)
    {
        $this->em = $em;
    }

    public function setMailSender(MailSender $mailSender)
    {
        $this->mailSender = $mailSender;
    }

}
<?php
namespace App;

use Nette\Application\LinkGenerator;
use Nette\Bridges\ApplicationLatte\ILatteFactory;
use Nette\Bridges\ApplicationLatte\UIMacros;
use Nette\Mail\Message;
use Nette\Mail\IMailer;
use Nette\Mail\SendmailMailer;
use Nette\Mail\SmtpException;
use Nette\Mail\SmtpMailer;
use Kdyby\Doctrine\EntityManager;
use Tracy\Debugger;

class MailSender
{

    /** @var SmtpMailer */
    protected $smtpMailer;

    /** @var  SendmailMailer */
    protected $sendMailer;

    /** @var EntityManager */
    protected $em;

    /** @var LinkGenerator */
    private $linkGenerator;

    /** @var ILatteFactory */
    private $latteFactory;

    function __construct(EntityManager $em, LinkGenerator $lg, ILatteFactory $latteFactory)
    {
        $this->em = $em;
        $this->linkGenerator = $lg;
        $this->latteFactory = $latteFactory;
        $this->smtpMailer = new SmtpMailer([
            'host' => 'smtp.mailgun.org',
            'username' => '',
            'password' => '',
            'secure' => 'ssl',
        ]);
        $this->sendMailer = new SendmailMailer();
    }


    function sendEmail(Message $message, $template, $variables)
    {
        $latte = $this->latteFactory->create();
        UIMacros::install($latte->getCompiler());
        $latte->addProvider("uiControl", $this->linkGenerator);
        $html = $latte->renderToString(__DIR__ . '/' . $template, [
            'variables' => $variables
        ]);
        $message
            ->setFrom('info@domena.cz', 'Domena')
            ->setHtmlBody($html);
        try{
            $this->smtpMailer->send($message);
        } catch (SmtpException $e){
            $this->sendMailer->send($message);
        }
    }
}
SontoEremo
Člen | 341
+
0
-

@jarda256
Ďakujem za odpoveď a aj priložené kódy… presne toto sa snažím docieliť len som nevedel ako dať vedie aké latte zaslať.

$this->mailSender->sendEmail($mail, 'confirmRegistration.latte', $params);

ani ma nenapadlo toto použiť…
Ešte sa ta spýtam ak by som chcel z congig.neon odoslať údaje do MailSender ako to mám preniesť? lebo toto riešim v config.neon či to je jedno a je to bezpečné uvádzať aj v MailSender?

akadlec
Člen | 1326
+
+2
-

Mail sender by IMHO měla být služba a tam kde je potřeba poslat mail by se měla vyžadovat. Dělat to jako komponentu je nepochopení celého nettefw. Cpát ji přes dekorátor do nějaké fasády? No nevim nevim, snad jen pro nějaký „quick“ projekt co se zbouchá za víkend a pak už se na něj nehrábe.

jarda256
Člen | 130
+
0
-

@akadlec dobřea jak bys tedy tohle řešil…mám fásady pro práci s doctrine entitami…a když se něco děje tak chci notifikovat mailem…proto jsem to řešil takhle a jelikož chci mít maily dostupné v každé fasádě proto přes decorátor…ale rád se nechám poučit

CZechBoY
Člen | 3608
+
0
-

Tak většinou si vyžaduju to co potřebuju a ne to co možná nějaký moji potomci budou někdy potřebovat. To si můžeš rovnou předat celej DIC, protože z něj někdy něco budeš potřebovat.

SontoEremo
Člen | 341
+
0
-

@CZechBoY @akadlec
Takže je lepšie dávať všade

$template = $this->templateFactory->createTemplate();
				$template->setFile('App\Presenters\Templates\Messages\activation.latte');
				$template->verifyLink = $this->linkGenerator->link('Global:activationAccount', array(
				'token' => $token,
				));
				$mail = new Message;
				$mail->setFrom('Mystical Vision <email@email.xx>')
					 ->addTo($values->email)
					 ->setSubject('Aktivácia účtu na stránke PageName')
					 ->setHtmlBody((string) $template, __DIR__ . '/../../assets/global/img/');
				$this->mailer->send($mail);

Tam kde registrujte zas registračný mail atď…?

CZechBoY
Člen | 3608
+
0
-

Určitě si to můžeš zjednodušit nějakou svojí továrnou, ale předej si ji normálně tam kde to potřebuješ a ne úplně všude.
btw. Na tohle jsou dobrý události. Vyvoláš událost někdo se registroval, mailovej modul se přihlásí k té události a pošle email nezávisle na tom kdo tu událost vyvolal (databázová vrstva nemusí vůbec o mailech vědět).

SontoEremo
Člen | 341
+
0
-

Tak som sa pokúsil niečo skúsiť napísať ale samozrejme končí to chybou ale nie tracy ale klasické php…
IEmailSenderFactory

<?php
namespace App\Emails;

interface IEmailSenderFactory {
	/**
	 * @return EmailSender
	 */
	function create();
}

EmailSender

<?php
namespace App\Emails;
use Tracy\Debugger;
use Nette\Mail\IMailer;
use Nette\Mail\Message;
use Nette\Mail\SmtpMailer;
use Nette\Mail\SmtpException;
use Latte\Loaders\StringLoader;
use Nette\Application\UI\Control;
use Nette\Application\UI\ITemplateFactory;

class EmailSender extends Control {

	/** @var Nette\Mail\IMailer @inject */
	private $mailer;

	/** @var Nette\Application\UI\ITemplateFactory */
	private $templateFactory;

	public function __construct(IMailer $mailer, ITemplateFactory $templateFactory) {
		$this->mailer = $mailer;
		$this->templateFactory = $templateFactory;
	}

	public function sendEmail(Message $message, $template, $variables) {
		try {
			$this->mailer = new SmtpMailer([
				'smtp' => 'true',
				'host' => 'smtp.gmail.com',
				'username' => 'xx@xx.xxx',
				'password' => '*******',
				'port' => 465,
				'secure' => 'ssl',
			]);

			$template = $this->templateFactory->createTemplate();
			$template->setFile(__DIR__ . 'messages/' . $template);
	        $message->setFrom('Page Name <xx@xx.xx>')
	        ->setHtmlBody($template);
	       $this->mailer->send($message);
		} catch (SmtpException $e) {
			$this->addError("Odoslanie mailu zlyhalo", "warning");
		}
	}
}

A SignUpFormFacory

$mail = new Message();
			$mail->addTo($values->email);
			$mail->setSubject('Aktivácia účtu na stránke Mystical Vision');
			$this->emailSender->sendEmail($mail, 'activation.latte');

Po odoslaní nastane chyba
Fatal error: Uncaught TypeError: Argument 1 passed to Tracy\Helpers::getSuggestion() must be of the type array, null given, called in C:\xampp\htdocs\mysticalvision\vendor\tracy\tracy\src\Tracy\Helpers.php on line 178 and defined in C:\xampp\htdocs\mysticalvision\vendor\tracy\tracy\src\Tracy\Helpers.php:211 Stack trace: #0 C:\xampp\htdocs\mysticalvision\vendor\tracy\tracy\src\Tracy\Helpers.php(178): Tracy\Helpers::getSuggestion(NULL, ‚sendEmail‘) #1 C:\xampp\htdocs\mysticalvision\vendor\tracy\tracy\src\Tracy\Debugger.php(281): Tracy\Helpers::improveException(Object(Error)) #2 [internal function]: Tracy\Debugger::exceptionHandler(Object(Error)) #3 {main} thrown in C:\xampp\htdocs\mysticalvision\vendor\tracy\tracy\src\Tracy\Helpers.php on line 211

uestla
Backer | 799
+
0
-

Co je v $this->emailSender v SignUpFormFacory?

jarda256
Člen | 130
+
0
-

@CZechBoY v každé fasádě je něco, co vyžaduje odeslání notifikačního mailu. Proto to mám dostupné pro všechny potomky třídy AbstractBaseFacade

SontoEremo
Člen | 341
+
0
-

uestla napsal(a):

Co je v $this->emailSender v SignUpFormFacory?

/** @var App\Emails\IEmailSenderFactory */
	public $emailSender;

public function __construct(FormFactory $factory, UserManager $userManager, LinkGenerator $linkGenerator, IEmailSenderFactory $emailSender) {
		$this->factory = $factory;
		$this->userManager = $userManager;
		$this->linkGenerator = $linkGenerator;
		$this->emailSender = $emailSender;

	}
CZechBoY
Člen | 3608
+
0
-

@jarda256 Co u tebe dela fasada? Ja si fasadu predstavuju jen jako zjednoduseni nad par tridama, tzn. tvoje fasada je takovej malej service locator. Coz je zbytecny pri pouziti DI.

CZechBoY
Člen | 3608
+
0
-

@SontoEremo Tovarna ani komponenta tu nedava vubec smysl. Z emailSenderu udelej normalni sluzbu a injectuj ji primo bez jakykoliv tovarny. Taky bych z ni udelal cistou tridu (nic nededi).

jarda256
Člen | 130
+
0
-

@CZechBoY pro každou entitu v db mám fasádu…takže mám třeba Registration.php a RegistrationFacade.php…respektive definice entity a práce s entitou

igor.pocta
Člen | 100
+
0
-

SontoEremo napsal(a):

Rád by som sa Vás spýtal ako riešite zasielanie mailu?
či to máte ako komponentu alebo si odosielanie mailu vkladáte tam kde to potrebujete?

My máme e-mailovou frontu v databázi, kde jsou dané priority. O odeslání se stará cron a posílá to po dávkách aby nám neodstřelil SMTP :D

Takže v podstatě jen vyrenderuji blok konkrétní úlohy (např. informace o novém úkolu) a ten uložím do databáze. Konzole spuštěná cronem si blok vezme do své šablony a odešle ji kompletně.

Editoval igor.pocta (13. 1. 2017 22:29)

SontoEremo
Člen | 341
+
0
-

@CZechBoY
Takže sa mi to podarilo spojazdniť emaily chodia…
Problém nastáva ak chcem pridať link vyhadzuje to chybu Invalid link destination ‚activation‘.
EmailSender

<?php
namespace App\Emails;
use Tracy\Debugger;
use Nette\Mail\IMailer;
use Nette\Mail\Message;
use Nette\Mail\SmtpMailer;
use Nette\Mail\SmtpException;
use Latte\Loaders\StringLoader;
use Nette\Application\UI\Control;
use Nette\Application\LinkGenerator;
use Nette\Bridges\ApplicationLatte\UIMacros;
use Nette\Bridges\ApplicationLatte\ILatteFactory;

class EmailSender {

	/** @var Nette\Mail\IMailer @inject */
	private $mailer;

	/** @var ILatteFactory */
    private $latteFactory;

    /** @var Nette\Application\LinkGenerator */
	public $linkGenerator;

	public function __construct(IMailer $mailer, ILatteFactory $latteFactory, LinkGenerator $linkGenerator) {
		$this->mailer = $mailer;
		$this->linkGenerator = $linkGenerator;
		$this->latteFactory = $latteFactory;
	}

	public function sendEmail(Message $message, $template, $variables) {
		try {
			$latte = $this->latteFactory->create();
			UIMacros::install($latte->getCompiler());
			$latte->addProvider("uiControl", $this->linkGenerator);
			$html = $latte->renderToString(__DIR__ . '/messages/' . $template, ['variable' => $variables]);
	        $message->setFrom('Page Name <xx@xx.xx>')
	        ->setHtmlBody((string) $html,  __DIR__ . '/../../assets/global/img/');
	       $this->mailer->send($message);
		} catch (SmtpException $e) {
			$this->addError("Odoslanie mailu zlyhalo", "warning");
		}
	}
}

SignUpFormFacory

$template = $this->templateFactory->createTemplate();
			$template->verifyLink = $this->linkGenerator->link(
				'activation',
				[
				'token' => $token,
			]);

			$mail = new Message();
			$mail->addTo($values->email);
			$mail->setSubject('Aktivácia účtu na stránke Page Name');
			$this->emailSender->sendEmail($mail, 'activation.latte', $template);

Neviem ako povedať SignUpFormFacory že activation sa nachádza v App\Emails\Messages

CZechBoY
Člen | 3608
+
0
-

@SontoEremo musis dat dat celou urlk presenteru, ktery bude dany formular zpracovavat.

SontoEremo
Člen | 341
+
0
-

@CZechBoY
Dal som to do GlobalPResenter ktorý sa má createComponentSignUpForm()

/**
	 * Sign-up form factory.
	 * @return Nette\Application\UI\Form
	 */
	protected function createComponentSignUpForm() {
		return $this->signUpFactory->create(function () {
			$token = Random::generate(32);
			$template = $this->templateFactory->createTemplate();
			$template->verifyLink = $this->linkGenerator->link('activation', ['token' => $token]);
			$mail = new Message();
			$mail->addTo($values->email);
			$mail->setSubject('Aktivácia účtu na stránke Mystical Vision');
			$this->emailSender->sendEmail($mail, 'activation');
			$this->redirect(':Homepage:default');
		});
	}

Ale stále to vyhadzuje Invalid link destination ‚activation‘.
Musím teda šablónu activation.latte ktorú mám v App\Emails\Messages dať do App\Presenters\Templates\Global?
tam mám login.latte register.latte atď…

Editoval SontoEremo (14. 1. 2017 12:50)

David Matějka
Moderator | 6445
+
0
-

jde o to generovani linku

$this->linkGenerator->link('activation', ['token' => $token]);

nestaci jen activation, musis uvest vcetne presenteru (bez dvojtecky na zacatku)

SontoEremo
Člen | 341
+
0
-

David Matějka napsal(a):

jde o to generovani linku

$this->linkGenerator->link('activation', ['token' => $token]);

nestaci jen activation, musis uvest vcetne presenteru (bez dvojtecky na zacatku)

Lenže EmailSender nemá presenter a activation mám v App\Emails\Messages\ mám teda activation preniesť do App\Presenters\Templates\Global\?

David Matějka
Moderator | 6445
+
0
-

ale tim ->link vytvaris odkaz na nejaky presenter. na jaky?

SontoEremo
Člen | 341
+
0
-

predtým som mal activation v Global a link bol ->link(‚Global:activation‘, [‚token‘ ⇒ $token])
lenže teraz ako sa pokúšam mať mail ako Službu tak mám zložku Emails a vnej messages kde sa nachádza aj activation… neviem ako to teda spraviť…

David Matějka
Moderator | 6445
+
+1
-

odesilani mas jako sluzbu. ale ten odkaz, na ktery ten uzivatel prijde, musi byt presenter.

SontoEremo
Člen | 341
+
0
-

David Matějka napsal(a):

odesilani mas jako sluzbu. ale ten odkaz, na ktery ten uzivatel prijde, musi byt presenter.

až teraz keď som to začal písať mi to zaplo ja som mal použiť

$template->setFile('App\Emails\Message\activation.latte');

čo sa odosiela na mail a

$template->verifyLink = $this->linkGenerator->link('Global:activationAccount', ['token' => $token]);

čo užívateľa prenesie na domena.xx/global/activationAccount

lenže mi to vyhadzuje chybu Undefined variable: verifyLink

<a href="{$verifyLink|noescape}">
				Aktivovať účet
			</a>

Ako dostať $verifyLink do App\Emails\Message\activation.latte

David Matějka
Moderator | 6445
+
0
-

ted na to koukam poradne a mas to (zas) nejaky pomateny. v signupformfactory vytvaris template, kterou posilas do sendMail jako treti parametr variables a to pak posilas do dalsi sablony… zkus se nad tim zamyslet, jestli je to opravdu tahle spravne ;)

SontoEremo
Člen | 341
+
0
-

@DavidMatějka
zo SignUpFormFactory som to preniesol do GlobalPresenter

/**
	 * Sign-up form factory.
	 * @return Nette\Application\UI\Form
	 */
	protected function createComponentSignUpForm() {
		return $this->signUpFactory->create(function () {
			$token = Random::generate(32);
			$template = $this->templateFactory->createTemplate();
			$template->setFile('App\Emails\Message\activation.latte');
			$template->verifyLink = $this->linkGenerator->link('Global:activationAccount', ['token' => $token]);
			$mail = new Message();
			$mail->addTo('email@email.xx');
			$mail->setSubject('Aktivácia účtu na stránke PageName');
			$this->emailSender->sendEmail($mail, 'activation', $template);
			$this->redirect('this');
		});
	}

SignUpFormFactory sa už stará iba o insert

Editoval SontoEremo (14. 1. 2017 14:18)

SontoEremo
Člen | 341
+
0
-

Tak sa mi to podarilo takto hádam je to dobré…
SendMailFacory

<?php
namespace App\Emails;
use Tracy\Debugger;
use Nette\Object;
use Nette\Mail\IMailer;
use Nette\Mail\Message;
use Nette\Mail\SmtpException;


class SendMailFacory {

	/** @var Nette\Mail\IMailer @inject */
	private $mailer;

	public function __construct(IMailer $mailer) {
		$this->mailer = $mailer;
	}

	public function sendEmail(Message $message) {
		try {
	        $message->setFrom('PageName <email@email.xx>');
	       $this->mailer->send($message);
		} catch (SmtpException $e) {
			$this->addError("Odoslanie mailu zlyhalo", "warning");
		}
	}
}

GlobalPresenter

/**
	 * Sign-up form factory.
	 * @return Nette\Application\UI\Form
	 */
	protected function createComponentSignUpForm() {
		return $this->signUpFactory->create(function () {
			$token = Random::generate(32);
			$template = $this->templateFactory->createTemplate();
			$template->setFile('App/Emails/Messages/activation.latte');
			$template->verifyLink = $this->linkGenerator->link('Global:activationAccount', ['token' => $token]);
			$mail = new Message();
			$mail->addTo('email@email.xx');
			$mail->setSubject('Aktivácia účtu na stránke Page Name');
			$mail->setHtmlBody((string) $template, __DIR__ . '/../../assets/global/img/');
			$this->sendMailFactory->sendEmail($mail, $template);
			$this->redirect('this');
		});
	}