Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/en/components/mail.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,13 @@ return [

```shell
php bin/hyperf.php gen:mail TestMail
php bin/hyperf.php gen:mail TestMail --markdown
```

`--markdown` (or `-m`) is a flag and takes no value. It generates a mailable referencing
`mail.test-mail` for `TestMail`; create that Blade template separately in your configured view
directory. Without the flag, the generated class uses a regular view.

```php
// app/Mail/TestMail.php

Expand Down Expand Up @@ -217,6 +222,22 @@ class TestMail extends Mailable
}
```

For pre-rendered HTML, return `Content(htmlString: ...)` from your mailable. The HTML is used
directly without rendering a Blade view:

```php
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;

class HtmlMail extends Mailable
{
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
}
```

### Defining the Controller or Service

```php
Expand Down
20 changes: 20 additions & 0 deletions docs/zh-cn/components/mail.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ return [

```shell
php bin/hyperf.php gen:mail TestMail
php bin/hyperf.php gen:mail TestMail --markdown
```

`--markdown`(或 `-m`)是无需传值的开关。对于 `TestMail`,生成的邮件类引用 `mail.test-mail`;
请在配置的视图目录中另行创建对应的 Blade 模板。不使用该开关时,生成的类使用普通视图。

```php
// app/Mail/TestMail.php

Expand Down Expand Up @@ -218,6 +222,22 @@ class TestMail extends Mailable
}
```

对于已渲染的 HTML,可在 mailable 中返回 `Content(htmlString: ...)`,直接作为邮件正文,
无需渲染 Blade 视图:

```php
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;

class HtmlMail extends Mailable
{
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
}
```

### 定义控制器或 Service

```php
Expand Down
20 changes: 20 additions & 0 deletions docs/zh-hk/components/mail.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ return [

```shell
php bin/hyperf.php gen:mail TestMail
php bin/hyperf.php gen:mail TestMail --markdown
```

`--markdown`(或 `-m`)是無需傳值的開關。對於 `TestMail`,生成的郵件類引用 `mail.test-mail`;
請在配置的視圖目錄中另行創建對應的 Blade 模板。不使用該開關時,生成的類使用普通視圖。

```php
// app/Mail/TestMail.php

Expand Down Expand Up @@ -218,6 +222,22 @@ class TestMail extends Mailable
}
```

對於已渲染的 HTML,可在 mailable 中返回 `Content(htmlString: ...)`,直接作為郵件正文,
無需渲染 Blade 視圖:

```php
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;

class HtmlMail extends Mailable
{
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
}
```

### 定義控制器或 Service

```php
Expand Down
20 changes: 20 additions & 0 deletions docs/zh-tw/components/mail.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ return [

```shell
php bin/hyperf.php gen:mail TestMail
php bin/hyperf.php gen:mail TestMail --markdown
```

`--markdown`(或 `-m`)是無需傳值的開關。對於 `TestMail`,生成的郵件類引用 `mail.test-mail`;
請在配置的檢視目錄中另行建立對應的 Blade 模板。不使用該開關時,生成的類使用普通檢視。

```php
// app/Mail/TestMail.php

Expand Down Expand Up @@ -218,6 +222,22 @@ class TestMail extends Mailable
}
```

對於已渲染的 HTML,可在 mailable 中返回 `Content(htmlString: ...)`,直接作為郵件正文,
無需渲染 Blade 檢視:

```php
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;

class HtmlMail extends Mailable
{
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
}
```

### 定義控制器或 Service

```php
Expand Down
21 changes: 21 additions & 0 deletions src/mail/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,13 @@ return [

```shell
php bin/hyperf.php gen:mail TestMail
php bin/hyperf.php gen:mail TestMail --markdown
```

`--markdown` (or `-m`) is a flag and takes no value. It generates a mailable referencing
`mail.test-mail` for `TestMail`; create that Blade template separately in your configured view
directory. Without the flag, the generated class uses a regular view.

```php
// app/Mail/TestMail.php

Expand Down Expand Up @@ -224,6 +229,22 @@ class TestMail extends Mailable
}
```

For pre-rendered HTML, return `Content(htmlString: ...)` from your mailable. The HTML is used
directly without rendering a Blade view:

```php
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;

class HtmlMail extends Mailable
{
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
}
```

### Your Controller or Service

```php
Expand Down
6 changes: 3 additions & 3 deletions src/mail/src/Command/MailCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __construct(

protected function getStub(): string
{
return $this->getConfig()['stub'] ?? ($this->input->getOption('markdown') !== false ? __DIR__ . '/stubs/mail.stub' : __DIR__ . '/stubs/markdown-mail.stub');
return $this->getConfig()['stub'] ?? ($this->input->getOption('markdown') ? __DIR__ . '/stubs/markdown-mail.stub' : __DIR__ . '/stubs/mail.stub');
}

protected function getDefaultNamespace(): string
Expand Down Expand Up @@ -81,7 +81,7 @@ protected function getView(): string
{
$view = $this->input->getOption('markdown');

if (! $view) {
if (! is_string($view) || $view === '') {
$name = str_replace('\\', '/', $this->input->getArgument('name'));

$view = 'mail.' . collect(explode('/', $name))
Expand All @@ -103,7 +103,7 @@ protected function buildClass(string $name): string
parent::buildClass($name)
);

if ($this->input->getOption('markdown') !== false) {
if ($this->input->getOption('markdown')) {
$class = str_replace(['DummyView', '{{ view }}'], $this->getView(), $class);
}

Expand Down
8 changes: 4 additions & 4 deletions src/mail/src/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ protected function sendMailable(MailableContract $mailable): ?SentMessage
/**
* Parse the given view name or array.
*/
protected function parseView(null|Closure|array|string $view): array
protected function parseView(null|Closure|Htmlable|HtmlString|array|string $view): array
{
if (is_string($view) || $view instanceof Closure) {
if (is_string($view) || $view instanceof Closure || $view instanceof Htmlable || $view instanceof HtmlString) {
return [$view, null, null];
}

Expand Down Expand Up @@ -315,7 +315,7 @@ protected function parseView(null|Closure|array|string $view): array
/**
* Add the content to a given message.
*/
protected function addContent(Message $message, null|Closure|string $view, null|Closure|string $plain, ?string $raw, array $data = []): void
protected function addContent(Message $message, null|Closure|Htmlable|HtmlString|string $view, null|Closure|Htmlable|HtmlString|string $plain, ?string $raw, array $data = []): void
{
if (isset($view)) {
$message->html($this->renderView($view, $data) ?: ' ');
Expand All @@ -333,7 +333,7 @@ protected function addContent(Message $message, null|Closure|string $view, null|
/**
* Render the given view.
*/
protected function renderView(Closure|string $view, array $data): string
protected function renderView(Closure|Htmlable|HtmlString|string $view, array $data): string
{
$view = value($view, $data);
if ($view instanceof Htmlable) {
Expand Down
62 changes: 62 additions & 0 deletions tests/Mail/MailCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact huangdijia@gmail.com
*/
use FriendsOfHyperf\Mail\Command\MailCommand;
use Hyperf\Config\Config;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Support\Filesystem\Filesystem;
use Symfony\Component\Console\Tester\CommandTester;

beforeEach(function () {
$this->config = new Config([]);
$this->swap(ConfigInterface::class, $this->config);
$this->files = new Filesystem();
$this->directory = tempnam(sys_get_temp_dir(), 'mail-command-');
unlink($this->directory);
$this->files->makeDirectory($this->directory);
});

afterEach(function () {
$this->files->deleteDirectory($this->directory);
});

it('generates a mailable with the correct content definition', function (string $name, array $options, string $namespace, string $content) {
$tester = new CommandTester(new MailCommand($this->files, $this->config));

$tester->execute(['name' => $name, '--path' => $this->directory] + $options);

$tester->assertCommandIsSuccessful();
$class = $this->files->get($this->directory . '/WelcomeMail.php');
expect($class)
->toContain('namespace ' . $namespace . ';')
->toContain('class WelcomeMail extends Mailable')
->toContain("subject: 'Welcome Mail'")
->toContain($content)
->not->toContain('{{');
})->with([
'view mailable' => ['WelcomeMail', [], 'App\Mail', "view: 'view.name'"],
'markdown flag' => ['WelcomeMail', ['--markdown' => true], 'App\Mail', "markdown: 'mail.welcome-mail'"],
'short markdown flag' => ['WelcomeMail', ['-m' => true], 'App\Mail', "markdown: 'mail.welcome-mail'"],
'slash namespace' => ['Orders/WelcomeMail', ['--markdown' => true], 'App\Mail\Orders', "markdown: 'mail.orders.welcome-mail'"],
'backslash namespace' => ['Orders\WelcomeMail', ['--markdown' => true], 'App\Mail\Orders', "markdown: 'mail.orders.welcome-mail'"],
]);

it('respects the configured mail stub', function () {
$stub = $this->directory . '/custom.stub';
$this->files->put($stub, '<?php namespace %NAMESPACE%; class %CLASS% { /* custom {{ view }} */ }');
$this->config->set('devtool.generator.mail.stub', $stub);
$tester = new CommandTester(new MailCommand($this->files, $this->config));

$tester->execute(['name' => 'WelcomeMail', '--path' => $this->directory, '--markdown' => true]);

$tester->assertCommandIsSuccessful();
expect($this->files->get($this->directory . '/WelcomeMail.php'))
->toBe('<?php namespace App\Mail; class WelcomeMail { /* custom mail.welcome-mail */ }');
});
81 changes: 81 additions & 0 deletions tests/Mail/MailHtmlContentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact huangdijia@gmail.com
*/
use FriendsOfHyperf\Mail\Mailable;
use FriendsOfHyperf\Mail\Mailable\Content;
use FriendsOfHyperf\Mail\Mailer;
use FriendsOfHyperf\Mail\Message;
use FriendsOfHyperf\Mail\Transport\ArrayTransport;
use FriendsOfHyperf\Support\HtmlString;
use Hyperf\ViewEngine\Contract\FactoryInterface as Factory;
use Hyperf\ViewEngine\Contract\Htmlable;
use Mockery as m;

it('sends HTML and text strings without view callbacks', function () {
$view = m::mock(Factory::class);
$view->shouldNotReceive('make');
$mailer = new Mailer('array', $view, new ArrayTransport());

$sentMessage = $mailer->send(
['html' => new HtmlString('<p>Hello Hyperf</p>'), 'text' => new HtmlString('Hello World')],
[],
function (Message $message) {
$message->to('recipient@example.com')->from('sender@example.com');
}
);

$this->assertSame('<p>Hello Hyperf</p>', $sentMessage->getOriginalMessage()->getHtmlBody());
$this->assertSame('Hello World', $sentMessage->getOriginalMessage()->getTextBody());
});

it('sends Htmlable content without rendering a view', function () {
$view = m::mock(Factory::class);
$view->shouldNotReceive('make');
$mailer = new Mailer('array', $view, new ArrayTransport());
$html = m::mock(Htmlable::class);
$html->expects('toHtml')->andReturn('<p>Hello Hyperf</p>');
$text = m::mock(Htmlable::class);
$text->expects('toHtml')->andReturn('Hello World');

$sentMessage = $mailer->send(['html' => $html, 'text' => $text], [], function (Message $message) {
$message->to('recipient@example.com')->from('sender@example.com');
});

$this->assertSame('<p>Hello Hyperf</p>', $sentMessage->getOriginalMessage()->getHtmlBody());
$this->assertSame('Hello World', $sentMessage->getOriginalMessage()->getTextBody());
});

it('renders HTML and text string content', function () {
$view = m::mock(Factory::class);
$view->shouldNotReceive('make');
$mailer = new Mailer('array', $view, new ArrayTransport());

$this->assertSame('<p>Hello Hyperf</p>', $mailer->render(['html' => new HtmlString('<p>Hello Hyperf</p>')]));
$this->assertSame('Hello World', $mailer->render(['text' => new HtmlString('Hello World')]));
});

it('sends a mailable with pre-rendered HTML content', function () {
$view = m::mock(Factory::class);
$view->shouldNotReceive('make');
$transport = new ArrayTransport();
$mailer = new Mailer('array', $view, $transport);
$mailable = new class extends Mailable {
public function content(): Content
{
return new Content(htmlString: '<h1>Hello</h1><p>Mail body</p>');
}
};
$mailable->from('sender@example.com')->to('recipient@example.com')->subject('Greeting');

$sentMessage = $mailer->send($mailable);

$this->assertCount(1, $transport->messages());
$this->assertSame('<h1>Hello</h1><p>Mail body</p>', $sentMessage->getOriginalMessage()->getHtmlBody());
});
Loading