ok
Direktori : /home2/selectio/public_html/fms-worksuite/vendor/laravel/framework/src/Illuminate/Mail/ |
Current File : /home2/selectio/public_html/fms-worksuite/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php |
<?php namespace Illuminate\Mail; use Illuminate\Config\Repository as ConfigRepository; use Illuminate\Container\Container; use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; use Illuminate\Contracts\Mail\Attachable; use Illuminate\Contracts\Mail\Factory as MailFactory; use Illuminate\Contracts\Mail\Mailable as MailableContract; use Illuminate\Contracts\Queue\Factory as Queue; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Contracts\Support\Renderable; use Illuminate\Contracts\Translation\HasLocalePreference; use Illuminate\Support\Collection; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; use Illuminate\Support\Traits\Conditionable; use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\Support\Traits\Localizable; use Illuminate\Support\Traits\Macroable; use Illuminate\Testing\Constraints\SeeInOrder; use PHPUnit\Framework\Assert as PHPUnit; use ReflectionClass; use ReflectionProperty; use Symfony\Component\Mailer\Header\MetadataHeader; use Symfony\Component\Mailer\Header\TagHeader; use Symfony\Component\Mime\Address; class Mailable implements MailableContract, Renderable { use Conditionable, ForwardsCalls, Localizable, Macroable { __call as macroCall; } /** * The locale of the message. * * @var string */ public $locale; /** * The person the message is from. * * @var array */ public $from = []; /** * The "to" recipients of the message. * * @var array */ public $to = []; /** * The "cc" recipients of the message. * * @var array */ public $cc = []; /** * The "bcc" recipients of the message. * * @var array */ public $bcc = []; /** * The "reply to" recipients of the message. * * @var array */ public $replyTo = []; /** * The subject of the message. * * @var string */ public $subject; /** * The Markdown template for the message (if applicable). * * @var string */ public $markdown; /** * The HTML to use for the message. * * @var string */ protected $html; /** * The view to use for the message. * * @var string */ public $view; /** * The plain text view to use for the message. * * @var string */ public $textView; /** * The view data for the message. * * @var array */ public $viewData = []; /** * The attachments for the message. * * @var array */ public $attachments = []; /** * The raw attachments for the message. * * @var array */ public $rawAttachments = []; /** * The attachments from a storage disk. * * @var array */ public $diskAttachments = []; /** * The tags for the message. * * @var array */ protected $tags = []; /** * The metadata for the message. * * @var array */ protected $metadata = []; /** * The callbacks for the message. * * @var array */ public $callbacks = []; /** * The name of the theme that should be used when formatting the message. * * @var string|null */ public $theme; /** * The name of the mailer that should send the message. * * @var string */ public $mailer; /** * The rendered mailable views for testing / assertions. * * @var array */ protected $assertionableRenderStrings; /** * The callback that should be invoked while building the view data. * * @var callable */ public static $viewDataCallback; /** * Send the message using the given mailer. * * @param \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer $mailer * @return \Illuminate\Mail\SentMessage|null */ public function send($mailer) { return $this->withLocale($this->locale, function () use ($mailer) { $this->prepareMailableForDelivery(); $mailer = $mailer instanceof MailFactory ? $mailer->mailer($this->mailer) : $mailer; return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) { $this->buildFrom($message) ->buildRecipients($message) ->buildSubject($message) ->buildTags($message) ->buildMetadata($message) ->runCallbacks($message) ->buildAttachments($message); }); }); } /** * Queue the message for sending. * * @param \Illuminate\Contracts\Queue\Factory $queue * @return mixed */ public function queue(Queue $queue) { if (isset($this->delay)) { return $this->later($this->delay, $queue); } $connection = property_exists($this, 'connection') ? $this->connection : null; $queueName = property_exists($this, 'queue') ? $this->queue : null; return $queue->connection($connection)->pushOn( $queueName ?: null, $this->newQueuedJob() ); } /** * Deliver the queued message after (n) seconds. * * @param \DateTimeInterface|\DateInterval|int $delay * @param \Illuminate\Contracts\Queue\Factory $queue * @return mixed */ public function later($delay, Queue $queue) { $connection = property_exists($this, 'connection') ? $this->connection : null; $queueName = property_exists($this, 'queue') ? $this->queue : null; return $queue->connection($connection)->laterOn( $queueName ?: null, $delay, $this->newQueuedJob() ); } /** * Make the queued mailable job instance. * * @return mixed */ protected function newQueuedJob() { return Container::getInstance()->make(SendQueuedMailable::class, ['mailable' => $this]) ->through(array_merge( method_exists($this, 'middleware') ? $this->middleware() : [], $this->middleware ?? [] )); } /** * Render the mailable into a view. * * @return string * * @throws \ReflectionException */ public function render() { return $this->withLocale($this->locale, function () { $this->prepareMailableForDelivery(); return Container::getInstance()->make('mailer')->render( $this->buildView(), $this->buildViewData() ); }); } /** * Build the view for the message. * * @return array|string * * @throws \ReflectionException */ protected function buildView() { if (isset($this->html)) { return array_filter([ 'html' => new HtmlString($this->html), 'text' => $this->textView ?? null, ]); } if (isset($this->markdown)) { return $this->buildMarkdownView(); } if (isset($this->view, $this->textView)) { return [$this->view, $this->textView]; } elseif (isset($this->textView)) { return ['text' => $this->textView]; } return $this->view; } /** * Build the Markdown view for the message. * * @return array * * @throws \ReflectionException */ protected function buildMarkdownView() { $data = $this->buildViewData(); return [ 'html' => $this->buildMarkdownHtml($data), 'text' => $this->buildMarkdownText($data), ]; } /** * Build the view data for the message. * * @return array * * @throws \ReflectionException */ public function buildViewData() { $data = $this->viewData; if (static::$viewDataCallback) { $data = array_merge($data, call_user_func(static::$viewDataCallback, $this)); } foreach ((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { if ($property->getDeclaringClass()->getName() !== self::class) { $data[$property->getName()] = $property->getValue($this); } } return $data; } /** * Build the HTML view for a Markdown message. * * @param array $viewData * @return \Closure */ protected function buildMarkdownHtml($viewData) { return fn ($data) => $this->markdownRenderer()->render( $this->markdown, array_merge($data, $viewData), ); } /** * Build the text view for a Markdown message. * * @param array $viewData * @return \Closure */ protected function buildMarkdownText($viewData) { return function ($data) use ($viewData) { if (isset($data['message'])) { $data = array_merge($data, [ 'message' => new TextMessage($data['message']), ]); } return $this->textView ?? $this->markdownRenderer()->renderText( $this->markdown, array_merge($data, $viewData) ); }; } /** * Resolves a Markdown instance with the mail's theme. * * @return \Illuminate\Mail\Markdown */ protected function markdownRenderer() { return tap(Container::getInstance()->make(Markdown::class), function ($markdown) { $markdown->theme($this->theme ?: Container::getInstance()->get(ConfigRepository::class)->get( 'mail.markdown.theme', 'default') ); }); } /** * Add the sender to the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildFrom($message) { if (! empty($this->from)) { $message->from($this->from[0]['address'], $this->from[0]['name']); } return $this; } /** * Add all of the recipients to the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildRecipients($message) { foreach (['to', 'cc', 'bcc', 'replyTo'] as $type) { foreach ($this->{$type} as $recipient) { $message->{$type}($recipient['address'], $recipient['name']); } } return $this; } /** * Set the subject for the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildSubject($message) { if ($this->subject) { $message->subject($this->subject); } else { $message->subject(Str::title(Str::snake(class_basename($this), ' '))); } return $this; } /** * Add all of the attachments to the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildAttachments($message) { foreach ($this->attachments as $attachment) { $message->attach($attachment['file'], $attachment['options']); } foreach ($this->rawAttachments as $attachment) { $message->attachData( $attachment['data'], $attachment['name'], $attachment['options'] ); } $this->buildDiskAttachments($message); return $this; } /** * Add all of the disk attachments to the message. * * @param \Illuminate\Mail\Message $message * @return void */ protected function buildDiskAttachments($message) { foreach ($this->diskAttachments as $attachment) { $storage = Container::getInstance()->make( FilesystemFactory::class )->disk($attachment['disk']); $message->attachData( $storage->get($attachment['path']), $attachment['name'] ?? basename($attachment['path']), array_merge(['mime' => $storage->mimeType($attachment['path'])], $attachment['options']) ); } } /** * Add all defined tags to the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildTags($message) { if ($this->tags) { foreach ($this->tags as $tag) { $message->getHeaders()->add(new TagHeader($tag)); } } return $this; } /** * Add all defined metadata to the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function buildMetadata($message) { if ($this->metadata) { foreach ($this->metadata as $key => $value) { $message->getHeaders()->add(new MetadataHeader($key, $value)); } } return $this; } /** * Run the callbacks for the message. * * @param \Illuminate\Mail\Message $message * @return $this */ protected function runCallbacks($message) { foreach ($this->callbacks as $callback) { $callback($message->getSymfonyMessage()); } return $this; } /** * Set the locale of the message. * * @param string $locale * @return $this */ public function locale($locale) { $this->locale = $locale; return $this; } /** * Set the priority of this message. * * The value is an integer where 1 is the highest priority and 5 is the lowest. * * @param int $level * @return $this */ public function priority($level = 3) { $this->callbacks[] = function ($message) use ($level) { $message->priority($level); }; return $this; } /** * Set the sender of the message. * * @param object|array|string $address * @param string|null $name * @return $this */ public function from($address, $name = null) { return $this->setAddress($address, $name, 'from'); } /** * Determine if the given recipient is set on the mailable. * * @param object|array|string $address * @param string|null $name * @return bool */ public function hasFrom($address, $name = null) { return $this->hasRecipient($address, $name, 'from'); } /** * Set the recipients of the message. * * @param object|array|string $address * @param string|null $name * @return $this */ public function to($address, $name = null) { if (! $this->locale && $address instanceof HasLocalePreference) { $this->locale($address->preferredLocale()); } return $this->setAddress($address, $name, 'to'); } /** * Determine if the given recipient is set on the mailable. * * @param object|array|string $address * @param string|null $name * @return bool */ public function hasTo($address, $name = null) { return $this->hasRecipient($address, $name, 'to'); } /** * Set the recipients of the message. * * @param object|array|string $address * @param string|null $name * @return $this */ public function cc($address, $name = null) { return $this->setAddress($address, $name, 'cc'); } /** * Determine if the given recipient is set on the mailable. * * @param object|array|string $address * @param string|null $name * @return bool */ public function hasCc($address, $name = null) { return $this->hasRecipient($address, $name, 'cc'); } /** * Set the recipients of the message. * * @param object|array|string $address * @param string|null $name * @return $this */ public function bcc($address, $name = null) { return $this->setAddress($address, $name, 'bcc'); } /** * Determine if the given recipient is set on the mailable. * * @param object|array|string $address * @param string|null $name * @return bool */ public function hasBcc($address, $name = null) { return $this->hasRecipient($address, $name, 'bcc'); } /** * Set the "reply to" address of the message. * * @param object|array|string $address * @param string|null $name * @return $this */ public function replyTo($address, $name = null) { return $this->setAddress($address, $name, 'replyTo'); } /** * Determine if the given replyTo is set on the mailable. * * @param object|array|string $address * @param string|null $name * @return bool */ public function hasReplyTo($address, $name = null) { return $this->hasRecipient($address, $name, 'replyTo'); } /** * Set the recipients of the message. * * All recipients are stored internally as [['name' => ?, 'address' => ?]] * * @param object|array|string $address * @param string|null $name * @param string $property * @return $this */ protected function setAddress($address, $name = null, $property = 'to') { if (empty($address)) { return $this; } foreach ($this->addressesToArray($address, $name) as $recipient) { $recipient = $this->normalizeRecipient($recipient); $this->{$property}[] = [ 'name' => $recipient->name ?? null, 'address' => $recipient->email, ]; } $this->{$property} = collect($this->{$property}) ->reverse() ->unique('address') ->reverse() ->values() ->all(); return $this; } /** * Convert the given recipient arguments to an array. * * @param object|array|string $address * @param string|null $name * @return array */ protected function addressesToArray($address, $name) { if (! is_array($address) && ! $address instanceof Collection) { $address = is_string($name) ? [['name' => $name, 'email' => $address]] : [$address]; } return $address; } /** * Convert the given recipient into an object. * * @param mixed $recipient * @return object */ protected function normalizeRecipient($recipient) { if (is_array($recipient)) { if (array_values($recipient) === $recipient) { return (object) array_map(function ($email) { return compact('email'); }, $recipient); } return (object) $recipient; } elseif (is_string($recipient)) { return (object) ['email' => $recipient]; } elseif ($recipient instanceof Address) { return (object) ['email' => $recipient->getAddress(), 'name' => $recipient->getName()]; } elseif ($recipient instanceof Mailables\Address) { return (object) ['email' => $recipient->address, 'name' => $recipient->name]; } return $recipient; } /** * Determine if the given recipient is set on the mailable. * * @param object|array|string $address * @param string|null $name * @param string $property * @return bool */ protected function hasRecipient($address, $name = null, $property = 'to') { if (empty($address)) { return false; } $expected = $this->normalizeRecipient( $this->addressesToArray($address, $name)[0] ); $expected = [ 'name' => $expected->name ?? null, 'address' => $expected->email, ]; if ($this->hasEnvelopeRecipient($expected['address'], $expected['name'], $property)) { return true; } return collect($this->{$property})->contains(function ($actual) use ($expected) { if (! isset($expected['name'])) { return $actual['address'] == $expected['address']; } return $actual == $expected; }); } /** * Determine if the mailable "envelope" method defines a recipient. * * @param string $address * @param string|null $name * @param string $property * @return bool */ private function hasEnvelopeRecipient($address, $name, $property) { return method_exists($this, 'envelope') && match ($property) { 'from' => $this->envelope()->isFrom($address, $name), 'to' => $this->envelope()->hasTo($address, $name), 'cc' => $this->envelope()->hasCc($address, $name), 'bcc' => $this->envelope()->hasBcc($address, $name), 'replyTo' => $this->envelope()->hasReplyTo($address, $name), }; } /** * Set the subject of the message. * * @param string $subject * @return $this */ public function subject($subject) { $this->subject = $subject; return $this; } /** * Determine if the mailable has the given subject. * * @param string $subject * @return bool */ public function hasSubject($subject) { return $this->subject === $subject || (method_exists($this, 'envelope') && $this->envelope()->hasSubject($subject)); } /** * Set the Markdown template for the message. * * @param string $view * @param array $data * @return $this */ public function markdown($view, array $data = []) { $this->markdown = $view; $this->viewData = array_merge($this->viewData, $data); return $this; } /** * Set the view and view data for the message. * * @param string $view * @param array $data * @return $this */ public function view($view, array $data = []) { $this->view = $view; $this->viewData = array_merge($this->viewData, $data); return $this; } /** * Set the rendered HTML content for the message. * * @param string $html * @return $this */ public function html($html) { $this->html = $html; return $this; } /** * Set the plain text view for the message. * * @param string $textView * @param array $data * @return $this */ public function text($textView, array $data = []) { $this->textView = $textView; $this->viewData = array_merge($this->viewData, $data); return $this; } /** * Set the view data for the message. * * @param string|array $key * @param mixed $value * @return $this */ public function with($key, $value = null) { if (is_array($key)) { $this->viewData = array_merge($this->viewData, $key); } else { $this->viewData[$key] = $value; } return $this; } /** * Attach a file to the message. * * @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file * @param array $options * @return $this */ public function attach($file, array $options = []) { if ($file instanceof Attachable) { $file = $file->toMailAttachment(); } if ($file instanceof Attachment) { return $file->attachTo($this, $options); } $this->attachments = collect($this->attachments) ->push(compact('file', 'options')) ->unique('file') ->all(); return $this; } /** * Attach multiple files to the message. * * @param array $files * @return $this */ public function attachMany($files) { foreach ($files as $file => $options) { if (is_int($file)) { $this->attach($options); } else { $this->attach($file, $options); } } return $this; } /** * Determine if the mailable has the given attachment. * * @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file * @param array $options * @return bool */ public function hasAttachment($file, array $options = []) { if ($file instanceof Attachable) { $file = $file->toMailAttachment(); } if ($file instanceof Attachment && $this->hasEnvelopeAttachment($file, $options)) { return true; } if ($file instanceof Attachment) { $parts = $file->attachWith( fn ($path) => [$path, [ 'as' => $options['as'] ?? $file->as, 'mime' => $options['mime'] ?? $file->mime, ]], fn ($data) => $this->hasAttachedData($data(), $options['as'] ?? $file->as, ['mime' => $options['mime'] ?? $file->mime]) ); if ($parts === true) { return true; } [$file, $options] = $parts === false ? [null, []] : $parts; } return collect($this->attachments)->contains( fn ($attachment) => $attachment['file'] === $file && array_filter($attachment['options']) === array_filter($options) ); } /** * Determine if the mailable has the given envelope attachment. * * @param \Illuminate\Mail\Attachment $attachment * @param array $options * @return bool */ private function hasEnvelopeAttachment($attachment, $options = []) { if (! method_exists($this, 'envelope')) { return false; } $attachments = $this->attachments(); return Collection::make(is_object($attachments) ? [$attachments] : $attachments) ->map(fn ($attached) => $attached instanceof Attachable ? $attached->toMailAttachment() : $attached) ->contains(fn ($attached) => $attached->isEquivalent($attachment, $options)); } /** * Attach a file to the message from storage. * * @param string $path * @param string|null $name * @param array $options * @return $this */ public function attachFromStorage($path, $name = null, array $options = []) { return $this->attachFromStorageDisk(null, $path, $name, $options); } /** * Attach a file to the message from storage. * * @param string $disk * @param string $path * @param string|null $name * @param array $options * @return $this */ public function attachFromStorageDisk($disk, $path, $name = null, array $options = []) { $this->diskAttachments = collect($this->diskAttachments)->push([ 'disk' => $disk, 'path' => $path, 'name' => $name ?? basename($path), 'options' => $options, ])->unique(function ($file) { return $file['name'].$file['disk'].$file['path']; })->all(); return $this; } /** * Determine if the mailable has the given attachment from storage. * * @param string $path * @param string|null $name * @param array $options * @return bool */ public function hasAttachmentFromStorage($path, $name = null, array $options = []) { return $this->hasAttachmentFromStorageDisk(null, $path, $name, $options); } /** * Determine if the mailable has the given attachment from a specific storage disk. * * @param string $disk * @param string $path * @param string|null $name * @param array $options * @return bool */ public function hasAttachmentFromStorageDisk($disk, $path, $name = null, array $options = []) { return collect($this->diskAttachments)->contains( fn ($attachment) => $attachment['disk'] === $disk && $attachment['path'] === $path && $attachment['name'] === ($name ?? basename($path)) && $attachment['options'] === $options ); } /** * Attach in-memory data as an attachment. * * @param string $data * @param string $name * @param array $options * @return $this */ public function attachData($data, $name, array $options = []) { $this->rawAttachments = collect($this->rawAttachments) ->push(compact('data', 'name', 'options')) ->unique(function ($file) { return $file['name'].$file['data']; })->all(); return $this; } /** * Determine if the mailable has the given data as an attachment. * * @param string $data * @param string $name * @param array $options * @return bool */ public function hasAttachedData($data, $name, array $options = []) { return collect($this->rawAttachments)->contains( fn ($attachment) => $attachment['data'] === $data && $attachment['name'] === $name && array_filter($attachment['options']) === array_filter($options) ); } /** * Add a tag header to the message when supported by the underlying transport. * * @param string $value * @return $this */ public function tag($value) { array_push($this->tags, $value); return $this; } /** * Determine if the mailable has the given tag. * * @param string $value * @return bool */ public function hasTag($value) { return in_array($value, $this->tags) || (method_exists($this, 'envelope') && in_array($value, $this->envelope()->tags)); } /** * Add a metadata header to the message when supported by the underlying transport. * * @param string $key * @param string $value * @return $this */ public function metadata($key, $value) { $this->metadata[$key] = $value; return $this; } /** * Determine if the mailable has the given metadata. * * @param string $key * @param string $value * @return bool */ public function hasMetadata($key, $value) { return (isset($this->metadata[$key]) && $this->metadata[$key] === $value) || (method_exists($this, 'envelope') && $this->envelope()->hasMetadata($key, $value)); } /** * Assert that the mailable is from the given address. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertFrom($address, $name = null) { $recipient = $this->formatAssertionRecipient($address, $name); PHPUnit::assertTrue( $this->hasFrom($address, $name), "Email was not from expected address [{$recipient}]." ); return $this; } /** * Assert that the mailable has the given recipient. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertTo($address, $name = null) { $recipient = $this->formatAssertionRecipient($address, $name); PHPUnit::assertTrue( $this->hasTo($address, $name), "Did not see expected recipient [{$recipient}] in email recipients." ); return $this; } /** * Assert that the mailable has the given recipient. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertHasTo($address, $name = null) { return $this->assertTo($address, $name); } /** * Assert that the mailable has the given recipient. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertHasCc($address, $name = null) { $recipient = $this->formatAssertionRecipient($address, $name); PHPUnit::assertTrue( $this->hasCc($address, $name), "Did not see expected recipient [{$recipient}] in email recipients." ); return $this; } /** * Assert that the mailable has the given recipient. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertHasBcc($address, $name = null) { $recipient = $this->formatAssertionRecipient($address, $name); PHPUnit::assertTrue( $this->hasBcc($address, $name), "Did not see expected recipient [{$recipient}] in email recipients." ); return $this; } /** * Assert that the mailable has the given "reply to" address. * * @param object|array|string $address * @param string|null $name * @return $this */ public function assertHasReplyTo($address, $name = null) { $replyTo = $this->formatAssertionRecipient($address, $name); PHPUnit::assertTrue( $this->hasReplyTo($address, $name), "Did not see expected address [{$replyTo}] as email 'reply to' recipient." ); return $this; } /** * Format the mailable recipient for display in an assertion message. * * @param object|array|string $address * @param string|null $name * @return string */ private function formatAssertionRecipient($address, $name = null) { if (! is_string($address)) { $address = json_encode($address); } if (filled($name)) { $address .= ' ('.$name.')'; } return $address; } /** * Assert that the mailable has the given subject. * * @param string $subject * @return $this */ public function assertHasSubject($subject) { PHPUnit::assertTrue( $this->hasSubject($subject), "Did not see expected text [{$subject}] in email subject." ); return $this; } /** * Assert that the given text is present in the HTML email body. * * @param string $string * @param bool $escape * @return $this */ public function assertSeeInHtml($string, $escape = true) { $string = $escape ? e($string) : $string; [$html, $text] = $this->renderForAssertions(); PHPUnit::assertStringContainsString( $string, $html, "Did not see expected text [{$string}] within email body." ); return $this; } /** * Assert that the given text is not present in the HTML email body. * * @param string $string * @param bool $escape * @return $this */ public function assertDontSeeInHtml($string, $escape = true) { $string = $escape ? e($string) : $string; [$html, $text] = $this->renderForAssertions(); PHPUnit::assertStringNotContainsString( $string, $html, "Saw unexpected text [{$string}] within email body." ); return $this; } /** * Assert that the given text strings are present in order in the HTML email body. * * @param array $strings * @param bool $escape * @return $this */ public function assertSeeInOrderInHtml($strings, $escape = true) { $strings = $escape ? array_map('e', $strings) : $strings; [$html, $text] = $this->renderForAssertions(); PHPUnit::assertThat($strings, new SeeInOrder($html)); return $this; } /** * Assert that the given text is present in the plain-text email body. * * @param string $string * @return $this */ public function assertSeeInText($string) { [$html, $text] = $this->renderForAssertions(); PHPUnit::assertStringContainsString( $string, $text, "Did not see expected text [{$string}] within text email body." ); return $this; } /** * Assert that the given text is not present in the plain-text email body. * * @param string $string * @return $this */ public function assertDontSeeInText($string) { [$html, $text] = $this->renderForAssertions(); PHPUnit::assertStringNotContainsString( $string, $text, "Saw unexpected text [{$string}] within text email body." ); return $this; } /** * Assert that the given text strings are present in order in the plain-text email body. * * @param array $strings * @return $this */ public function assertSeeInOrderInText($strings) { [$html, $text] = $this->renderForAssertions(); PHPUnit::assertThat($strings, new SeeInOrder($text)); return $this; } /** * Assert the mailable has the given attachment. * * @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file * @param array $options * @return $this */ public function assertHasAttachment($file, array $options = []) { $this->renderForAssertions(); PHPUnit::assertTrue( $this->hasAttachment($file, $options), 'Did not find the expected attachment.' ); return $this; } /** * Assert the mailable has the given data as an attachment. * * @param string $data * @param string $name * @param array $options * @return $this */ public function assertHasAttachedData($data, $name, array $options = []) { $this->renderForAssertions(); PHPUnit::assertTrue( $this->hasAttachedData($data, $name, $options), 'Did not find the expected attachment.' ); return $this; } /** * Assert the mailable has the given attachment from storage. * * @param string $path * @param string|null $name * @param array $options * @return $this */ public function assertHasAttachmentFromStorage($path, $name = null, array $options = []) { $this->renderForAssertions(); PHPUnit::assertTrue( $this->hasAttachmentFromStorage($path, $name, $options), 'Did not find the expected attachment.' ); return $this; } /** * Assert the mailable has the given attachment from a specific storage disk. * * @param string $disk * @param string $path * @param string|null $name * @param array $options * @return $this */ public function assertHasAttachmentFromStorageDisk($disk, $path, $name = null, array $options = []) { $this->renderForAssertions(); PHPUnit::assertTrue( $this->hasAttachmentFromStorageDisk($disk, $path, $name, $options), 'Did not find the expected attachment.' ); return $this; } /** * Assert that the mailable has the given tag. * * @param string $tag * @return $this */ public function assertHasTag($tag) { PHPUnit::assertTrue( $this->hasTag($tag), "Did not see expected tag [{$tag}] in email tags." ); return $this; } /** * Assert that the mailable has the given metadata. * * @param string $key * @param string $value * @return $this */ public function assertHasMetadata($key, $value) { PHPUnit::assertTrue( $this->hasMetadata($key, $value), "Did not see expected key [{$key}] and value [{$value}] in email metadata." ); return $this; } /** * Render the HTML and plain-text version of the mailable into views for assertions. * * @return array * * @throws \ReflectionException */ protected function renderForAssertions() { if ($this->assertionableRenderStrings) { return $this->assertionableRenderStrings; } return $this->assertionableRenderStrings = $this->withLocale($this->locale, function () { $this->prepareMailableForDelivery(); $html = Container::getInstance()->make('mailer')->render( $view = $this->buildView(), $this->buildViewData() ); if (is_array($view) && isset($view[1])) { $text = $view[1]; } $text ??= $view['text'] ?? ''; if (! empty($text) && ! $text instanceof Htmlable) { $text = Container::getInstance()->make('mailer')->render( $text, $this->buildViewData() ); } return [(string) $html, (string) $text]; }); } /** * Prepare the mailable instance for delivery. * * @return void */ protected function prepareMailableForDelivery() { if (method_exists($this, 'build')) { Container::getInstance()->call([$this, 'build']); } $this->ensureHeadersAreHydrated(); $this->ensureEnvelopeIsHydrated(); $this->ensureContentIsHydrated(); $this->ensureAttachmentsAreHydrated(); } /** * Ensure the mailable's headers are hydrated from the "headers" method. * * @return void */ private function ensureHeadersAreHydrated() { if (! method_exists($this, 'headers')) { return; } $headers = $this->headers(); $this->withSymfonyMessage(function ($message) use ($headers) { if ($headers->messageId) { $message->getHeaders()->addIdHeader('Message-Id', $headers->messageId); } if (count($headers->references) > 0) { $message->getHeaders()->addTextHeader('References', $headers->referencesString()); } foreach ($headers->text as $key => $value) { $message->getHeaders()->addTextHeader($key, $value); } }); } /** * Ensure the mailable's "envelope" data is hydrated from the "envelope" method. * * @return void */ private function ensureEnvelopeIsHydrated() { if (! method_exists($this, 'envelope')) { return; } $envelope = $this->envelope(); if (isset($envelope->from)) { $this->from($envelope->from->address, $envelope->from->name); } foreach (['to', 'cc', 'bcc', 'replyTo'] as $type) { foreach ($envelope->{$type} as $address) { $this->{$type}($address->address, $address->name); } } if ($envelope->subject) { $this->subject($envelope->subject); } foreach ($envelope->tags as $tag) { $this->tag($tag); } foreach ($envelope->metadata as $key => $value) { $this->metadata($key, $value); } foreach ($envelope->using as $callback) { $this->withSymfonyMessage($callback); } } /** * Ensure the mailable's content is hydrated from the "content" method. * * @return void */ private function ensureContentIsHydrated() { if (! method_exists($this, 'content')) { return; } $content = $this->content(); if ($content->view) { $this->view($content->view); } if ($content->html) { $this->view($content->html); } if ($content->text) { $this->text($content->text); } if ($content->markdown) { $this->markdown($content->markdown); } if ($content->htmlString) { $this->html($content->htmlString); } foreach ($content->with as $key => $value) { $this->with($key, $value); } } /** * Ensure the mailable's attachments are hydrated from the "attachments" method. * * @return void */ private function ensureAttachmentsAreHydrated() { if (! method_exists($this, 'attachments')) { return; } $attachments = $this->attachments(); Collection::make(is_object($attachments) ? [$attachments] : $attachments) ->each(function ($attachment) { $this->attach($attachment); }); } /** * Set the name of the mailer that should send the message. * * @param string $mailer * @return $this */ public function mailer($mailer) { $this->mailer = $mailer; return $this; } /** * Register a callback to be called with the Symfony message instance. * * @param callable $callback * @return $this */ public function withSymfonyMessage($callback) { $this->callbacks[] = $callback; return $this; } /** * Register a callback to be called while building the view data. * * @param callable $callback * @return void */ public static function buildViewDataUsing(callable $callback) { static::$viewDataCallback = $callback; } /** * Dynamically bind parameters to the message. * * @param string $method * @param array $parameters * @return $this * * @throws \BadMethodCallException */ public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); } if (str_starts_with($method, 'with')) { return $this->with(Str::camel(substr($method, 4)), $parameters[0]); } static::throwBadMethodCallException($method); } }