PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 All 50 releases
← All changes | app/Services/Email/EmailPreviewService.php +80 -0 1.6.6 → 1.7.0 View file →
@@ -1,8 +1,11 @@
1 1 <?php
2 2
3 3 namespace FluentCart\App\Services\Email;
4 4
5 +use FluentCart\App\Helpers\Status;
6 +use FluentCart\App\Models\Product;
7 +use FluentCart\App\Models\ProductReview;
5 8 use FluentCart\App\Services\DateTime\DateTime;
6 9 use FluentCart\Framework\Support\Collection;
7 10 use FluentCart\Framework\Support\Str;
8 11
@@ -40,8 +43,24 @@
40 43 $previewData['reminder']['stage'] = 'overdue_1';
41 44 }
42 45 }
43 46
47 + // The review templates read a review, not an order. Without one the
48 + // preview of any of them fatals on the first property it touches.
49 + if (Str::startsWith($template, 'review.')) {
50 + $previewData['product'] = $this->getPreviewProduct();
51 + $previewData['review'] = $this->getDummyReview((int) $previewData['product']->ID);
52 +
53 + if (Str::startsWith($template, 'review.replied.')) {
54 + $previewData['reply'] = $this->getDummyReply((int) $previewData['product']->ID);
55 + }
56 + // A review is not about an order, and a real send carries none —
57 + // the order header (date, number) renders only when one is there.
58 + // Left in, the preview would show a header the email never has.
59 + $previewData['order'] = null;
60 + $previewData['transaction'] = null;
61 + }
62 +
44 63 if (Str::startsWith($template, 'subscription.')) {
45 64 $previewData['transaction'] = $subscription->getLatestTransaction();
46 65
47 66 if (Str::contains($template, 'trial_end')) {
@@ -58,8 +77,69 @@
58 77 }
59 78 }
60 79
61 80 return $previewData;
81 + }
82 +
83 + /**
84 + * A stand-in review with every field the review templates and the
85 + * {{review.*}} codes read. A real model, not a stdClass: the templates
86 + * call attachItemLabels() on it and the parser type-checks for one.
87 + */
88 + /**
89 + * The product a review preview is about: a real published one when the
90 + * store has any, so the preview links where the email will — the
91 + * "See your review" button needs a page to point at, and a placeholder
92 + * has none. An empty store previews without the button, as the email
93 + * would for a product since deleted.
94 + */
95 + private function getPreviewProduct(): object
96 + {
97 + $product = Product::query()
98 + ->where('post_status', 'publish')
99 + ->orderBy('ID', 'DESC')
100 + ->first();
101 +
102 + if ($product) {
103 + return $product;
104 + }
105 +
106 + return (object) [
107 + 'ID' => 0,
108 + 'post_title' => 'Sample Product',
109 + ];
110 + }
111 +
112 + private function getDummyReview(int $postId = 0): ProductReview
113 + {
114 + $review = new ProductReview([
115 + 'post_id' => $postId,
116 + 'reviewer_name' => 'John Doe',
117 + 'reviewer_email' => '[email protected]',
118 + 'title' => 'Exactly what I was looking for',
119 + 'review' => 'Arrived quickly and works just as described. Would happily buy again.',
120 + 'rating' => 5,
121 + ]);
122 + $review->id = 0;
123 + $review->status = Status::REVIEW_APPROVED;
124 +
125 + return $review;
126 + }
127 +
128 + private function getDummyReply(int $postId = 0): ProductReview
129 + {
130 + $reply = new ProductReview([
131 + 'post_id' => $postId,
132 + 'parent_id' => 0,
133 + 'reviewer_name' => 'The Store Team',
134 + 'reviewer_email' => '[email protected]',
135 + 'review' => 'Thank you so much for the kind words — we are thrilled it worked out. Enjoy!',
136 + ]);
137 + $reply->id = 0;
138 + $reply->status = Status::REVIEW_APPROVED;
139 + $reply->is_admin_reply = 1;
140 +
141 + return $reply;
62 142 }
63 143
64 144 private function getDummyCustomer(): object
65 145 {