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
fluent-cart / app / Models / OrderItem.php

OrderItem.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.0, at app/Models/OrderItem.php

281 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Models;
4
5 use FluentCart\App\Helpers\Helper;
6 use FluentCart\App\Helpers\OrderItemHelper;
7 use FluentCart\App\Models\Concerns\CanSearch;
8 use FluentCart\App\Models\Concerns\CanUpdateBatch;
9 use FluentCart\App\Models\WpModels\PostMeta;
10 use FluentCart\Framework\Database\Orm\Relations\HasOne;
11 use FluentCart\Framework\Support\Arr;
12
13 /**
14 * OrderItem Model - DB Model for Order Items
15 *
16 * Database Model
17 *
18 * @package FluentCart\App\Models
19 *
20 * @version 1.0.0
21 */
22 class OrderItem extends Model
23 {
24 use CanSearch, CanUpdateBatch;
25
26 protected $table = 'fct_order_items';
27
28 /**
29 * The attributes that are mass assignable.
30 *
31 * @var array
32 */
33 protected $fillable = [
34 'order_id',
35 'post_id',
36 'fulfillment_type',
37 'fulfilled_quantity',
38 'post_title',
39 'title',
40 'object_id',
41 'cart_index',
42 'quantity',
43 'unit_price',
44 'cost',
45 'subtotal',
46 'tax_amount',
47 'discount_total',
48 'refund_total',
49 'line_total',
50 'rate',
51 'other_info',
52 'line_meta',
53 'referrer',
54 'object_type',
55 'payment_type',
56 'created_at',
57 // 'shipping_charge'
58 ];
59 protected $appends = ['payment_info', 'setup_info', 'is_custom', 'variation_display_title'];
60
61 protected $casts = [
62 'unit_price' => 'double',
63 'cost' => 'double',
64 'subtotal' => 'double',
65 'tax_amount' => 'double',
66 'shipping_charge' => 'double',
67 'discount_total' => 'double',
68 'line_total' => 'double',
69 'refund_total' => 'double',
70 ];
71
72 protected static function booted()
73 {
74 static::retrieved(function ($product) {
75 $product->append('formatted_total');
76 });
77 }
78
79 protected function getFormattedTotalAttribute()
80 {
81 if ($this->line_total == 0 && $this->discount_total == 0) {
82 return Helper::toDecimal($this->subtotal);
83 }
84 return Helper::toDecimal($this->line_total);
85 }
86
87 public function getCouponDiscountAttribute()
88 {
89 return (int)($this->line_meta['coupon_discount'] ?? 0);
90 }
91
92 public function setOtherInfoAttribute($value)
93 {
94 if (is_array($value) || is_object($value)) {
95 $this->attributes['other_info'] = json_encode($value);
96 } else {
97 $this->attributes['other_info'] = $value;
98 }
99 }
100
101 public function getOtherInfoAttribute($value)
102 {
103 if (is_string($value)) {
104 $decoded = json_decode($value, true);
105 return is_array($decoded) ? $decoded : [];
106 }
107
108 return [];
109 }
110
111 public function setLineMetaAttribute($value)
112 {
113 if (is_array($value) || is_object($value)) {
114 $this->attributes['line_meta'] = json_encode($value);
115 } else {
116 $this->attributes['line_meta'] = [];
117 }
118 }
119
120 public function getLineMetaAttribute($value)
121 {
122 if (is_string($value)) {
123 $decoded = json_decode($value, true);
124 if ($decoded) {
125 return $decoded;
126 }
127 }
128
129 return [];
130 }
131
132 public function getFullNameAttribute($value)
133 {
134 return $this->title . ' ' . $this->post_title;
135 }
136
137 public function order()
138 {
139 return $this->belongsTo(Order::class, 'order_id', 'id');
140 }
141
142 public function product()
143
144 {
145 return $this->belongsTo(Product::class, 'post_id', 'ID');
146 }
147
148
149 public function variants()
150 {
151 return $this->belongsTo(ProductVariation::class, 'object_id', 'id');
152 }
153
154 public function product_downloads()
155 {
156 return $this->belongsTo(ProductDownload::class, 'post_id', 'post_id');
157 }
158
159 /**
160 * All reviews of the product this line item points to — keyed on
161 * post_id, not on the item itself, so whereDoesntHave() over it
162 * expresses "the customer never reviewed this product".
163 */
164 public function product_reviews()
165 {
166 return $this->hasMany(ProductReview::class, 'post_id', 'post_id');
167 }
168
169 public function createItem($orderItems)
170 {
171 return $this->belongsTo(ProductVariation::class, 'variation_id', 'id');
172 }
173
174 public function processCustom($product, $orderId)
175 {
176 return (new OrderItemHelper())->processCustom($product, $orderId);
177 }
178
179 /**
180 * Get subscription payment info if available
181 *
182 * @return string
183 */
184 public function getPaymentInfoAttribute(): string
185 {
186 return $this->getSubscriptionInfo('payment');
187 }
188
189 /**
190 * Get subscription setup info if available
191 *
192 * @return string
193 */
194 public function getSetupInfoAttribute(): string
195 {
196 return $this->getSubscriptionInfo('setup');
197 }
198
199 /**
200 * Helper method to get subscription info
201 *
202 * @param string $type 'payment' or 'setup'
203 * @return string
204 */
205
206 private function getSubscriptionInfo(string $type): string
207 {
208 if ($this->payment_type !== 'subscription') {
209 return '';
210 }
211
212 $otherInfo = $this->other_info ?? [];
213 $unitPrice = $this->unit_price ?? 0;
214
215 if ($type === 'payment') {
216 return Helper::generateSubscriptionInfo($otherInfo, $unitPrice) ?? '';
217 }
218
219 if ($type === 'setup') {
220 return Helper::generateSetupFeeInfo($otherInfo) ?? '';
221 }
222
223 return '';
224 }
225
226 public function productImage(): HasOne
227 {
228 return $this->hasOne(PostMeta::class, 'post_id', 'post_id')
229 ->where('postmeta.meta_key', 'fluent-products-gallery-image');
230 }
231
232 public function variantImages(): HasOne
233 {
234 return $this->hasOne(ProductMeta::class, 'object_id', 'object_id')
235 ->where('object_type', 'product_variant_info')
236 ->where('meta_key', 'product_thumbnail');
237 }
238
239 public function getIsCustomAttribute()
240 {
241 return $this->other_info['is_custom'] ?? false;
242 }
243
244 public function getViewUrlAttribute()
245 {
246 return $this->is_custom
247 ? ($this->other_info['view_url'] ?? '')
248 : '';
249 }
250
251 public function getDisplayTitle()
252 {
253 if ($this->post_title == $this->title) {
254 return $this->title;
255 }
256
257 return $this->post_title . ' - ' . $this->title;
258
259 }
260
261 /**
262 * Labeled attribute combination resolved from the frozen
263 * other_info['item_attributes'] snapshot, e.g. "Color: Red | Size: XS".
264 * Falls back to the stored variation title when no attribute snapshot
265 * resolves (custom/simple items, or attrs whose groups were removed).
266 *
267 * @return string
268 */
269 public function getVariationDisplayTitleAttribute()
270 {
271 // Order items carry the item_attributes snapshot (written at order
272 // creation), so resolve directly — getDisplayAttributesString renders
273 // the labeled combination and falls back to the variation title.
274 return \FluentCart\App\Helpers\AttributeHelper::getDisplayAttributesString(
275 Arr::get($this->other_info, 'item_attributes', []),
276 $this,
277 'order_item'
278 );
279 }
280 }
281