Předání závislého interface

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

Ahoj,

chtěl jsem si vytvořit obecnější odesílač emailu.

Mám třídy a interface:

<?php
namespace App\Model\Mails;

class MailTemplateFactory extends \Nette\Object {
    const DEFAULT_FROM = "test@test.cz";

    private $template;

    public function __construct(IMailTemplate $template) {

        $this->template = $template;
    }

    public function getMailTemplate($subject, $body, $to, $from = self::DEFAULT_FROM) {
        return $this->template->getMailTemplate($subject, $body, $to,
                $from);
    }
}
?>
<?php
namespace App\Model\Mails;

interface IMailTemplate {
    public function getMailTemplate($subject, $body, $to, $from);
}
?>
<?php
class NetteMailTemplate implements IMailTemplate {
    private $template;

    public function __construct() {
        $this->template = new \Nette\Mail\Message();
    }

    public function getMailTemplate($subject, $body, $to, $from) {
        $this->template = new \Nette\Mail\Message();
        $this->template->setFrom($from);
        $this->template->setBody($body);
        $this->template->setSubject($subject);
        $this->template->addTo($to);

        return $this->template;
    }
}
?>
<?php
namespace App\Model\Mails;

use Nette;

class MailSenderFactory extends Nette\Object {
    private $mailSender;

    public function __construct(IMailSender $mailSender) {
        $this->mailSender = $mailSender;
    }

    public function send(IMailTemplate $template) {
        $this->mailSender->send($template);
    }
}

?>
<?php
namespace App\Model\Mails;

interface IMailSender {
    function send(IMailTemplate $template);
}

?>
<?php
class NetteMailSender implements IMailSender {

    public function __construct() {
    }

    public function send(IMailTemplate $template) {
        $mailSender = new Nette\Mail\SendmailMailer();
        $mailSender->send($template);
    }

}
?>

Pak si v konstruktoru předávám závislosti na továrničky.

<?php
public function __construct(Nette\Database\Context $database,
            Mails\MailTemplateFactory $mailTemplateFactory, Mails\MailSenderFactory $mailSenderFactory) {
?>

A v neonu mám:

<?php
services:
	- App\Model\UserManager
	- App\RouterFactory
	- App\Model\Documents
	- App\Model\FileFeeder
	- App\Model\Users
	- App\Model\Mails\MailSenderFactory
	- App\Model\Mails\MailTemplateFactory
	- App\Model\Mails\IMailSender
	- App\Model\Mails\IMailTemplate
?>

Takhle mi to po spuštění zahlásí:
Interface App\Model\Mails\IMailSender used in service ‚61_App_Model_Mails_IMailSender‘ must have just one non-static method create() or get().

A pokud smažu poslední 2 řádky v neonu, tak mi to hlásí:
Service ‚61_App_Model_Mails_MailSenderFactory‘: No service of type App\Model\Mails\IMailSender found. Make sure the type hint in App\Model\Mails\MailSenderFactory::__construct() is written correctly and service of this type is registered

Mohl by mi někdo poradit, jak na to?
Budu rád za každou radu nebo kritiku.

Díky.

Michal Vyšinský
Člen | 608
+
+1
-

Ahoj,

interface je to, co používáš na injektování (v konstruktoru). Do neon dáváš třídu konkrétní implementace takže:

App\Model\Mails\IMailSender → NetteMailSender
App\Model\Mails\IMailTemplate → NetteMailTemplate

Tím, že tam předáš interface si nette myslí, že má generovat továrničku podle daného interface. Použití interfaců pro závislosti je dobrý postup – lehce jde cokoliv změnit. V neon konfiguraci ale dáváš konkrétní konfiguraci pro konkrétní aplikaci, takže tam se dávají už konkrétní implementace.

tttpapi
Člen | 100
+
0
-

Díky moc.

Ještě bych se chtěl zeptat, jak to vyřeším, když chci, aby mi na localhostu to běželo na A, na devu na B a na produkci na C mail?

Do neonu teprve pronikám. Dají se nějak vytvořit, aby se automaticky přepínali, podle nějakého nastavení?

Michal Vyšinský
Člen | 608
+
0
-

Na tyto specifické věci (mail, databáze) je dobré mít config/local.neon, který nebudeš mít v gitu (jestli jej používáš).

Pak v boostrapu přidáš:

$configurator->addConfig(__DIR__ . '/config/local.neon');

V local.neon můžeš mít veškerá specifická nastavení jako je právě připojení k databázi, logování atd. Můžeš tam dát i definici služeb.

tttpapi
Člen | 100
+
0
-

To mám. Ještě bych ale teda potřeboval mít různé configy pro různá prostředí – a zajímalo by mě, jestli je možnost si je nějak automaticky přepínat, nebo jestli to musím někde vždycky po přesunu ručně změnit, že např. místo config.production.neon bude používat config.localhost.neon…

Michal Vyšinský
Člen | 608
+
0
-

Možná něco takového, když local.neon nevyhovuje:

vytvořit soubor config/env.php:

<?php
return 'production';

A přidat do bootstrap.php:

$env = require __DIR__ . '/config/evn.php';
$configurator->addConfig(__DIR__ . '/config/config.' . $env . '.neon');
tttpapi
Člen | 100
+
0
-

ok, díky

Jan Endel
Člen | 1016
+
0
-

To je relativně jednoduché, řešením jsou sekce v config.neonu, nějak takto:

common:
	database:
		dbname: my_app

production < common:
	database:
		user: my_app
		password: xgarR4tGV$dx5#
		host: example.com
		port: 3307

development < common:
	database:
		user: pilec
		password: jakobin
		host: localhost

Pozn. znak < je v neonu použit pro dědění sekcí

tttpapi
Člen | 100
+
0
-

Jan Endel napsal(a):

To je relativně jednoduché, řešením jsou sekce v config.neonu, nějak takto:

common:
	database:
		dbname: my_app

production < common:
	database:
		user: my_app
		password: xgarR4tGV$dx5#
		host: example.com
		port: 3307

development < common:
	database:
		user: pilec
		password: jakobin
		host: localhost

Pozn. znak < je v neonu použit pro dědění sekcí

To je přesně to, co jsem chtěl.
A jak ještě oznámím aplikaci, na kterém prostředí se nacházím?

Jan Endel
Člen | 1016
+
0
-

V bootstrapu třeba takhle:

$configurator->addParameters(array(
	'environment' => $configurator->isDebugMode() ? 'development' : 'production',
));