Jak spustit Presenter:akce z konzole / CLI
- ludek
- Člen | 83
Zdravím,
mám napsanou akci na posílání emailů.
Když to spustím v prohlížeči
http://localhost/test/www/email/daily
, bez problémů funguje.
Není to nic zvláštního (vizte níže), ale dědí to z BasePresenteru, využívá šablon, atd.
DOTAZ: co mám udělat, abych to mohl spustit z konzole (CLI) každý den
pomocí cronu?
Jaký je nejjednodušší způsob jak z konzole zavolat to, co jsem si
v prohlížeči otestoval?
Našel jsem:
php -d display_errors=1 -f index.php Email:daily
→ neudělá nic, nevypíše ani žádné chybyKdyby\Console
aContributte\Console
, ale nechápu, jestli je to nástroj k tomu, co potřebuju. Umožní mi to spustitbin/console Email:daily
?
Díky.
_______________
Co se má spouštět vypadá takto:
namespace App\Presenters;
use Nette;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
final class EmailPresenter extends BasePresenter {
/** @var Nette\Mail\IMailer Mailer */
private $mailer;
/**
* @param Nette\Mail\IMailer $mailer
*/
public function injectMailer(Nette\Mail\IMailer $mailer) {
$this->mailer = $mailer;
}
public function renderDaily(): void {
$users = $this->repository->table('users')->fetchAll();
foreach ($users as $u) {
$stuff = $this->repository->table('stuff')->where('users_id', $u->id);
$this->sendMail($u, $stuff);
sleep(1);
}
$this->terminate();
}
public function sendMail(Nette\Database\Table\ActiveRow $user, Nette\Database\Table\Selection $stuff): void {
$latte = new \Latte\Engine;
$params['stuff'] = $stuff;
$params['user'] = $user;
$subject = 'Your stuff ' . $this->today->format('d-m-Y');
$mail = new Message;
$sendTo = $user->email;
$mail->setFrom('robot@domain.com')
->addTo($sendTo)
->setSubject($subject)
->setHtmlBody($latte->renderToString(__DIR__ . '/../templates/email.latte', $params));
try { $this->mailer->send($mail); echo "<br> SENT " . $sendTo; }
catch (Nette\Mail\SendException $e) { echo " ERROR " . $e->getMessage(); }
}
}
- Polki
- Člen | 553
Contributte/Console
udělá práci za tebe, akorát pro ni musíš tu knihovnu na odesílání
mailů upravit, aby byla přes tu knihovnu spustitelná viz dokumentace.
Takže by tvoje třída vypadala asi takto:
/**
*
* @cron 0 * * * * // definice času spouštění jako u klasického cronu.
*/
final class EmailCronCommand extends Command {
protected static $defaultName = 'email:send';
/** @var Nette\Mail\IMailer Mailer */
private $mailer;
public function __construct(Nette\Mail\IMailer $mailer) {
$this->mailer = $mailer;
// další inject věci
}
protected function configure(): void
{
$this->setName(self::$defaultName);
$this->setDescription('This is command for execute emails.'); // Použij lepší hlášku
}
public function execute(InputInterface $input, OutputInterface $output): void {
$users = $this->repository->table('users')->fetchAll();
foreach ($users as $u) {
$stuff = $this->repository->table('stuff')->where('users_id', $u->id);
$this->sendMail($u, $stuff);
sleep(1);
}
}
public function sendMail(Nette\Database\Table\ActiveRow $user, Nette\Database\Table\Selection $stuff): void {
$latte = new \Latte\Engine;
$params['stuff'] = $stuff;
$params['user'] = $user;
$subject = 'Your stuff ' . $this->today->format('d-m-Y');
$mail = new Message;
$sendTo = $user->email;
$mail->setFrom('robot@domain.com')
->addTo($sendTo)
->setSubject($subject)
->setHtmlBody($latte->renderToString(__DIR__ . '/../templates/email.latte', $params));
try { $this->mailer->send($mail); echo "<br> SENT " . $sendTo; }
catch (Nette\Mail\SendException $e) { echo " ERROR " . $e->getMessage(); }
}
}
a spouštěla se takto:
$ php console.php email:send
Doufám, že jsem nic nepopletl případně mě tvůrci console opravte, nebo ukamenujte :D
Pokud nechceš použít contributte/console, tak můžeš alternativně si to
spustit sám:
soubor s kódem:
final class EmailCronCommand{
/** @var Nette\Mail\IMailer Mailer */
private $mailer;
public function __construct(Nette\Mail\IMailer $mailer) {
$this->mailer = $mailer;
// další inject věci
}
public function execute(): void {
$users = $this->repository->table('users')->fetchAll();
foreach ($users as $u) {
$stuff = $this->repository->table('stuff')->where('users_id', $u->id);
$this->sendMail($u, $stuff);
sleep(1);
}
}
public function sendMail(Nette\Database\Table\ActiveRow $user, Nette\Database\Table\Selection $stuff): void {
$latte = new \Latte\Engine;
$params['stuff'] = $stuff;
$params['user'] = $user;
$subject = 'Your stuff ' . $this->today->format('d-m-Y');
$mail = new Message;
$sendTo = $user->email;
$mail->setFrom('robot@domain.com')
->addTo($sendTo)
->setSubject($subject)
->setHtmlBody($latte->renderToString(__DIR__ . '/../templates/email.latte', $params));
try { $this->mailer->send($mail); echo "<br> SENT " . $sendTo; }
catch (Nette\Mail\SendException $e) { echo " ERROR " . $e->getMessage(); }
}
}
config:
services:
- Namespace\EmailCronCommand
napřklad emailCron.php:
#!/usr/bin/env php
<?php declare(strict_types = 1);
require __DIR__ . '/../vendor/autoload.php';
exit(App\Bootstrap::boot()
->createContainer()
->getByType(Namespace\EmailCronCommand::class)
->execute());
spuštění:
php emailCron.php
Rozdíl, mezi těmito 2 přístupy je, že contributte/console ti všechny
crony zpřístupní přes 1 entrypoint a můžeš je spouštět podle
parametru, což je strašně super. Dál má další fičury, co ti pomůžou,
ale pokud chceš používat to, jak jsem zmínil v anotaci @cron, tak
musíš si k tomu napsat vlastní obslužný skript (případně dodám).
Druhé řešení má zase tu výhodu, že není závislé na žádné knihovně
a když se autor knihovny rozhodne nepokračovat a nebude mít následníky,
nemusíš překopávat aplikace. (Což si myslím v případě Contributte
nehrozí)
Oba příkazy pak můžeš spouštět cyklicky cronem, takže z pohledu spuštění je to jedno, co použiješ. Záleží, jak se cítíš. Nicméně celá logika by měla být v modelu a ne v Presenteru. Kterákoliv z těch 2 tříd, co jsem popsal nahoře je modelová service, která funguje ať ji spouštíš z presenteru, nebo z CLI. No a z presenteru se to pak dá spouštět takto:
final class EmailPresenter extends BasePresenter {
/** @var EmailCronCommand Mailer */
private $mailer;
public function injectMailer(EmailCronCommand $mailer) {
$this->mailer = $mailer;
}
public function renderDaily(): void {
$this->mailer->execute();
}
}
a volat pak jak psal Milo
wget http://localhost/test/www/email/daily
Otázka je, jestli chceš to mít přístupné přes URL. Pokud ne, tak když to je napsáno správně (logika v service ne v Presenteru), tak tu servicu je jedno odkud spustíš, takže ji můžeš klidně spouštět přes CLI
Editoval Polki (7. 10. 2021 0:40)
- ludek
- Člen | 83
@Milo wget mě taky napadl a původně jsem to tak udělal. Ale i když by to asi nebyl problém, spíše z principu nechci, aby to bylo přístupné přes URL. Díky.
@Polki Skvělá odpověď!
Napsal jsem modelovou třídu. Víc cronů nepotřebuju a tak je asi lepší
vyhnout se závislosti na Contributte/Console
.
1. modelová třída (umístit do
app/model/ModelovaTrida.php
)
<?php
namespace App\Model;
use Nette;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
final class ModelovaTrida {
use Nette\SmartObject;
/** @var Nette\Database\Context Pristup k databazi */
private $repository;
/** @var Nette\Mail\IMailer Pro posilani emailu */
private $mailer;
// předat závislosti (spojení na databázi a mailer)
public function __construct(Nette\Database\Context $database, Nette\Mail\IMailer $mailer) {
$this->repository = $database;
$this->mailer = $mailer;
}
/**
* Nějaké úkony s databází, šablonami a posíláním emailů
* @return void
*/
public function execute(): void {
$params = $this->repository->table('table');
$latte = new \Latte\Engine;
$mail = new Message;
$mail->setFrom('email@domain.com')
->addTo('email@domain.com')
->setSubject('Předmět zprávy')
->setHtmlBody($latte->renderToString(__DIR__ . '/../templates/email.latte', $params));
try { $this->mailer->send($mail); echo "\nSENT ". date('Y-m-d H:i:s'); }
catch (Nette\Mail\SendException $e) { echo "\nERROR ".$e->getMessage(); }
}
} // class
2. zaregistrovat v config/local.neon
services:
- App\Model\ModelovaTrida
2.5 smazat cache (když to neudělám, v TRACY/DIC panelu to vidím, ale při spuštění z konzole napíše, že neexistuje)
3. spouštěcí script
/cron/spustit-modelovou-tridu.php
#!/usr/bin/env php
<?php declare(strict_types = 1);
require __DIR__ . '/../vendor/autoload.php';
exit(App\Bootstrap::boot()
->createContainer()
->getByType(App\Model\ModelovaTrida::class)
->execute());
4. spustit z konzole v /cron
php spustit-modelovou-tridu.php
Funguje perfektně. Ještě jednou moc díky za vyčerpávající odpověď.
- Marek Znojil
- Člen | 90
@ludek Tady se můžeš ještě inspirovat jak si popřípadě zařídit i rozdílnou konfiguraci přímo pro tasky.