Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
199 lars 1
<?php namespace Clockwork\DataSource;
2
 
3
use Clockwork\Helpers\Serializer;
4
use Clockwork\Helpers\StackTrace;
5
use Clockwork\Request\Request;
6
 
7
use Illuminate\Contracts\Events\Dispatcher;
8
use Illuminate\Mail\Mailable;
9
use Illuminate\Mail\Events\MessageSending;
10
use Illuminate\Mail\Events\MessageSent;
11
use Illuminate\Notifications\Events\NotificationSending;
12
use Illuminate\Notifications\Events\NotificationSent;
13
 
14
// Data source for Laravel notifications and mail components, provides sent notifications and emails
15
class LaravelNotificationsDataSource extends DataSource
16
{
17
	// Event dispatcher instance
18
	protected $dispatcher;
19
 
20
	// Sent notifications
21
	protected $notifications = [];
22
 
23
	// Last collected notification
24
	protected $lastNotification;
25
 
26
	// Create a new data source instance, takes an event dispatcher as argument
27
	public function __construct(Dispatcher $dispatcher)
28
	{
29
		$this->dispatcher = $dispatcher;
30
	}
31
 
32
	// Add sent notifications to the request
33
	public function resolve(Request $request)
34
	{
35
		$request->notifications = array_merge($request->notifications, $this->notifications);
36
 
37
		return $request;
38
	}
39
 
40
	// Reset the data source to an empty state, clearing any collected data
41
	public function reset()
42
	{
43
		$this->notifications = [];
44
	}
45
 
46
	// Listen to the email and notification events
47
	public function listenToEvents()
48
	{
49
		$this->dispatcher->listen(MessageSending::class, function ($event) { $this->sendingMessage($event); });
50
		$this->dispatcher->listen(MessageSent::class, function ($event) { $this->sentMessage($event); });
51
 
52
		$this->dispatcher->listen(NotificationSending::class, function ($event) { $this->sendingNotification($event); });
53
		$this->dispatcher->listen(NotificationSent::class, function ($event) { $this->sentNotification($event); });
54
	}
55
 
56
	// Collect a sent email
57
	protected function sendingMessage($event)
58
	{
59
		$trace = StackTrace::get()->resolveViewName();
60
 
61
		$mailable = ($frame = $trace->first(function ($frame) { return is_subclass_of($frame->object, Mailable::class); }))
62
			? $frame->object : null;
63
 
64
		$notification = (object) [
65
			'subject' => $event->message->getSubject(),
66
			'from'    => $this->messageAddressToString($event->message->getFrom()),
67
			'to'      => $this->messageAddressToString($event->message->getTo()),
68
			'content' => $this->messageBody($event->message),
69
			'type'    => 'mail',
70
			'data'    => [
71
				'cc'       => $this->messageAddressToString($event->message->getCc()),
72
				'bcc'      => $this->messageAddressToString($event->message->getBcc()),
73
				'replyTo'  => $this->messageAddressToString($event->message->getReplyTo()),
74
				'mailable' => (new Serializer)->normalize($mailable)
75
			],
76
			'time'    => microtime(true),
77
			'trace'   => (new Serializer)->trace($trace)
78
		];
79
 
80
		if ($this->updateLastNotification($notification)) return;
81
 
82
		if ($this->passesFilters([ $notification ])) {
83
			$this->notifications[] = $this->lastNotification = $notification;
84
		} else {
85
			$this->lastNotification = null;
86
		}
87
	}
88
 
89
	// Update last notification with time taken to send it
90
	protected function sentMessage($event)
91
	{
92
		if ($this->lastNotification) {
93
			$this->lastNotification->duration = (microtime(true) - $this->lastNotification->time) * 1000;
94
		}
95
	}
96
 
97
	// Collect a sent notification
98
	protected function sendingNotification($event)
99
	{
100
		$trace = StackTrace::get()->resolveViewName();
101
 
102
		$channelSpecific = $this->resolveChannelSpecific($event);
103
 
104
		$notification = (object) [
105
			'subject' => $channelSpecific['subject'],
106
			'from'    => $channelSpecific['from'],
107
			'to'      => $channelSpecific['to'],
108
			'content' => $channelSpecific['content'],
109
			'type'    => $event->channel,
110
			'data'    => array_merge($channelSpecific['data'], [
111
				'notification' => (new Serializer)->normalize($event->notification),
112
				'notifiable'   => (new Serializer)->normalize($event->notifiable)
113
			]),
114
			'time'    => microtime(true),
115
			'trace'   => (new Serializer)->trace($trace)
116
		];
117
 
118
		if ($this->passesFilters([ $notification ])) {
119
			$this->notifications[] = $this->lastNotification = $notification;
120
		} else {
121
			$this->lastNotification = null;
122
		}
123
	}
124
 
125
	// Update last notification with time taken to send it and response
126
	protected function sentNotification($event)
127
	{
128
		if ($this->lastNotification) {
129
			$this->lastNotification->duration = (microtime(true) - $this->lastNotification->time) * 1000;
130
			$this->lastNotification->data['response'] = $event->response;
131
		}
132
	}
133
 
134
	// Update last sent email notification with additional data from the message sent event
135
	protected function updateLastNotification($notification)
136
	{
137
		if (! $this->lastNotification) return false;
138
 
139
		if ($this->lastNotification->to !== $notification->to) return false;
140
 
141
		$this->lastNotification->subject = $notification->subject;
142
		$this->lastNotification->from    = $notification->from;
143
		$this->lastNotification->to      = $notification->to;
144
		$this->lastNotification->content = $notification->content;
145
 
146
		$this->lastNotification->data = array_merge($this->lastNotification->data, $notification->data);
147
 
148
		return true;
149
	}
150
 
151
	// Resolve notification channel specific data
152
	protected function resolveChannelSpecific($event)
153
	{
154
		if (method_exists($event->notification, 'toMail')) {
155
			$channelSpecific = $this->resolveMailChannelSpecific($event, $event->notification->toMail($event->notifiable));
156
		} elseif (method_exists($event->notification, 'toSlack')) {
157
			$channelSpecific = $this->resolveSlackChannelSpecific($event, $event->notification->toSlack($event->notifiable));
158
		} elseif (method_exists($event->notification, 'toNexmo')) {
159
			$channelSpecific = $this->resolveNexmoChannelSpecific($event, $event->notification->toNexmo($event->notifiable));
160
		} elseif (method_exists($event->notification, 'toBroadcast')) {
161
			$channelSpecific = [ 'data' => [ 'data' => (new Serializer)->normalize($event->notification->toBroadcast($event->notifiable)) ] ];
162
		} elseif (method_exists($event->notification, 'toArray')) {
163
			$channelSpecific = [ 'data' => [ 'data' => (new Serializer)->normalize($event->notification->toArray($event->notifiable)) ] ];
164
		} else {
165
			$channelSpecific = [];
166
		}
167
 
168
		return array_merge(
169
			[ 'subject' => null, 'from' => null, 'to' => null, 'content' => null, 'data' => [] ], $channelSpecific
170
		);
171
	}
172
 
173
	// Resolve mail notification channel specific data
174
	protected function resolveMailChannelSpecific($event, $message)
175
	{
176
		return [
177
			'subject' => $message->subject ?: get_class($event->notification),
178
			'from'    => $this->notificationAddressToString($message->from),
179
			'to'      => $this->notificationAddressToString($event->notifiable->routeNotificationFor('mail', $event->notification)),
180
			'data'    => [
181
				'cc'      => $this->notificationAddressToString($message->cc),
182
				'bcc'     => $this->notificationAddressToString($message->bcc),
183
				'replyTo' => $this->notificationAddressToString($message->replyTo)
184
			]
185
		];
186
	}
187
 
188
	// Resolve Slack notification channel specific data
189
	protected function resolveSlackChannelSpecific($event, $message)
190
	{
191
		return [
192
			'subject' => get_class($event->notification),
193
			'from'    => $message->username,
194
			'to'      => $message->channel,
195
			'content' => $message->content
196
		];
197
	}
198
 
199
	// Resolve Nexmo notification channel specific data
200
	protected function resolveNexmoChannelSpecific($event, $message)
201
	{
202
		return [
203
			'subject' => get_class($event->notification),
204
			'from'    => $message->from,
205
			'to'      => $event->notifiable->routeNotificationFor('nexmo', $event->notification),
206
			'content' => $message->content
207
		];
208
	}
209
 
210
	protected function messageAddressToString($address)
211
	{
212
		if (! $address) return;
213
 
214
		return array_map(function ($address, $key) {
215
			// Laravel 8 or earlier
216
			if (! ($address instanceof \Symfony\Component\Mime\Address)) {
217
				return $address ? "{$address} <{$key}>" : $key;
218
			}
219
 
220
			// Laravel 9 or later
221
			return $address->toString();
222
		}, $address, array_keys($address));
223
	}
224
 
225
	protected function messageBody($message)
226
	{
227
		// Laravel 8 or earlier
228
		if (! ($message instanceof \Symfony\Component\Mime\Email)) {
229
			return $message->getBody();
230
		}
231
 
232
		// Laravel 9 or later
233
		return $message->getHtmlBody() ?: $message->getTextBody();
234
	}
235
 
236
	protected function notificationAddressToString($address)
237
	{
238
		if (! $address) return;
239
		if (! is_array($address)) $address = [ $address ];
240
 
241
		return array_map(function ($address) {
242
			if (! is_array($address)) return $address;
243
 
244
			$email = isset($address['address']) ? $address['address'] : $address[0];
245
			$name = isset($address['name']) ? $address['name'] : $address[1];
246
 
247
			return $name ? "{$name} <{$email}>" : $email;
248
		}, $address);
249
	}
250
}