ITemplate nevyžaduje __toString() ani renderToString()
- petr.pavel
- Člen | 535
Objevil jsem koncepční problém v renderování šablony do stringu.
V dokumentaci je příklad:
<?php
class MailSender
{
...
/** @var Nette\Application\UI\ITemplateFactory */
private $templateFactory;
public function sendEmail(): void
{
$template = $this->createTemplate();
...
$mail = new Message;
$mail->setHtmlBody($template);
}
protected function createTemplate(): Nette\Application\UI\ITemplate
{
$template = $this->templateFactory->createTemplate();
...
return $template;
}
}
?>
Vyžadujeme ITemplateFactory
, které vrací instanci
ITemplate
. Tuto instanci předáváme metodě, která vyžaduje
string
. Tohle za určitých okolností funguje:
- používáme pouze TemplateFactory z Nette a nekonfigurujeme ji, aby nám vracela jinou třídu
- proto vrací
\Nette\Bridges\ApplicationLatte\Template
, která implementuje__toString
- nedeklarujeme
strict_types=1
Nic moc. Jaké je ale řešení?
Problém (3) vyřeší ruční přetypování
$mail->setHtmlBody((string) $template);
ale tím
nekončíme.
Varianta 1: nepoužívat interface, ale konkrétní
třídy
Tím jdu ale proti SOLID a navíc závislost na TemplateFactory
stejně nezajistí, že vrací Template
. Nepovažuju za správné
řešení.
Varianta 2: nadefinuju si vlastní interface a obálkové
třídy, které nakonec vrátí interface, který vyžaduje implementaci
__toString
IMyTemplateFactory extends ITemplateFactory, MyTemplateFactory extends TemplateFactory implements IMyTemplateFactory { public createTemplate(): IMyTemplate {...}}, IMyTemplate extends ITemplate { public function __toString(); }
…
To tady mám pro úplnost, taky nepovažuju za správné řešení, příliš
mnoho „zbytečného“ psaní.
Varianta 3: vykašlu se na __toString a volám
renderToString
Jenže to je to samé v bledě modrém – ITemplate také nevyžaduje
existenci této metody
Varianta 4: do MailSender
zkopíruju
vnitřek Template::__toString
<?php
try {
return $this->latte->renderToString($this->file, $this->params);
...
?>
Předpokládám, že šablona má vlastnost Latte $latte, což v ITemplate opět není. Takže zase v bledě modrém to samé.
Varianta 5: změnit ITemplate, aby vyžadovala __toString
To mně omezuje, pokud v aplikaci nikdy renderování do stringu nepotřebuju. Asi nejmenší zlo, ne? Vždycky můžu zaplácnout stubem.
Co myslíte? Něco mi uniklo?
- David Grudl
- Nette Core | 8218
Ještě je možnost vyrenderovat do stringu pomocí
$str = Nette\Utils\Helpers::capture(function () use ($template) { $template->render(); });
- petr.pavel
- Člen | 535
David Grudl napsal(a):
Ještě je možnost vyrenderovat do stringu pomocí
$str = Nette\Utils\Helpers::capture(function () use ($template) { $template->render(); })
Zajímavá technika, díky.
Je lepší než přidat renderToString anebo __toString do ITemplate?
Zkouším si představit ten příklad v dokumentaci:
<?php
$mail = new Message;
$renderedTemplate = Nette\Utils\Helpers::capture(function () use ($template) { $template->render(); })
$mail->setHtmlBody($renderedTemplate);
?>
- David Grudl
- Nette Core | 8218
Čistě technicky, do PHP 7.4 je nevýhoda __toString v tom, že v nich
nelze vyhodit výjimky, zabily by tím aplikaci. Zase od 7.4 se dá to
zachytávání napsat víc elegantně
Nette\Utils\Helpers::capture(fn() => $template->render())
.
Když by se přidávala metoda do rozhraní, připadalo by mi
srozumitelnější
$mail->setHtmlBody($template->renderToString);
než $mail->setHtmlBody((string) $template);
Na druhou stranu, v PHP 8.0 se objeví rozhraní Stringable a union typy,
takže bude možné upravit Mail, aby přijímal Stringable|string
a mohlo by fungovat $mail->setHtmlBody($template);