Completed
Push — master ( cf6343...033dab )
by Scott
04:23
created

Discount::savePrices()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
rs 9.2
cc 2
eloc 14
nc 1
nop 0
1
<?php namespace Bedard\Shop\Models;
2
3
use Bedard\Shop\Models\Price;
4
use Carbon\Carbon;
5
use Flash;
6
use Lang;
7
use Model;
8
use October\Rain\Database\ModelException;
9
use Queue;
10
11
/**
12
 * Discount Model.
13
 */
14
class Discount extends Model
15
{
16
    use \Bedard\Shop\Traits\Subqueryable,
17
        \Bedard\Shop\Traits\Timeable,
18
        \October\Rain\Database\Traits\Purgeable,
19
        \October\Rain\Database\Traits\Validation;
20
21
    /**
22
     * @var string The database table used by the model.
23
     */
24
    public $table = 'bedard_shop_discounts';
25
26
    /**
27
     * @var array Default attributes
28
     */
29
    public $attributes = [
30
        'is_percentage' => true,
31
    ];
32
33
    /**
34
     * @var array Guarded fields
35
     */
36
    protected $guarded = ['*'];
37
38
    /**
39
     * @var array Fillable fields
40
     */
41
    protected $fillable = [
42
        'amount',
43
        'amount_exact',
44
        'amount_percentage',
45
        'end_at',
46
        'is_percentage',
47
        'name',
48
        'start_at',
49
    ];
50
51
    /**
52
     * @var array Purgeable vields
53
     */
54
    protected $purgeable = [
55
        'amount_exact',
56
        'amount_percentage',
57
    ];
58
59
    /**
60
     * @var array Relations
61
     */
62
    public $belongsToMany = [
63
        'categories' => [
64
            'Bedard\Shop\Models\Category',
65
            'table' => 'bedard_shop_category_discount',
66
        ],
67
        'products' => [
68
            'Bedard\Shop\Models\Product',
69
            'table' => 'bedard_shop_discount_product',
70
        ],
71
    ];
72
73
    public $hasMany = [
74
        'prices' => [
75
            'Bedard\Shop\Models\Price',
76
            'delete' => true,
77
        ],
78
    ];
79
80
    /**
81
     * @var  array Validation rules
82
     */
83
    public $rules = [
84
        'end_at' => 'date',
85
        'name' => 'required',
86
        'start_at' => 'date',
87
        'amount_exact' => 'numeric|min:0',
88
        'amount_percentage' => 'integer|min:0',
89
    ];
90
91
    /**
92
     * After save.
93
     *
94
     * @return void
95
     */
96
    public function afterSave()
97
    {
98
        $this->savePrices();
99
    }
100
101
    /**
102
     * After validate.
103
     *
104
     * @return void
105
     */
106
    public function afterValidate()
107
    {
108
        $this->validateDates();
109
    }
110
111
    /**
112
     * Before save.
113
     *
114
     * @return void
115
     */
116
    public function beforeSave()
117
    {
118
        $this->setAmount();
119
    }
120
121
    /**
122
     * Filter form fields.
123
     *
124
     * @param  object   $fields
125
     * @return void
126
     */
127
    public function filterFields($fields)
128
    {
129
        $fields->amount_exact->hidden = $this->is_percentage;
130
        $fields->amount_percentage->hidden = ! $this->is_percentage;
131
    }
132
133
    /**
134
     * Fetch all of the products within the scope of this discount.
135
     *
136
     * @return array
137
     */
138
    public function getProductIds()
139
    {
140
        $categoryProductIds = $this->categories()
141
            ->select('id')
142
            ->with(['products' => function ($products) {
143
                return $products->select('id');
144
            }])
145
            ->lists('categories.products.id');
146
147
        $productIds = $this->products()->lists('id');
148
149
        return array_unique(array_merge($productIds, $categoryProductIds));
150
    }
151
152
    /**
153
     * Save the prices created by this discount.
154
     *
155
     * @return void
156
     */
157
    public function savePrices()
158
    {
159
        $id = $this->id;
160
        $productIds = $this->getProductIds();
161
162
        Queue::push(function($job) use ($id, $productIds) {
163
            $discount = Discount::findOrFail($id);
164
            $discount->prices()->delete();
165
166
            foreach ($productIds as $productId) {
167
                Price::create([
168
                    'discount_id' => $discount->id,
169
                    'product_id' => $productId,
170
                    'price' => 0,
171
                    'start_at' => $discount->start_at,
172
                    'end_at' => $discount->end_at,
173
                ]);
174
            }
175
176
            $job->delete();
177
        });
178
    }
179
180
    /**
181
     * This exists to makes statuses sortable by assigning them a value.
182
     *
183
     * Expired  0
184
     * Running  1
185
     * Upcoming 2
186
     *
187
     * @param  \October\Rain\Database\Builder   $query
188
     * @return \October\Rain\Database\Builder
189
     */
190
    public function scopeSelectStatus($query)
191
    {
192
        $grammar = $query->getQuery()->getGrammar();
193
        $start_at = $grammar->wrap($this->table.'.start_at');
194
        $end_at = $grammar->wrap($this->table.'.end_at');
195
        $now = Carbon::now();
196
197
        $subquery = 'CASE '.
198
            "WHEN ({$end_at} IS NOT NULL AND {$end_at} < '{$now}') THEN 0 ".
199
            "WHEN ({$start_at} IS NOT NULL AND {$start_at} > '{$now}') THEN 2 ".
200
            'ELSE 1 '.
201
        'END';
202
203
        return $query->selectSubquery($subquery, 'status');
204
    }
205
206
    /**
207
     * Set the discount amount.
208
     *
209
     * @return  void
210
     */
211
    public function setAmount()
212
    {
213
        $exact = $this->getOriginalPurgeValue('amount_exact');
214
        $percentage = $this->getOriginalPurgeValue('amount_percentage');
215
216
        $this->amount = $this->is_percentage
217
            ? $percentage
218
            : $exact;
219
    }
220
221
    /**
222
     * Ensure the start and end dates are valid.
223
     *
224
     * @return void
225
     */
226
    public function validateDates()
227
    {
228
        // Start date must be after the end date
229
        if ($this->start_at !== null &&
230
            $this->end_at !== null &&
231
            $this->start_at >= $this->end_at) {
232
            Flash::error(Lang::get('bedard.shop::lang.discounts.form.start_at_invalid'));
233
            throw new ModelException($this);
234
        }
235
    }
236
}
237