Completed
Push — master ( 28c3ac...3c408f )
by Scott
04:28
created

Product::saveRelatedOptions()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 19
rs 9.2
cc 4
eloc 12
nc 5
nop 1
1
<?php namespace Bedard\Shop\Models;
2
3
use Bedard\Shop\Models\Inventory;
4
use Bedard\Shop\Models\Option;
5
use Bedard\Shop\Models\OptionValue;
6
use DB;
7
use Model;
8
use October\Rain\Database\Builder;
9
use Queue;
10
11
/**
12
 * Product Model.
13
 */
14
class Product extends Model
15
{
16
    use \Bedard\Shop\Traits\Subqueryable,
17
        \October\Rain\Database\Traits\Purgeable,
18
        \October\Rain\Database\Traits\Validation;
19
20
    /**
21
     * @var string The database table used by the model.
22
     */
23
    public $table = 'bedard_shop_products';
24
25
    /**
26
     * @var array Default attributes
27
     */
28
    public $attributes = [
29
        'base_price' => 0,
30
    ];
31
32
    /**
33
     * @var array Guarded fields
34
     */
35
    protected $guarded = ['*'];
36
37
    /**
38
     * @var array Fillable fields
39
     */
40
    protected $fillable = [
41
        'categoriesList',
42
        'name',
43
        'base_price',
44
        'slug',
45
    ];
46
47
    /**
48
     * @var array Purgeable fields
49
     */
50
    public $purgeable = [
51
        'categoriesList',
52
        'optionsInventories',
53
    ];
54
55
    /**
56
     * @var array Relations
57
     */
58
    public $belongsToMany = [
59
        'categories' => [
60
            'Bedard\Shop\Models\Category',
61
            'table' => 'bedard_shop_category_product',
62
            'conditions' => 'is_inherited = 0',
63
        ],
64
        'discounts' => [
65
            'Bedard\Shop\Models\Discount',
66
            'table' => 'bedard_shop_discount_product',
67
        ],
68
    ];
69
70
    public $hasMany = [
71
        'inventories' => [
72
            'Bedard\Shop\Models\Inventory',
73
            'delete' => true,
74
        ],
75
        'options' => [
76
            'Bedard\Shop\Models\Option',
77
            'delete' => true,
78
        ],
79
        'prices' => [
80
            'Bedard\Shop\Models\Price',
81
            'delete' => true,
82
        ],
83
    ];
84
85
    public $hasOne = [
86
        'current_price' => [
87
            'Bedard\Shop\Models\Price',
88
            'scope' => 'isActive',
89
            'order' => 'price asc',
90
        ],
91
    ];
92
93
    /**
94
     * @var array Validation
95
     */
96
    public $rules = [
97
        'name' => 'required',
98
        'base_price' => 'required|numeric|min:0',
99
        'slug' => 'required|unique:bedard_shop_products',
100
    ];
101
102
    /**
103
     * After save.
104
     *
105
     * @return void
106
     */
107
    public function afterSave()
108
    {
109
        $this->saveBasePrice();
110
        $this->saveCategoryRelationships();
111
        $this->saveOptionAndInventoryRelationships();
112
    }
113
114
    /**
115
     * Get the categories options.
116
     *
117
     * @return array
118
     */
119
    public function getCategoriesListOptions()
120
    {
121
        return Category::orderBy('name')->lists('name', 'id');
122
    }
123
124
    /**
125
     * Get the categories that are directly related to this product.
126
     *
127
     * @return void
128
     */
129
    public function getCategoriesListAttribute()
130
    {
131
        return $this->categories()->lists('id');
132
    }
133
134
    /**
135
     * Update of create the related base price model.
136
     *
137
     * @return void
138
     */
139
    public function saveBasePrice()
140
    {
141
        Price::updateOrCreate(
142
            ['product_id' => $this->id, 'discount_id' => null],
143
            ['price' => $this->base_price]
144
        );
145
    }
146
147
    /**
148
     * Sync the categories checkboxlist with the category relationship.
149
     *
150
     * @return void
151
     */
152
    public function saveCategoryRelationships()
153
    {
154
        $categoryIds = $this->getOriginalPurgeValue('categoriesList');
155
156
        if (is_array($categoryIds)) {
157
            $this->categories()->sync($categoryIds);
158
        }
159
160
        $this->syncInheritedCategories();
161
    }
162
163
    /**
164
     * Save the options and inventories
165
     *
166
     * @return void
167
     */
168
    public function saveOptionAndInventoryRelationships()
169
    {
170
        $data = $this->getOriginalPurgeValue('optionsInventories');
171
172
        if (is_array($data['options'])) {
173
            $options = $this->saveRelatedOptions($data['options']);
0 ignored issues
show
Unused Code introduced by
$options is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
174
        }
175
    }
176
177
    /**
178
     * Save an array of related options
179
     *
180
     * @param  array  $options
181
     * @return array
182
     */
183
    protected function saveRelatedOptions(array $options) {
184
        foreach ($options as &$option) {
185
            $model = $option['id'] !== null
186
                ? Option::firstOrNew(['id' => $option['id']])
187
                : new Option;
188
189
            $model->product_id = $this->id;
190
            $model->fill($option);
191
            $model->save();
192
193
            $option['id'] = $model->id;
194
195
            if (is_array($option['values'])) {
196
                $this->saveRelatedOptionValues($model, $option['values']);
197
            }
198
        }
199
200
        return $options;
201
    }
202
203
    /**
204
     * Save related option values.
205
     *
206
     * @param  Option $option
207
     * @param  array  $values
208
     * @return void
209
     */
210
    protected function saveRelatedOptionValues(Option $option, array $values) {
211
        foreach ($values as $value) {
212
            $model = $value['id'] !== null
213
                ? OptionValue::firstOrNew(['id' => $value['id']])
214
                : new OptionValue;
215
216
            $model->option_id = $option->id;
217
            $model->fill($value);
218
            $model->save();
219
        }
220
    }
221
222
    /**
223
     * Left joins a subquery containing the product price.
224
     *
225
     * @param  \October\Rain\Database\Builder   $query
226
     * @return \October\Rain\Database\Builder
227
     */
228
    public function scopeJoinPrice(Builder $query)
229
    {
230
        $alias = 'prices';
231
        $grammar = $query->getQuery()->getGrammar();
232
233
        $subquery = Price::isActive()
234
            ->addselect('bedard_shop_prices.product_id')
235
            ->selectRaw('MIN('.$grammar->wrap('bedard_shop_prices.price').') as '.$grammar->wrap('price'))
236
            ->groupBy('bedard_shop_prices.product_id');
237
238
        return $query
239
            ->addSelect($alias.'.price')
240
            ->joinSubquery($subquery, $alias, 'bedard_shop_products.id', '=', $alias.'.product_id');
241
    }
242
243
    /**
244
     * Sync the inherited categories of all products.
245
     *
246
     * @return void
247
     */
248
    public static function syncAllInheritedCategories()
249
    {
250
        Queue::push(function ($job) {
251
            $data = [];
252
            $products = Product::with('categories')->get();
253
            $categoryTree = Category::getParentCategoryIds();
254
255
            foreach ($products as $product) {
256
                $inheritedCategoryIds = [];
257
                foreach ($product->categories as $category) {
258
                    if (array_key_exists($category->id, $categoryTree)) {
259
                        $inheritedCategoryIds = array_merge($inheritedCategoryIds, $categoryTree[$category->id]);
260
                    }
261
                }
262
263
                foreach (array_unique($inheritedCategoryIds) as $categoryId) {
264
                    $data[] = [
265
                        'category_id' => $categoryId,
266
                        'product_id' => $product->id,
267
                        'is_inherited' => 1,
268
                    ];
269
                }
270
            }
271
272
            DB::table('bedard_shop_category_product')->whereIsInherited(1)->delete();
273
            DB::table('bedard_shop_category_product')->insert($data);
274
            Discount::syncAllPrices();
275
276
            $job->delete();
277
        });
278
    }
279
280
    /**
281
     * Sync a product with it's inherited categories.
282
     *
283
     * @return void
284
     */
285
    public function syncInheritedCategories()
286
    {
287
        $data = [];
288
        $categoryIds = $this->categories()->lists('id');
289
        $parentIds = Category::isParentOf($categoryIds)->lists('id');
290
        foreach ($parentIds as $parentId) {
291
            $data[] = [
292
                'category_id' => $parentId,
293
                'product_id' => $this->id,
294
                'is_inherited' => true,
295
            ];
296
        }
297
298
        DB::table('bedard_shop_category_product')->whereProductId($this->id)->whereIsInherited(1)->delete();
299
        DB::table('bedard_shop_category_product')->insert($data);
300
301
        $categoryScope = array_merge($categoryIds, $parentIds);
302
        Discount::syncProductPrice($this, $categoryScope);
303
    }
304
}
305