Commit 772a3ca4c30980bad4e17b998bf80f08a7cd39bf

Authored by Yarik
1 parent 6d9a4125

Big commit

backend/controllers/SliderController.php
@@ -82,8 +82,7 @@ @@ -82,8 +82,7 @@
82 if ($model->load(Yii::$app->request->post()) && $model->save()) { 82 if ($model->load(Yii::$app->request->post()) && $model->save()) {
83 return $this->redirect( 83 return $this->redirect(
84 [ 84 [
85 - 'view',  
86 - 'id' => $model->id, 85 + 'index',
87 ] 86 ]
88 ); 87 );
89 } else { 88 } else {
@@ -112,8 +111,7 @@ @@ -112,8 +111,7 @@
112 if ($model->load(Yii::$app->request->post()) && $model->save()) { 111 if ($model->load(Yii::$app->request->post()) && $model->save()) {
113 return $this->redirect( 112 return $this->redirect(
114 [ 113 [
115 - 'view',  
116 - 'id' => $model->id, 114 + 'index',
117 ] 115 ]
118 ); 116 );
119 } else { 117 } else {
common/modules/product/controllers/VariantController.php
@@ -234,7 +234,12 @@ @@ -234,7 +234,12 @@
234 $stocks = Stock::find() 234 $stocks = Stock::find()
235 ->joinWith('lang') 235 ->joinWith('lang')
236 ->where([ 'stock_lang.title' => $stock_names ]) 236 ->where([ 'stock_lang.title' => $stock_names ])
237 - ->indexBy('title') 237 + ->indexBy(function($row) {
  238 + /**
  239 + * @var Stock $row
  240 + */
  241 + return $row->lang->title;
  242 + })
238 ->all(); 243 ->all();
239 foreach ($productStocks as $stockName => $quantity) { 244 foreach ($productStocks as $stockName => $quantity) {
240 $quantity = (int) $quantity; 245 $quantity = (int) $quantity;
common/modules/product/models/Import.php
@@ -8,7 +8,12 @@ @@ -8,7 +8,12 @@
8 use Yii; 8 use Yii;
9 use yii\base\Model; 9 use yii\base\Model;
10 use yii\helpers\ArrayHelper; 10 use yii\helpers\ArrayHelper;
11 - 11 +
  12 + /**
  13 + * Class Import
  14 + *
  15 + * @package common\modules\product\models
  16 + */
12 class Import extends Model 17 class Import extends Model
13 { 18 {
14 /** 19 /**
@@ -410,13 +415,13 @@ @@ -410,13 +415,13 @@
410 /** 415 /**
411 * Save ProductVariants 416 * Save ProductVariants
412 * 417 *
413 - * @param array $data ProductVariats data 418 + * @param array $data ProductVariats data
414 * @param float $product_cost_old Old price 419 * @param float $product_cost_old Old price
415 - * @param int $product_id Product ID  
416 - * @param array $category_id Ca 420 + * @param int $product_id Product ID
  421 + * @param array $category_id Ca
417 * @param float|null $product_cost 422 * @param float|null $product_cost
418 * 423 *
419 - * @return array 424 + * @return int[] Array of ProductVariants IDs
420 * @throws \Exception 425 * @throws \Exception
421 */ 426 */
422 private function saveVariants( 427 private function saveVariants(
@@ -503,10 +508,14 @@ @@ -503,10 +508,14 @@
503 return $MOD_ARRAY; 508 return $MOD_ARRAY;
504 } 509 }
505 510
506 - // private function debug($start_time, $message) {  
507 - // echo $message.': '.(time()-$start_time).'s passed';  
508 - // }  
509 - 511 + /**
  512 + * Perform product import
  513 + *
  514 + * @param int $from Begin row
  515 + * @param null $limit Row limit
  516 + *
  517 + * @return array|bool Array if OK, false if error
  518 + */
510 public function goProducts($from = 0, $limit = null) 519 public function goProducts($from = 0, $limit = null)
511 { 520 {
512 set_time_limit(0); 521 set_time_limit(0);
@@ -713,6 +722,13 @@ @@ -713,6 +722,13 @@
713 return $result; 722 return $result;
714 } 723 }
715 724
  725 + /**
  726 + * Get import file
  727 + *
  728 + * @param string $file_type
  729 + *
  730 + * @return bool|resource false if File not found and file resource if OK
  731 + */
716 private function getProductsFile($file_type) 732 private function getProductsFile($file_type)
717 { 733 {
718 $filename = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@' . $file_type); 734 $filename = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@' . $file_type);
@@ -724,11 +740,13 @@ @@ -724,11 +740,13 @@
724 } 740 }
725 741
726 /** 742 /**
727 - * @param $filters array of filters like [['pol'='ะผัƒะถัะบะพะน'],['god' =  
728 - * '2013'],['volume'='25 ะป']*['size'='49 x 30 x  
729 - * 20ัะผ'],['composition'='600D ะฟะพะปะธััั‚ะตั€']]  
730 - * @param int $level 0 for products and 1 for product variant  
731 - * @param $catalog_names array catalogs id 743 + * Save filters
  744 + *
  745 + * @param array $filters array of filters like [['pol'='ะผัƒะถัะบะพะน'],['god' =
  746 + * '2013'],['volume'='25 ะป']*['size'='49 x 30 x
  747 + * 20ัะผ'],['composition'='600D ะฟะพะปะธััั‚ะตั€']]
  748 + * @param int $level 0 for products and 1 for product variant
  749 + * @param int[] $catalog_names array catalogs id
732 * 750 *
733 * @return array 751 * @return array
734 * @throws \Exception 752 * @throws \Exception
@@ -819,4 +837,5 @@ @@ -819,4 +837,5 @@
819 } 837 }
820 return $options; 838 return $options;
821 } 839 }
822 - }  
823 \ No newline at end of file 840 \ No newline at end of file
  841 + }
  842 +
824 \ No newline at end of file 843 \ No newline at end of file
common/modules/product/models/Product.php
@@ -7,46 +7,58 @@ @@ -7,46 +7,58 @@
7 use common\models\ProductToRating; 7 use common\models\ProductToRating;
8 use common\modules\comment\models\CommentModel; 8 use common\modules\comment\models\CommentModel;
9 use common\modules\language\behaviors\LanguageBehavior; 9 use common\modules\language\behaviors\LanguageBehavior;
10 - use common\modules\language\models\Language;  
11 use common\modules\rubrication\models\TaxGroup; 10 use common\modules\rubrication\models\TaxGroup;
  11 + use common\modules\rubrication\models\TaxGroupToCategory;
12 use common\modules\rubrication\models\TaxOption; 12 use common\modules\rubrication\models\TaxOption;
13 use Yii; 13 use Yii;
  14 + use yii\base\InvalidParamException;
14 use yii\db\ActiveQuery; 15 use yii\db\ActiveQuery;
15 use yii\db\ActiveRecord; 16 use yii\db\ActiveRecord;
16 use yii\helpers\ArrayHelper; 17 use yii\helpers\ArrayHelper;
  18 + use yii\web\NotFoundHttpException;
17 use yii\web\Request; 19 use yii\web\Request;
18 20
19 /** 21 /**
20 * This is the model class for table "{{%product}}". 22 * This is the model class for table "{{%product}}".
21 * 23 *
22 - * @property integer $brand_id  
23 - * @property integer $id  
24 - * @property Category $category  
25 - * @property Category[] $categories  
26 - * @property ProductVariant[] $variants  
27 - * @property ProductVariant $variant  
28 - * @property boolean $is_top  
29 - * @property boolean $is_new  
30 - * @property boolean $is_discount  
31 - * @property ProductToRating $averageRating  
32 - * @property TaxGroup[] $properties  
33 - * @property ProductVariant $enabledVariant  
34 - * @property ProductVariant[] $enabledVariants  
35 - * @property string $video  
36 - * @property TaxOption[] $options  
37 - * @property Brand $brand  
38 - * @property TaxOption[] $filters  
39 - * @property ProductVariant[] $variantsWithFilters  
40 - * @property string $remote_id  
41 - * @property string $fullname 24 + * @property integer $brand_id
  25 + * @property integer $id
  26 + * @property Category $category
  27 + * @property Category[] $categories
  28 + * @property ProductVariant $variant
  29 + * @property ProductVariant[] $variants
  30 + * @property ProductVariant $productVariant
  31 + * @property ProductVariant[] $productVariants
  32 + * @property boolean $is_top
  33 + * @property boolean $is_new
  34 + * @property boolean $is_discount
  35 + * @property ProductToRating $averageRating
  36 + * @property TaxGroup[] $properties
  37 + * @property ProductVariant $enabledVariant
  38 + * @property ProductVariant[] $enabledVariants
  39 + * @property string $video
  40 + * @property TaxOption[] $options
  41 + * @property Brand $brand
  42 + * @property TaxOption[] $filters
  43 + * @property ProductVariant[] $variantsWithFilters
  44 + * @property string $remote_id
  45 + * @property string $fullname
  46 + * @property float $variantPrice
  47 + * @property float $enabledVariantPrice
  48 + * @property array $categoryNames
  49 + * @property Stock[] $stocks
  50 + * @property ProductStock[] $productStocks
  51 + * @property int $quantity
  52 + * @property TaxGroupToCategory[] $categoriesToGroups
  53 + * @property TaxGroup[] $taxGroupsByLevel
42 * * From language behavior * 54 * * From language behavior *
43 - * @property ProductLang $lang  
44 - * @property ProductLang[] $langs  
45 - * @property ProductLang $objectLang  
46 - * @property string $ownerKey  
47 - * @property string $langKey  
48 - * @property ProductLang[] $modelLangs  
49 - * @property bool $transactionStatus 55 + * @property ProductLang $lang
  56 + * @property ProductLang[] $langs
  57 + * @property ProductLang $objectLang
  58 + * @property string $ownerKey
  59 + * @property string $langKey
  60 + * @property ProductLang[] $modelLangs
  61 + * @property bool $transactionStatus
50 * @method string getOwnerKey() 62 * @method string getOwnerKey()
51 * @method void setOwnerKey( string $value ) 63 * @method void setOwnerKey( string $value )
52 * @method string getLangKey() 64 * @method string getLangKey()
@@ -60,9 +72,9 @@ @@ -60,9 +72,9 @@
60 * @method bool getTransactionStatus() 72 * @method bool getTransactionStatus()
61 * * End language behavior * 73 * * End language behavior *
62 * * From multipleImage behavior 74 * * From multipleImage behavior
63 - * @property ProductImage $image  
64 - * @property ProductImage[] $images  
65 - * @property array imagesConfig 75 + * @property ProductImage $image
  76 + * @property ProductImage[] $images
  77 + * @property array imagesConfig
66 * @method ActiveQuery getImage() 78 * @method ActiveQuery getImage()
67 * @method ActiveQuery getImages() 79 * @method ActiveQuery getImages()
68 * @method array getImagesConfig() 80 * @method array getImagesConfig()
@@ -86,7 +98,7 @@ @@ -86,7 +98,7 @@
86 'directory' => 'products', 98 'directory' => 'products',
87 'column' => 'image', 99 'column' => 'image',
88 'links' => [ 100 'links' => [
89 - 'product_id' => 'id', 101 + 'id' => 'product_id',
90 ], 102 ],
91 'model' => ProductImage::className(), 103 'model' => ProductImage::className(),
92 ], 104 ],
@@ -102,7 +114,7 @@ @@ -102,7 +114,7 @@
102 'config' => [ 114 'config' => [
103 'caption' => 'image', 115 'caption' => 'image',
104 'delete_action' => '/product/manage/delete-image', 116 'delete_action' => '/product/manage/delete-image',
105 - 'id' => 'product_image_id', 117 + 'id' => 'id',
106 ], 118 ],
107 ], 119 ],
108 'language' => [ 120 'language' => [
@@ -164,9 +176,7 @@ @@ -164,9 +176,7 @@
164 'id' => Yii::t('product', 'ID'), 176 'id' => Yii::t('product', 'ID'),
165 'brand_id' => Yii::t('product', 'Brand'), 177 'brand_id' => Yii::t('product', 'Brand'),
166 'categories' => Yii::t('product', 'Categories'), 178 'categories' => Yii::t('product', 'Categories'),
167 - // relation behavior field  
168 'category' => Yii::t('product', 'Category'), 179 'category' => Yii::t('product', 'Category'),
169 - // relation behavior field  
170 'image' => Yii::t('product', 'Image'), 180 'image' => Yii::t('product', 'Image'),
171 'images' => Yii::t('product', 'Images'), 181 'images' => Yii::t('product', 'Images'),
172 'video' => Yii::t('product', 'Video embeded'), 182 'video' => Yii::t('product', 'Video embeded'),
@@ -178,6 +188,8 @@ @@ -178,6 +188,8 @@
178 } 188 }
179 189
180 /** 190 /**
  191 + * Get Brand query to current Product
  192 + *
181 * @return \yii\db\ActiveQuery 193 * @return \yii\db\ActiveQuery
182 */ 194 */
183 public function getBrand() 195 public function getBrand()
@@ -186,6 +198,8 @@ @@ -186,6 +198,8 @@
186 } 198 }
187 199
188 /** 200 /**
  201 + * Get ProductVariant query to current Product
  202 + *
189 * @return \yii\db\ActiveQuery 203 * @return \yii\db\ActiveQuery
190 */ 204 */
191 public function getVariant() 205 public function getVariant()
@@ -194,62 +208,135 @@ @@ -194,62 +208,135 @@
194 } 208 }
195 209
196 /** 210 /**
  211 + * Synonim of getVariant()
  212 + *
  213 + * @see Product::getVariant()
197 * @return \yii\db\ActiveQuery 214 * @return \yii\db\ActiveQuery
198 */ 215 */
199 - public function getEnabledVariant() 216 + public function getProductVariant()
200 { 217 {
201 - return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ])  
202 - ->andOnCondition(  
203 - [  
204 - '!=',  
205 - ProductVariant::tableName() . '.stock',  
206 - 0,  
207 - ]  
208 - ); 218 + return $this->getVariant();
209 } 219 }
210 220
211 - public function getVariantPrice() 221 + /**
  222 + * Get ProductVariants query to current Product
  223 + *
  224 + * @return \yii\db\ActiveQuery
  225 + */
  226 + public function getVariants()
212 { 227 {
213 - return $this->variant->price; 228 + return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]);
214 } 229 }
215 230
216 - public function getEnabledVariantPrice() 231 + /**
  232 + * Synonim of getVariants()
  233 + *
  234 + * @see Product::getVariants()
  235 + * @return \yii\db\ActiveQuery
  236 + */
  237 + public function getProductVariants()
217 { 238 {
218 - return $this->enabledVariants[ 0 ]->price; 239 + return $this->getVariant();
219 } 240 }
220 241
221 /** 242 /**
  243 + * Get ProductVariant query fetching only available in stock to current Product
  244 + *
  245 + * @see Product::getVariant()
222 * @return \yii\db\ActiveQuery 246 * @return \yii\db\ActiveQuery
223 */ 247 */
224 - public function getVariants() 248 + public function getEnabledVariant()
225 { 249 {
226 - return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]); 250 + return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ])
  251 + ->andWhere(
  252 + [
  253 + '!=',
  254 + ProductVariant::tableName() . '.stock',
  255 + 0,
  256 + ]
  257 + );
227 } 258 }
228 259
  260 + /**
  261 + * Get ProductVariants query fetching only available in stock to current Product
  262 + *
  263 + * @see Product::getVariants()
  264 + * @return \yii\db\ActiveQuery
  265 + */
229 public function getEnabledVariants() 266 public function getEnabledVariants()
230 { 267 {
231 return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) 268 return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ])
232 - ->andOnCondition( 269 + ->andWhere(
233 [ 270 [
234 '!=', 271 '!=',
235 ProductVariant::tableName() . '.stock', 272 ProductVariant::tableName() . '.stock',
236 0, 273 0,
237 ] 274 ]
238 - )  
239 - ->joinWith('image'); 275 + );
  276 + }
  277 +
  278 + /**
  279 + * Get random ProductVariant price or 0 if not exist
  280 + *
  281 + * @param bool $exception Whether to throw exception if variant not exist
  282 + *
  283 + * @return float
  284 + * @throws \yii\web\NotFoundHttpException
  285 + */
  286 + public function getVariantPrice(bool $exception = false): float
  287 + {
  288 + if (!empty( $this->variant )) {
  289 + return $this->variant->price;
  290 + } elseif ($exception) {
  291 + throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got variants');
  292 + } else {
  293 + return 0;
  294 + }
240 } 295 }
241 296
242 - public function setVariants($variants) 297 + /**
  298 + * Get random ProductVariant that in stock price or 0 or exception if not exist
  299 + *
  300 + * @param bool $exception Whether to throw exception if variant not exist
  301 + *
  302 + * @return float
  303 + * @throws \yii\web\NotFoundHttpException
  304 + */
  305 + public function getEnabledVariantPrice(bool $exception = false): float
243 { 306 {
244 - $this->variants = $variants; 307 + if (!empty( $this->enabledVariant )) {
  308 + return $this->enabledVariant->price;
  309 + } elseif ($exception) {
  310 + throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got enabled variants');
  311 + } else {
  312 + return 0;
  313 + }
245 } 314 }
246 315
247 - public function getFullname() 316 + /**
  317 + * Get Product name concatenated with Brand name
  318 + *
  319 + * @return string
  320 + */
  321 + public function getFullname():string
248 { 322 {
249 return empty( $this->brand ) ? $this->lang->title : $this->brand->lang->title . ' ' . $this->lang->title; 323 return empty( $this->brand ) ? $this->lang->title : $this->brand->lang->title . ' ' . $this->lang->title;
250 } 324 }
251 325
252 /** 326 /**
  327 + * Get Category query for current Product
  328 + *
  329 + * @return ActiveQuery
  330 + */
  331 + public function getCategory()
  332 + {
  333 + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ])
  334 + ->viaTable('product_category', [ 'product_id' => 'id' ]);
  335 + }
  336 +
  337 + /**
  338 + * Get Categories query for current Product
  339 + *
253 * @return ActiveQuery 340 * @return ActiveQuery
254 */ 341 */
255 public function getCategories() 342 public function getCategories()
@@ -258,19 +345,30 @@ @@ -258,19 +345,30 @@
258 ->viaTable('product_category', [ 'product_id' => 'id' ]); 345 ->viaTable('product_category', [ 'product_id' => 'id' ]);
259 } 346 }
260 347
261 - public function getCategoriesNames() 348 + /**
  349 + * @param bool $index
  350 + *
  351 + * @return array
  352 + */
  353 + public function getCategoryNames(bool $index = false): array
262 { 354 {
263 - $result = [];  
264 - foreach ($this->categories as $category) {  
265 - $result[] = $category->lang->title; 355 + if ($index) {
  356 + $result = ArrayHelper::map($this->categories, 'id', 'lang.title');
  357 + } else {
  358 + $result = ArrayHelper::getColumn($this->categories, 'lang.title');
266 } 359 }
267 return $result; 360 return $result;
268 } 361 }
269 362
  363 + /**
  364 + * Get ProductVariants query with lang, filters and image for current Product
  365 + *
  366 + * @return ActiveQuery
  367 + */
270 public function getVariantsWithFilters() 368 public function getVariantsWithFilters()
271 { 369 {
272 return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) 370 return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ])
273 - ->joinWith('lang', true, 'INNER JOIN') 371 + ->joinWith('lang')
274 ->with( 372 ->with(
275 [ 373 [
276 'filters', 374 'filters',
@@ -279,50 +377,11 @@ @@ -279,50 +377,11 @@
279 ); 377 );
280 } 378 }
281 379
282 - public function getVariantsFilter()  
283 - {  
284 - return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ])  
285 - ->joinWith('lang')  
286 - ->joinWith(  
287 - [  
288 - 'options variant_options' => function ($query) {  
289 - /**  
290 - * @var ActiveQuery $query  
291 - */  
292 - $query->joinWith([ 'langs variant_options_lang' ])  
293 - ->andWhere(  
294 - [ 'variant_options_lang.language_id' => Language::getCurrent()->id ]  
295 - )  
296 - ->joinWith(  
297 - [  
298 - 'taxGroup variant_options_group' => function ($subquery) {  
299 - /**  
300 - * @var ActiveQuery $subquery  
301 - */  
302 - $subquery->joinWith([ 'langs variant_options_group_lang' ])  
303 - ->andWhere(  
304 - [  
305 - 'variant_options_group_lang.language_id' => Language::getCurrent(  
306 - )->id,  
307 - ]  
308 - );  
309 - },  
310 - ]  
311 - );  
312 - },  
313 - ]  
314 - );  
315 - }  
316 -  
317 /** 380 /**
  381 + * Get TaxOptions query for current Product
  382 + *
318 * @return ActiveQuery 383 * @return ActiveQuery
319 */ 384 */
320 - public function getCategory()  
321 - {  
322 - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ])  
323 - ->viaTable('product_category', [ 'product_id' => 'id' ]);  
324 - }  
325 -  
326 public function getOptions() 385 public function getOptions()
327 { 386 {
328 return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) 387 return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ])
@@ -330,9 +389,24 @@ @@ -330,9 +389,24 @@
330 } 389 }
331 390
332 /** 391 /**
  392 + * Get TaxOptions query for current Product joined with TaxGroups
  393 + *
  394 + * @see Product::getOptions()
  395 + * @return ActiveQuery
  396 + */
  397 + public function getFilters()
  398 + {
  399 + return $this->getOptions()
  400 + ->joinWith('taxGroup.lang')
  401 + ->joinWith('lang');
  402 + }
  403 +
  404 + /**
  405 + * Get all TaxGroups for current Product filled with $customOptions that satisfy current Product
  406 + *
333 * @return TaxGroup[] 407 * @return TaxGroup[]
334 */ 408 */
335 - public function getProperties() 409 + public function getProperties(): array
336 { 410 {
337 $groups = $options = []; 411 $groups = $options = [];
338 foreach ($this->getOptions() 412 foreach ($this->getOptions()
@@ -341,7 +415,7 @@ @@ -341,7 +415,7 @@
341 /** 415 /**
342 * @var TaxOption $option 416 * @var TaxOption $option
343 */ 417 */
344 - $options[ $option->tax_group_id ][] = $option; 418 + $options[ $option[ 'tax_group_id' ] ][] = $option;
345 } 419 }
346 foreach (TaxGroup::find() 420 foreach (TaxGroup::find()
347 ->where([ 'id' => array_keys($options) ]) 421 ->where([ 'id' => array_keys($options) ])
@@ -351,60 +425,52 @@ @@ -351,60 +425,52 @@
351 * @var TaxGroup $group 425 * @var TaxGroup $group
352 */ 426 */
353 if (!empty( $options[ $group->id ] )) { 427 if (!empty( $options[ $group->id ] )) {
354 - $group->options = $options[ $group->id ];  
355 - $groups[] = $group;  
356 - }  
357 - }  
358 - return $groups;  
359 - }  
360 -  
361 - public function getActiveProperties($category_id)  
362 - {  
363 - $groups = $options = [];  
364 - foreach ($this->options as $option) {  
365 - $options[ $option->tax_group_id ][] = $option;  
366 - }  
367 - /**  
368 - * @var TaxGroup[] $taxGroups  
369 - */  
370 - $taxGroups = TaxGroup::find()  
371 - ->joinWith('categories')  
372 - ->where(  
373 - [  
374 - 'tax_group.id' => array_keys($options),  
375 - 'tax_group.display' => true,  
376 - 'category.id' => $category_id,  
377 - ]  
378 - )  
379 - ->all();  
380 -  
381 - foreach ($taxGroups as $group) {  
382 - if (!empty( $options[ $group->id ] )) {  
383 - $group->options = $options[ $group->id ]; 428 + $group->customOptions = $options[ $group->id ];
384 $groups[] = $group; 429 $groups[] = $group;
385 } 430 }
386 } 431 }
387 return $groups; 432 return $groups;
388 } 433 }
389 434
  435 + /**
  436 + * Get Stock query where current Product is in stock
  437 + *
  438 + * @return ActiveQuery
  439 + */
390 public function getStocks() 440 public function getStocks()
391 { 441 {
392 - return $this->hasMany(Stock::className(), [ 'id' => 'product_id' ])  
393 - ->via('variants'); 442 + return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ])
  443 + ->via('productStocks');
394 } 444 }
395 445
  446 + /**
  447 + * Get ProductStocks query for current Product
  448 + *
  449 + * @return ActiveQuery
  450 + */
396 public function getProductStocks() 451 public function getProductStocks()
397 { 452 {
398 return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]) 453 return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ])
399 ->via('variants'); 454 ->via('variants');
400 } 455 }
401 456
402 - public function getQuantity() 457 + /**
  458 + * Get quantity of all ProductVariants for current Product
  459 + *
  460 + * @see Product::getProductStocks()
  461 + * @return int
  462 + */
  463 + public function getQuantity():int
403 { 464 {
404 return $this->getProductStocks() 465 return $this->getProductStocks()
405 ->sum('quantity'); 466 ->sum('quantity');
406 } 467 }
407 468
  469 + /**
  470 + * Override Categories and TaxOptions
  471 + *
  472 + * @inheritdoc
  473 + */
408 public function afterSave($insert, $changedAttributes) 474 public function afterSave($insert, $changedAttributes)
409 { 475 {
410 parent::afterSave($insert, $changedAttributes); 476 parent::afterSave($insert, $changedAttributes);
@@ -424,37 +490,15 @@ @@ -424,37 +490,15 @@
424 $this->link('options', $option); 490 $this->link('options', $option);
425 } 491 }
426 } 492 }
427 -  
428 - if (!empty( $this->variants )) {  
429 - $todel = [];  
430 - foreach ($this->variants ? : [] as $_variant) {  
431 - /**  
432 - * @var ProductVariant $_variant  
433 - */  
434 - $todel[ $_variant->id ] = $_variant->id;  
435 - }  
436 - foreach ($this->variants as $_variant) {  
437 - if (!is_array($_variant)) {  
438 - return;  
439 - }  
440 - if (!empty( $_variant[ 'id' ] )) {  
441 - unset( $todel[ $_variant[ 'id' ] ] );  
442 - $model = ProductVariant::findOne($_variant[ 'id' ]);  
443 - } else {  
444 - $model = new ProductVariant();  
445 - }  
446 - $_variant[ 'product_id' ] = $this->id;  
447 - $model->load([ 'ProductVariant' => $_variant ]);  
448 - $model->product_id = $this->id;  
449 - $model->save();  
450 - }  
451 - if (!empty( $todel )) {  
452 - ProductVariant::deleteAll([ 'id' => $todel ]);  
453 - }  
454 - }  
455 } 493 }
456 494
457 - public function recalculateRating() 495 + /**
  496 + * Recalculate rating for artboxcomment module
  497 + *
  498 + * @todo Rewrite with behavior
  499 + * @return bool
  500 + */
  501 + public function recalculateRating():bool
458 { 502 {
459 /** 503 /**
460 * @var ProductToRating $averageRating 504 * @var ProductToRating $averageRating
@@ -484,6 +528,12 @@ @@ -484,6 +528,12 @@
484 } 528 }
485 } 529 }
486 530
  531 + /**
  532 + * Get CommmentModel query for artboxcomment module
  533 + *
  534 + * @todo Rewrite with behavior
  535 + * @return ActiveQuery
  536 + */
487 public function getComments() 537 public function getComments()
488 { 538 {
489 return $this->hasMany(CommentModel::className(), [ 'entity_id' => 'id' ]) 539 return $this->hasMany(CommentModel::className(), [ 'entity_id' => 'id' ])
@@ -496,39 +546,67 @@ @@ -496,39 +546,67 @@
496 ); 546 );
497 } 547 }
498 548
  549 + /**
  550 + * Get ProductToRating query in order to get average rating for current Product
  551 + *
  552 + * @return \yii\db\ActiveQuery
  553 + */
499 public function getAverageRating() 554 public function getAverageRating()
500 { 555 {
501 return $this->hasOne(ProductToRating::className(), [ 'product_id' => 'id' ]); 556 return $this->hasOne(ProductToRating::className(), [ 'product_id' => 'id' ]);
502 } 557 }
503 558
504 - public function getTaxGroupsByLevel($level) 559 + /**
  560 + * Get TaxGroupToCategories query via product_category table
  561 + *
  562 + * @return ActiveQuery
  563 + */
  564 + public function getCategoriesToGroups()
505 { 565 {
506 - $categories = ArrayHelper::getColumn($this->categories, 'id');  
507 - return TaxGroup::find()  
508 - ->distinct()  
509 - ->innerJoin(  
510 - 'tax_group_to_category',  
511 - 'tax_group_to_category.tax_group_id = tax_group.id'  
512 - )  
513 - ->andWhere([ 'tax_group_to_category.category_id' => $categories ])  
514 - ->andWhere([ 'level' => $level ]); 566 + return $this->hasMany(TaxGroupToCategory::className(), [ 'category_id' => 'category_id' ])
  567 + ->viaTable('product_category', [ 'product_id' => 'id' ]);
515 } 568 }
516 569
  570 + /**
  571 + * Get TaxGroups query for current Product according to level
  572 + * * 0 - Product Tax Groups
  573 + * * 1 - ProductVariant Tax Groups
  574 + *
  575 + * @param int $level
  576 + *
  577 + * @return ActiveQuery
  578 + * @throws InvalidParamException
  579 + */
  580 + public function getTaxGroupsByLevel(int $level = 0)
  581 + {
  582 + if ($level !== 0 && $level !== 1) {
  583 + throw new InvalidParamException(
  584 + 'Level must be 0 for Product Tax Groups or 1 for Product Variant Tax Groups'
  585 + );
  586 + }
  587 + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ])
  588 + ->via('categoriesToGroups')
  589 + ->where([ 'level' => $level ])
  590 + ->distinct();
  591 + }
  592 +
  593 + /**
  594 + * Setter for Categories
  595 + *
  596 + * @param array $values
  597 + */
517 public function setCategories($values) 598 public function setCategories($values)
518 { 599 {
519 $this->categories = $values; 600 $this->categories = $values;
520 } 601 }
521 602
  603 + /**
  604 + * Setter for Options
  605 + *
  606 + * @param array $values
  607 + */
522 public function setOptions($values) 608 public function setOptions($values)
523 { 609 {
524 $this->options = $values; 610 $this->options = $values;
525 } 611 }
526 -  
527 - public function getFilters()  
528 - {  
529 - return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ])  
530 - ->viaTable('product_option', [ 'product_id' => 'id' ])  
531 - ->joinWith('taxGroup.lang', true, 'INNER JOIN')  
532 - ->joinWith('lang', true, 'INNER JOIN');  
533 - }  
534 } 612 }
common/modules/product/models/ProductCategory.php
@@ -2,21 +2,17 @@ @@ -2,21 +2,17 @@
2 2
3 namespace common\modules\product\models; 3 namespace common\modules\product\models;
4 4
5 - use common\modules\rubrication\models\TaxOption;  
6 use Yii; 5 use Yii;
7 use yii\db\ActiveRecord; 6 use yii\db\ActiveRecord;
8 7
9 /** 8 /**
10 * This is the model class for table "{{%product_category}}". 9 * This is the model class for table "{{%product_category}}".
11 - * @property integer $product_id  
12 - * @property integer $category_id  
13 - * @property TaxOption $devCategory 10 + *
  11 + * @property integer $product_id
  12 + * @property integer $category_id
14 */ 13 */
15 class ProductCategory extends ActiveRecord 14 class ProductCategory extends ActiveRecord
16 { 15 {
17 -  
18 - public $alias = 'product_categories';  
19 -  
20 /** 16 /**
21 * @inheritdoc 17 * @inheritdoc
22 */ 18 */
@@ -66,4 +62,13 @@ @@ -66,4 +62,13 @@
66 ]; 62 ];
67 } 63 }
68 64
  65 + public function getCategory()
  66 + {
  67 + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]);
  68 + }
  69 +
  70 + public function getProduct()
  71 + {
  72 + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]);
  73 + }
69 } 74 }
common/modules/product/models/ProductImage.php
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 /** 9 /**
10 * This is the model class for table "product_image". 10 * This is the model class for table "product_image".
11 * 11 *
12 - * @property integer $product_image_id 12 + * @property integer $id
13 * @property integer $product_id 13 * @property integer $product_id
14 * @property integer $product_variant_id 14 * @property integer $product_variant_id
15 * @property string $image 15 * @property string $image
@@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
52 ], 52 ],
53 [ 53 [
54 [ 54 [
55 - 'product_image_id', 55 + 'id',
56 'product_id', 56 'product_id',
57 'product_variant_id', 57 'product_variant_id',
58 ], 58 ],
@@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
90 public function attributeLabels() 90 public function attributeLabels()
91 { 91 {
92 return [ 92 return [
93 - 'product_image_id' => Yii::t('product', 'Product Image ID'), 93 + 'id' => Yii::t('product', 'Product Image ID'),
94 'product_id' => Yii::t('product', 'Product ID'), 94 'product_id' => Yii::t('product', 'Product ID'),
95 'product_variant_id' => Yii::t('product', 'Product Variant ID'), 95 'product_variant_id' => Yii::t('product', 'Product Variant ID'),
96 'product' => Yii::t('product', 'Product'), 96 'product' => Yii::t('product', 'Product'),
@@ -107,9 +107,6 @@ @@ -107,9 +107,6 @@
107 public function getProduct() 107 public function getProduct()
108 { 108 {
109 $return = $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); 109 $return = $this->hasOne(Product::className(), [ 'id' => 'product_id' ]);
110 - if (empty( $return )) {  
111 - $return = $this->productVariant->product_id;  
112 - }  
113 return $return; 110 return $return;
114 } 111 }
115 112
common/modules/product/models/ProductSearch.php
@@ -89,7 +89,6 @@ @@ -89,7 +89,6 @@
89 */ 89 */
90 public function search($params) 90 public function search($params)
91 { 91 {
92 -  
93 $query = Product::find(); 92 $query = Product::find();
94 $query->select( 93 $query->select(
95 [ 94 [
@@ -150,6 +149,14 @@ @@ -150,6 +149,14 @@
150 ], 149 ],
151 ] 150 ]
152 ); 151 );
  152 +
  153 + $this->load($params);
  154 +
  155 + if(!$this->validate()) {
  156 + // uncomment the following line if you do not want to return any records when validation fails
  157 + // $query->where('0=1');
  158 + return $dataProvider;
  159 + }
153 160
154 if (isset( $this->is_top )) { 161 if (isset( $this->is_top )) {
155 $query->andWhere( 162 $query->andWhere(
common/modules/product/models/ProductStock.php
@@ -13,10 +13,11 @@ @@ -13,10 +13,11 @@
13 * @property Product $product 13 * @property Product $product
14 * @property ProductVariant $productVariant 14 * @property ProductVariant $productVariant
15 * @property Stock $stock 15 * @property Stock $stock
  16 + * @property string $title
16 */ 17 */
17 class ProductStock extends ActiveRecord 18 class ProductStock extends ActiveRecord
18 { 19 {
19 - 20 + protected $title;
20 /** 21 /**
21 * @inheritdoc 22 * @inheritdoc
22 */ 23 */
@@ -90,17 +91,28 @@ @@ -90,17 +91,28 @@
90 return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); 91 return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]);
91 } 92 }
92 93
93 - public function getName() 94 + /**
  95 + * Get Stock title, tries to get from Stock lang
  96 + *
  97 + * @return string
  98 + */
  99 + public function getTitle(): string
94 { 100 {
95 - return ( !empty( $this->stock ) ) ? $this->stock->title : ''; 101 + if (!empty( $this->title )) {
  102 + return $this->title;
  103 + } elseif (!empty( $this->stock )) {
  104 + return $this->stock->lang->title;
  105 + } else {
  106 + return '';
  107 + }
96 } 108 }
97 109
98 /** 110 /**
99 - * @todo Check if needed 111 + * Set Stock title, will be saved to Stock table
100 * 112 *
101 * @param mixed $value 113 * @param mixed $value
102 */ 114 */
103 - public function setName($value) 115 + public function setTitle(string $value)
104 { 116 {
105 $this->title = $value; 117 $this->title = $value;
106 } 118 }
@@ -113,6 +125,9 @@ @@ -113,6 +125,9 @@
113 return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]); 125 return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]);
114 } 126 }
115 127
  128 + /**
  129 + * @inheritdoc
  130 + */
116 public static function primaryKey() 131 public static function primaryKey()
117 { 132 {
118 return [ 133 return [
common/modules/product/models/ProductVariant.php
@@ -8,15 +8,14 @@ @@ -8,15 +8,14 @@
8 use common\modules\rubrication\models\TaxGroup; 8 use common\modules\rubrication\models\TaxGroup;
9 use common\modules\rubrication\models\TaxOption; 9 use common\modules\rubrication\models\TaxOption;
10 use Yii; 10 use Yii;
  11 + use yii\base\InvalidParamException;
11 use yii\db\ActiveQuery; 12 use yii\db\ActiveQuery;
12 use yii\db\ActiveRecord; 13 use yii\db\ActiveRecord;
13 - use yii\helpers\ArrayHelper;  
14 use yii\web\Request; 14 use yii\web\Request;
15 15
16 /** 16 /**
17 * This is the model class for table "product_variant". 17 * This is the model class for table "product_variant".
18 * 18 *
19 - * @todo Refactor  
20 * @property integer $id 19 * @property integer $id
21 * @property integer $product_id 20 * @property integer $product_id
22 * @property integer $remote_id 21 * @property integer $remote_id
@@ -25,12 +24,19 @@ @@ -25,12 +24,19 @@
25 * @property double $price_old 24 * @property double $price_old
26 * @property double $stock 25 * @property double $stock
27 * @property integer $product_unit_id 26 * @property integer $product_unit_id
28 - * @property string|null $fullname 27 + * @property string $fullname
29 * @property TaxOption[] $options 28 * @property TaxOption[] $options
30 * @property ProductUnit $productUnit 29 * @property ProductUnit $productUnit
31 * @property Product $product 30 * @property Product $product
32 * @property Category[] $categories 31 * @property Category[] $categories
  32 + * @property Category $category
33 * @property TaxOption[] $filters 33 * @property TaxOption[] $filters
  34 + * @property ProductStock[] $productStocks
  35 + * @property int $quantity
  36 + * @property ProductStock[] $variantStocks
  37 + * @property Stock[] $stocks
  38 + * @property TaxGroup[] $properties
  39 + * @property TaxGroup[] $taxGroupsByLevel
34 * * From language behavior * 40 * * From language behavior *
35 * @property ProductVariantLang $lang 41 * @property ProductVariantLang $lang
36 * @property ProductVariantLang[] $langs 42 * @property ProductVariantLang[] $langs
@@ -63,10 +69,9 @@ @@ -63,10 +69,9 @@
63 */ 69 */
64 class ProductVariant extends ActiveRecord 70 class ProductVariant extends ActiveRecord
65 { 71 {
66 - public $sumCost;  
67 -  
68 - public $productName;  
69 - 72 + /**
  73 + * @var int[] $options
  74 + */
70 private $options; 75 private $options;
71 76
72 /** @var array $_images */ 77 /** @var array $_images */
@@ -98,7 +103,7 @@ @@ -98,7 +103,7 @@
98 'column' => 'image', 103 'column' => 'image',
99 'links' => [ 104 'links' => [
100 'product_id' => 'product_id', 105 'product_id' => 'product_id',
101 - 'product_variant_id' => 'id', 106 + 'id' => 'product_variant_id',
102 ], 107 ],
103 'model' => ProductImage::className(), 108 'model' => ProductImage::className(),
104 ], 109 ],
@@ -111,7 +116,7 @@ @@ -111,7 +116,7 @@
111 'config' => [ 116 'config' => [
112 'caption' => 'image', 117 'caption' => 'image',
113 'delete_action' => '/product/variant/delete-image', 118 'delete_action' => '/product/variant/delete-image',
114 - 'id' => 'product_image_id', 119 + 'id' => 'id',
115 ], 120 ],
116 ], 121 ],
117 ]; 122 ];
@@ -165,6 +170,13 @@ @@ -165,6 +170,13 @@
165 'targetClass' => ProductUnit::className(), 170 'targetClass' => ProductUnit::className(),
166 'targetAttribute' => [ 'product_unit_id' => 'id' ], 171 'targetAttribute' => [ 'product_unit_id' => 'id' ],
167 ], 172 ],
  173 + [
  174 + [ 'product_id' ],
  175 + 'exist',
  176 + 'skipOnError' => true,
  177 + 'targetClass' => Product::className(),
  178 + 'targetAttribute' => [ 'product_id' => 'id' ],
  179 + ],
168 ]; 180 ];
169 } 181 }
170 182
@@ -203,63 +215,104 @@ @@ -203,63 +215,104 @@
203 return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); 215 return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]);
204 } 216 }
205 217
206 - public function getProductStock() 218 + /**
  219 + * @return \yii\db\ActiveQuery
  220 + */
  221 + public function getProductStocks()
207 { 222 {
208 return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]); 223 return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]);
209 } 224 }
210 225
211 - public function getQuantity()  
212 - {  
213 - return ProductStock::find()  
214 - ->where([ 'product_variant_id' => $this->id ])  
215 - ->sum('quantity');  
216 - }  
217 -  
218 - public function getStockCaption() 226 + /**
  227 + * Get qunatity for current ProductVariant
  228 + * If $recalculate set to true will recalculate stock via product_stock table
  229 + *
  230 + * @param bool $recalculate
  231 + *
  232 + * @return int
  233 + */
  234 + public function getQuantity(bool $recalculate = false): int
219 { 235 {
220 - return is_null($this->stock) ? 'โˆž' : ( $this->stock > 0 ? Yii::t('product', 'Enable') : Yii::t(  
221 - 'product',  
222 - 'Disable'  
223 - ) ); 236 + if (!$recalculate) {
  237 + return $this->stock;
  238 + } else {
  239 + $quantity = $this->getProductStocks()
  240 + ->sum('quantity');
  241 + if (empty( $quantity )) {
  242 + $this->stock = 0;
  243 + } else {
  244 + $this->stock = (int) $quantity;
  245 + }
  246 + $this->save(false, [ 'stock' ]);
  247 + return $this->stock;
  248 + }
224 } 249 }
225 250
  251 + /**
  252 + * Get ProductStocks query woth preloaded Stocks for current ProductVariant
  253 + * **Used in dynamic fields in product variant form**
  254 + *
  255 + * @return ActiveQuery
  256 + */
226 public function getVariantStocks() 257 public function getVariantStocks()
227 { 258 {
228 - return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]) 259 + return $this->getProductStocks()
229 ->joinWith('stock'); 260 ->joinWith('stock');
230 } 261 }
231 262
  263 + /**
  264 + * @return ActiveQuery
  265 + */
232 public function getStocks() 266 public function getStocks()
233 { 267 {
234 return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ]) 268 return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ])
235 - ->viaTable(ProductStock::tableName(), [ 'product_variant_id' => 'id' ]); 269 + ->via('productStocks');
236 } 270 }
237 271
238 - public function getFilters() 272 + /**
  273 + * @return ActiveQuery
  274 + */
  275 + public function getOptions()
239 { 276 {
240 return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) 277 return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ])
241 - ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ])  
242 - ->joinWith('taxGroup.lang', true, 'INNER JOIN')  
243 - ->joinWith('lang', true, 'INNER JOIN'); 278 + ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]);
244 } 279 }
245 280
246 - public function getFullname() 281 + /**
  282 + * Get TaxOptions with preloaded TaxGroups for current ProductVariant
  283 + *
  284 + * @return ActiveQuery
  285 + */
  286 + public function getFilters()
247 { 287 {
248 - return empty( $this->product ) ? null : ( $this->product->lang->title . ( empty( $this->lang->title ) ? '' : ' ' . $this->lang->title ) ); 288 + return $this->getOptions()
  289 + ->joinWith('taxGroup.lang')
  290 + ->joinWith('lang');
249 } 291 }
250 292
251 - public function setOptions($values) 293 + /**
  294 + * Get Product title concanated with current ProductVariant title
  295 + *
  296 + * @return string
  297 + */
  298 + public function getFullname(): string
252 { 299 {
253 - $this->options = $values; 300 + return $this->product->lang->title . ' ' . $this->lang->title;
254 } 301 }
255 302
256 - public function getOptions() 303 + /**
  304 + * Set Options to override previous
  305 + *
  306 + * @param int[] $values
  307 + */
  308 + public function setOptions($values)
257 { 309 {
258 - return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ])  
259 - ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]); 310 + $this->options = $values;
260 } 311 }
261 312
262 /** 313 /**
  314 + * Get all TaxGroups for current ProductVariant filled with $customOptions that satisfy current ProductVariant
  315 + *
263 * @return TaxGroup[] 316 * @return TaxGroup[]
264 */ 317 */
265 public function getProperties() 318 public function getProperties()
@@ -282,7 +335,7 @@ @@ -282,7 +335,7 @@
282 * @var TaxGroup $group 335 * @var TaxGroup $group
283 */ 336 */
284 if (!empty( $options[ $group->id ] )) { 337 if (!empty( $options[ $group->id ] )) {
285 - $group->options = $options[ $group->id ]; 338 + $group->customOptions = $options[ $group->id ];
286 $groups[] = $group; 339 $groups[] = $group;
287 } 340 }
288 } 341 }
@@ -290,7 +343,7 @@ @@ -290,7 +343,7 @@
290 } 343 }
291 344
292 /** 345 /**
293 - * @todo Check if needed 346 + * Set stocks to override existing in product_stock table
294 * 347 *
295 * @param mixed $stocks 348 * @param mixed $stocks
296 */ 349 */
@@ -299,29 +352,37 @@ @@ -299,29 +352,37 @@
299 $this->stocks = (array) $stocks; 352 $this->stocks = (array) $stocks;
300 } 353 }
301 354
  355 + /**
  356 + * @return ActiveQuery
  357 + */
302 public function getCategory() 358 public function getCategory()
303 { 359 {
304 return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]) 360 return $this->hasOne(Category::className(), [ 'id' => 'category_id' ])
305 ->viaTable('product_category', [ 'product_id' => 'product_id' ]); 361 ->viaTable('product_category', [ 'product_id' => 'product_id' ]);
306 } 362 }
307 363
  364 + /**
  365 + * @return ActiveQuery
  366 + */
308 public function getCategories() 367 public function getCategories()
309 { 368 {
310 return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) 369 return $this->hasMany(Category::className(), [ 'id' => 'category_id' ])
311 ->viaTable('product_category', [ 'product_id' => 'product_id' ]); 370 ->viaTable('product_category', [ 'product_id' => 'product_id' ]);
312 } 371 }
313 372
314 - public function getTaxGroupsByLevel($level) 373 + /**
  374 + * Get TaxGroups query for current ProductVariant according to level
  375 + * * 0 - Product Tax Groups
  376 + * * 1 - ProductVariant Tax Groups
  377 + *
  378 + * @param int $level
  379 + *
  380 + * @return ActiveQuery
  381 + * @throws InvalidParamException
  382 + */
  383 + public function getTaxGroupsByLevel(int $level = 0)
315 { 384 {
316 - $categories = ArrayHelper::getColumn($this->categories, 'id');  
317 - return TaxGroup::find()  
318 - ->distinct()  
319 - ->innerJoin(  
320 - 'tax_group_to_category',  
321 - 'tax_group_to_category.tax_group_id = tax_group.id'  
322 - )  
323 - ->where([ 'tax_group_to_category.category_id' => $categories ])  
324 - ->where([ 'level' => $level ]); 385 + return $this->product->getTaxGroupsByLevel($level);
325 } 386 }
326 387
327 public function afterSave($insert, $changedAttributes) 388 public function afterSave($insert, $changedAttributes)
common/modules/product/models/ProductVariantSearch.php
@@ -134,20 +134,4 @@ @@ -134,20 +134,4 @@
134 134
135 return $dataProvider; 135 return $dataProvider;
136 } 136 }
137 -  
138 - /**  
139 - * @param string $sku  
140 - *  
141 - * @return ProductVariant|null  
142 - */  
143 - public static function findBySku($sku)  
144 - {  
145 - /**  
146 - * @var ProductVariant|null $result  
147 - */  
148 - $result = ProductVariant::find()  
149 - ->andWhere([ 'sku' => $sku ])  
150 - ->one();  
151 - return $result;  
152 - }  
153 } 137 }
common/modules/product/models/Stock.php
@@ -7,20 +7,22 @@ @@ -7,20 +7,22 @@
7 use yii\db\ActiveQuery; 7 use yii\db\ActiveQuery;
8 use yii\db\ActiveRecord; 8 use yii\db\ActiveRecord;
9 use yii\web\Request; 9 use yii\web\Request;
10 - 10 +
11 /** 11 /**
12 * This is the model class for table "stock". 12 * This is the model class for table "stock".
13 * 13 *
14 - * @property integer $id  
15 - * @property ProductStock[] $productStocks 14 + * @property integer $id
  15 + * @property ProductStock[] $productStocks
  16 + * @property ProductVariant[] $productVariants
  17 + * @property Product[] $products
16 * * From language behavior * 18 * * From language behavior *
17 - * @property StockLang $lang  
18 - * @property StockLang[] $langs  
19 - * @property StockLang $objectLang  
20 - * @property string $ownerKey  
21 - * @property string $langKey  
22 - * @property StockLang[] $modelLangs  
23 - * @property bool $transactionStatus 19 + * @property StockLang $lang
  20 + * @property StockLang[] $langs
  21 + * @property StockLang $objectLang
  22 + * @property string $ownerKey
  23 + * @property string $langKey
  24 + * @property StockLang[] $modelLangs
  25 + * @property bool $transactionStatus
24 * @method string getOwnerKey() 26 * @method string getOwnerKey()
25 * @method void setOwnerKey( string $value ) 27 * @method void setOwnerKey( string $value )
26 * @method string getLangKey() 28 * @method string getLangKey()
@@ -36,7 +38,6 @@ @@ -36,7 +38,6 @@
36 */ 38 */
37 class Stock extends ActiveRecord 39 class Stock extends ActiveRecord
38 { 40 {
39 -  
40 /** 41 /**
41 * @inheritdoc 42 * @inheritdoc
42 */ 43 */
@@ -44,7 +45,7 @@ @@ -44,7 +45,7 @@
44 { 45 {
45 return 'stock'; 46 return 'stock';
46 } 47 }
47 - 48 +
48 public function behaviors() 49 public function behaviors()
49 { 50 {
50 return [ 51 return [
@@ -60,21 +61,30 @@ @@ -60,21 +61,30 @@
60 public function attributeLabels() 61 public function attributeLabels()
61 { 62 {
62 return [ 63 return [
63 - 'id' => Yii::t('product', 'Stock ID'), 64 + 'id' => Yii::t('product', 'Stock ID'),
64 ]; 65 ];
65 } 66 }
66 67
  68 + /**
  69 + * @return \yii\db\ActiveQuery
  70 + */
67 public function getProductStocks() 71 public function getProductStocks()
68 { 72 {
69 return $this->hasMany(ProductStock::className(), [ 'stock_id' => 'id' ]); 73 return $this->hasMany(ProductStock::className(), [ 'stock_id' => 'id' ]);
70 } 74 }
71 75
  76 + /**
  77 + * @return ActiveQuery
  78 + */
72 public function getProductVariants() 79 public function getProductVariants()
73 { 80 {
74 return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) 81 return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ])
75 ->via('productStocks'); 82 ->via('productStocks');
76 } 83 }
77 84
  85 + /**
  86 + * @return ActiveQuery
  87 + */
78 public function getProducts() 88 public function getProducts()
79 { 89 {
80 return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) 90 return $this->hasMany(Product::className(), [ 'id' => 'product_id' ])
common/modules/product/models/StockLang.php
@@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
68 'exist', 68 'exist',
69 'skipOnError' => true, 69 'skipOnError' => true,
70 'targetClass' => Stock::className(), 70 'targetClass' => Stock::className(),
71 - 'targetAttribute' => [ 'id' => 'stock_id' ], 71 + 'targetAttribute' => [ 'stock_id' => 'id' ],
72 ], 72 ],
73 [ 73 [
74 [ 'language_id' ], 74 [ 'language_id' ],
common/modules/product/views/manage/_form.php
1 <?php 1 <?php
2 2
3 use common\modules\language\widgets\LanguageForm; 3 use common\modules\language\widgets\LanguageForm;
  4 + use common\modules\product\models\Brand;
4 use common\modules\product\models\ProductLang; 5 use common\modules\product\models\ProductLang;
5 use common\modules\rubrication\models\TaxGroup; 6 use common\modules\rubrication\models\TaxGroup;
6 use yii\db\ActiveQuery; 7 use yii\db\ActiveQuery;
@@ -41,9 +42,9 @@ @@ -41,9 +42,9 @@
41 <?= $form->field($model, 'brand_id') 42 <?= $form->field($model, 'brand_id')
42 ->dropDownList( 43 ->dropDownList(
43 ArrayHelper::map( 44 ArrayHelper::map(
44 - ProductHelper::getBrands()  
45 - ->with('lang')  
46 - ->all(), 45 + Brand::find()
  46 + ->with('lang')
  47 + ->all(),
47 'id', 48 'id',
48 'lang.title' 49 'lang.title'
49 ), 50 ),
common/modules/product/views/variant/_form.php
@@ -167,10 +167,7 @@ $(&quot;.dynamicform_wrapper&quot;).on(&quot;limitReached&quot;, function(e, item) { @@ -167,10 +167,7 @@ $(&quot;.dynamicform_wrapper&quot;).on(&quot;limitReached&quot;, function(e, item) {
167 ->all(), 167 ->all(),
168 'id', 168 'id',
169 'lang.title' 169 'lang.title'
170 - ),  
171 - [  
172 - 'prompt' => Yii::t('product', 'Unit'),  
173 - ] 170 + )
174 ) 171 )
175 ->label(Yii::t('product', 'Unit')) ?> 172 ->label(Yii::t('product', 'Unit')) ?>
176 173
common/modules/product/widgets/brandsCarouselWidget.php
@@ -18,8 +18,12 @@ @@ -18,8 +18,12 @@
18 $brands = Brand::find() 18 $brands = Brand::find()
19 ->with('lang') 19 ->with('lang')
20 ->all(); 20 ->all();
21 - return $this->render('brandsCarousel', [  
22 - 'brands' => $brands,  
23 - ]); 21 + return $this->render(
  22 + 'brandsCarousel',
  23 + [
  24 + 'brands' => $brands,
  25 + ]
  26 + );
24 } 27 }
25 - }  
26 \ No newline at end of file 28 \ No newline at end of file
  29 + }
  30 +
27 \ No newline at end of file 31 \ No newline at end of file
common/modules/product/widgets/lastProducts.php
@@ -15,10 +15,14 @@ @@ -15,10 +15,14 @@
15 15
16 public function run() 16 public function run()
17 { 17 {
18 - return $this->render('products_block', [  
19 - 'title' => \Yii::t('product', 'ะ’ั‹ ะฝะตะดะฐะฒะฝะพ ะฟั€ะพัะผะฐั‚ั€ะธะฒะฐะปะธ'),  
20 - 'class' => 'last-products',  
21 - 'products' => ProductHelper::getLastProducts(true),  
22 - ]); 18 + return $this->render(
  19 + 'products_block',
  20 + [
  21 + 'title' => \Yii::t('product', 'ะ’ั‹ ะฝะตะดะฐะฒะฝะพ ะฟั€ะพัะผะฐั‚ั€ะธะฒะฐะปะธ'),
  22 + 'class' => 'last-products',
  23 + 'products' => ProductHelper::getLastProducts(true),
  24 + ]
  25 + );
23 } 26 }
24 - }  
25 \ No newline at end of file 27 \ No newline at end of file
  28 + }
  29 +
26 \ No newline at end of file 30 \ No newline at end of file
common/modules/product/widgets/similarProducts.php
@@ -24,14 +24,18 @@ @@ -24,14 +24,18 @@
24 { 24 {
25 $products = ProductHelper::getSimilarProducts($this->product, $this->count); 25 $products = ProductHelper::getSimilarProducts($this->product, $this->count);
26 26
27 - if(!$this->title) { 27 + if (!$this->title) {
28 $this->title = Yii::t('product', 'Similar products'); 28 $this->title = Yii::t('product', 'Similar products');
29 } 29 }
30 30
31 - return $this->render('products_block', [  
32 - 'title' => $this->title,  
33 - 'class' => 'similar-products',  
34 - 'products' => $products,  
35 - ]); 31 + return $this->render(
  32 + 'products_block',
  33 + [
  34 + 'title' => $this->title,
  35 + 'class' => 'similar-products',
  36 + 'products' => $products,
  37 + ]
  38 + );
36 } 39 }
37 - }  
38 \ No newline at end of file 40 \ No newline at end of file
  41 + }
  42 +
39 \ No newline at end of file 43 \ No newline at end of file
common/modules/product/widgets/specialProducts.php
@@ -13,8 +13,6 @@ @@ -13,8 +13,6 @@
13 13
14 public $count = 8; 14 public $count = 8;
15 15
16 - public $sort = 'default';  
17 -  
18 public $title; 16 public $title;
19 17
20 public function init() 18 public function init()
@@ -24,10 +22,10 @@ @@ -24,10 +22,10 @@
24 22
25 public function run() 23 public function run()
26 { 24 {
27 - $products = ProductHelper::getSpecialProducts($this->type, $this->count, $this->sort); 25 + $products = ProductHelper::getSpecialProducts($this->type, $this->count);
28 26
29 - if(!$this->title) {  
30 - switch($this->type) { 27 + if (!$this->title) {
  28 + switch ($this->type) {
31 case 'top': 29 case 'top':
32 $this->title = Yii::t('product', 'Top products'); 30 $this->title = Yii::t('product', 'Top products');
33 break; 31 break;
@@ -40,10 +38,14 @@ @@ -40,10 +38,14 @@
40 } 38 }
41 } 39 }
42 40
43 - return $this->render('products_block', [  
44 - 'title' => $this->title,  
45 - 'class' => $this->type,  
46 - 'products' => $products,  
47 - ]); 41 + return $this->render(
  42 + 'products_block',
  43 + [
  44 + 'title' => $this->title,
  45 + 'class' => $this->type,
  46 + 'products' => $products,
  47 + ]
  48 + );
48 } 49 }
49 - }  
50 \ No newline at end of file 50 \ No newline at end of file
  51 + }
  52 +
51 \ No newline at end of file 53 \ No newline at end of file
common/modules/product/widgets/views/product_smart.php
@@ -69,9 +69,9 @@ @@ -69,9 +69,9 @@
69 69
70 <li><span>ะ‘ั€ะตะฝะด:</span> <?= $product->brand->lang->title ?></li> 70 <li><span>ะ‘ั€ะตะฝะด:</span> <?= $product->brand->lang->title ?></li>
71 71
72 - <?php foreach ($product->getActiveProperties($product->category->id) as $group): ?> 72 + <?php foreach ($product->getProperties() as $group): ?>
73 <li> 73 <li>
74 - <span><?= $group->title ?> <?php foreach ( $group->options as $option ) : ?>&nbsp;</span><?= $option->value ?><?php endforeach ?> 74 + <span><?= $group->lang->title ?> <?php foreach ( $group->customOptions as $option ) : ?>&nbsp;</span><?= $option->lang->value ?><?php endforeach ?>
75 </li> 75 </li>
76 <?php endforeach; ?> 76 <?php endforeach; ?>
77 77
common/modules/rubrication/controllers/TaxGroupController.php
@@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
2 2
3 namespace common\modules\rubrication\controllers; 3 namespace common\modules\rubrication\controllers;
4 4
5 - use common\modules\rubrication\models\TaxOption;  
6 use Yii; 5 use Yii;
7 use common\modules\rubrication\models\TaxGroup; 6 use common\modules\rubrication\models\TaxGroup;
8 use yii\data\ActiveDataProvider; 7 use yii\data\ActiveDataProvider;
@@ -130,16 +129,6 @@ @@ -130,16 +129,6 @@
130 ]); 129 ]);
131 } 130 }
132 131
133 - /*  
134 - * Rebuilp MP-params for group options  
135 - */  
136 - public function actionRebuild($id)  
137 - {  
138 - TaxOption::find()  
139 - ->rebuildMP($id);  
140 - return $this->redirect([ 'index' ]);  
141 - }  
142 -  
143 /** 132 /**
144 * Finds the TaxGroup model based on its primary key value. 133 * Finds the TaxGroup model based on its primary key value.
145 * If the model is not found, a 404 HTTP exception will be thrown. 134 * If the model is not found, a 404 HTTP exception will be thrown.
common/modules/rubrication/controllers/TaxOptionController.php
@@ -113,8 +113,6 @@ @@ -113,8 +113,6 @@
113 $model->generateLangs(); 113 $model->generateLangs();
114 if ($model->load(Yii::$app->request->post())) { 114 if ($model->load(Yii::$app->request->post())) {
115 $model->loadLangs(\Yii::$app->request); 115 $model->loadLangs(\Yii::$app->request);
116 - // TaxOption::find()  
117 - // ->rebuildMP($model->tax_group_id);  
118 if ($model->save() && $model->transactionStatus) { 116 if ($model->save() && $model->transactionStatus) {
119 return $this->redirect( 117 return $this->redirect(
120 [ 118 [
common/modules/rubrication/models/TaxGroup.php
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 use common\modules\language\behaviors\LanguageBehavior; 5 use common\modules\language\behaviors\LanguageBehavior;
6 use common\modules\language\models\Language; 6 use common\modules\language\models\Language;
7 use common\modules\product\models\Category; 7 use common\modules\product\models\Category;
  8 + use yii\base\InvalidValueException;
8 use yii\db\ActiveQuery; 9 use yii\db\ActiveQuery;
9 use yii\db\ActiveRecord; 10 use yii\db\ActiveRecord;
10 use yii\web\Request; 11 use yii\web\Request;
@@ -22,6 +23,8 @@ @@ -22,6 +23,8 @@
22 * @property TaxOption[] $taxOptions 23 * @property TaxOption[] $taxOptions
23 * @property Category[] $categories 24 * @property Category[] $categories
24 * @property TaxOption[] $options 25 * @property TaxOption[] $options
  26 + * @property TaxOption[] $customOptions
  27 + * @property string $alias
25 * * From language behavior * 28 * * From language behavior *
26 * @property TaxGroupLang $lang 29 * @property TaxGroupLang $lang
27 * @property TaxGroupLang[] $langs 30 * @property TaxGroupLang[] $langs
@@ -52,7 +55,7 @@ @@ -52,7 +55,7 @@
52 /** 55 /**
53 * @var TaxOption[] $options 56 * @var TaxOption[] $options
54 */ 57 */
55 - public $options = []; 58 + protected $customOptions = [];
56 59
57 /** 60 /**
58 * @inheritdoc 61 * @inheritdoc
@@ -116,17 +119,28 @@ @@ -116,17 +119,28 @@
116 ]; 119 ];
117 } 120 }
118 121
  122 + /**
  123 + * @return ActiveQuery
  124 + */
119 public function getCategories() 125 public function getCategories()
120 { 126 {
121 return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) 127 return $this->hasMany(Category::className(), [ 'id' => 'category_id' ])
122 ->viaTable('tax_group_to_category', [ 'tax_group_id' => 'id' ]); 128 ->viaTable('tax_group_to_category', [ 'tax_group_id' => 'id' ]);
123 } 129 }
124 130
125 - public function setCategories($values) 131 + /**
  132 + * Set categories to override tax_group_to_category table data
  133 + *
  134 + * @param int[] $values
  135 + */
  136 + public function setCategories(array $values)
126 { 137 {
127 $this->categories = $values; 138 $this->categories = $values;
128 } 139 }
129 140
  141 + /**
  142 + * @inheritdoc
  143 + */
130 public function afterSave($insert, $changedAttributes) 144 public function afterSave($insert, $changedAttributes)
131 { 145 {
132 parent::afterSave($insert, $changedAttributes); 146 parent::afterSave($insert, $changedAttributes);
@@ -141,6 +155,18 @@ @@ -141,6 +155,18 @@
141 } 155 }
142 156
143 /** 157 /**
  158 + * @return ActiveQuery
  159 + */
  160 + public function getTaxOptions()
  161 + {
  162 + return $this->hasMany(TaxOption::className(), [ 'tax_group_id' => 'id' ])
  163 + ->inverseOf('taxGroup');
  164 + }
  165 +
  166 + /**
  167 + * Synonim for getTaxOptions()
  168 + *
  169 + * @see TaxGroup::getTaxOptions()
144 * @return \yii\db\ActiveQuery 170 * @return \yii\db\ActiveQuery
145 */ 171 */
146 public function getOptions() 172 public function getOptions()
@@ -148,12 +174,44 @@ @@ -148,12 +174,44 @@
148 return $this->getTaxOptions(); 174 return $this->getTaxOptions();
149 } 175 }
150 176
151 - public function getTaxOptions() 177 + /**
  178 + * Get customOptins that were filled dynamically.
  179 + * If $fillDefault is true then fill $customOptions with TaxOptions for current TaxGroup
  180 + *
  181 + * @param bool $fillDefault
  182 + *
  183 + * @return TaxOption[]
  184 + */
  185 + public function getCustomOptions(bool $fillDefault = false): array
152 { 186 {
153 - return $this->hasMany(TaxOption::className(), [ 'tax_group_id' => 'id' ])  
154 - ->inverseOf('taxGroup'); 187 + if ($fillDefault && empty( $this->custom_options )) {
  188 + $this->customOptions = $this->getTaxOptions()
  189 + ->with('lang')
  190 + ->all();
  191 + }
  192 + return $this->customOptions;
  193 + }
  194 +
  195 + /**
  196 + * Set customOptions
  197 + *
  198 + * @param TaxOption[] $value
  199 + */
  200 + public function setCustomOptions(array $value)
  201 + {
  202 + foreach ($value as $item) {
  203 + if (!( $item instanceof TaxOption )) {
  204 + throw new InvalidValueException('All elements must be instances of ' . TaxOption::className());
  205 + }
  206 + }
  207 + $this->customOptions = $value;
155 } 208 }
156 209
  210 + /**
  211 + * Get default lang alias
  212 + *
  213 + * @return string
  214 + */
157 public function getAlias() 215 public function getAlias()
158 { 216 {
159 $default_lang = Language::getDefaultLanguage(); 217 $default_lang = Language::getDefaultLanguage();
common/modules/rubrication/models/TaxOption.php
@@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
20 * @property integer $sort 20 * @property integer $sort
21 * @property string $image 21 * @property string $image
22 * @property TaxGroup $taxGroup 22 * @property TaxGroup $taxGroup
  23 + * @property TaxGroup $group
23 * @property Product[] $products 24 * @property Product[] $products
24 * @property ProductVariant[] $productVariants 25 * @property ProductVariant[] $productVariants
25 * * From language behavior * 26 * * From language behavior *
@@ -52,8 +53,6 @@ @@ -52,8 +53,6 @@
52 class TaxOption extends ActiveRecord 53 class TaxOption extends ActiveRecord
53 { 54 {
54 55
55 - public $itemsCount;  
56 -  
57 /** 56 /**
58 * @inheritdoc 57 * @inheritdoc
59 */ 58 */
@@ -122,26 +121,35 @@ @@ -122,26 +121,35 @@
122 /** 121 /**
123 * @return \yii\db\ActiveQuery 122 * @return \yii\db\ActiveQuery
124 */ 123 */
125 - public function getGroup() 124 + public function getTaxGroup()
126 { 125 {
127 - return $this->getTaxGroup(); 126 + return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ])
  127 + ->inverseOf('taxOptions');
128 } 128 }
129 129
130 /** 130 /**
  131 + * Synonim for TaxOption::getTaxGroup()
  132 + *
  133 + * @see TaxOption::getTaxGroup()
131 * @return \yii\db\ActiveQuery 134 * @return \yii\db\ActiveQuery
132 */ 135 */
133 - public function getTaxGroup() 136 + public function getGroup()
134 { 137 {
135 - return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ])  
136 - ->inverseOf('taxOptions'); 138 + return $this->getTaxGroup();
137 } 139 }
138 140
  141 + /**
  142 + * @return ActiveQuery
  143 + */
139 public function getProducts() 144 public function getProducts()
140 { 145 {
141 return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) 146 return $this->hasMany(Product::className(), [ 'id' => 'product_id' ])
142 ->viaTable('product_option', [ 'option_id' => 'id' ]); 147 ->viaTable('product_option', [ 'option_id' => 'id' ]);
143 } 148 }
144 149
  150 + /**
  151 + * @return ActiveQuery
  152 + */
145 public function getProductVariants() 153 public function getProductVariants()
146 { 154 {
147 return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) 155 return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ])
common/modules/rubrication/models/TaxOptionQuery.php deleted
1 -<?php  
2 -  
3 - namespace common\modules\rubrication\models;  
4 -  
5 - use common\components\artboxtree\ArtboxTreeQueryTrait;  
6 - use yii\db\ActiveQuery;  
7 -  
8 - /**  
9 - * This is the ActiveQuery class for [[TaxOption]].  
10 - * @see TaxOption  
11 - */  
12 - class TaxOptionQuery extends ActiveQuery  
13 - {  
14 -  
15 - use ArtboxTreeQueryTrait;  
16 -  
17 - /**  
18 - * @inheritdoc  
19 - * @return TaxOption[]|array  
20 - */  
21 - public function all($db = NULL)  
22 - {  
23 - return parent::all($db);  
24 - }  
25 -  
26 - /**  
27 - * @inheritdoc  
28 - * @return TaxOption|array|null  
29 - */  
30 - public function one($db = NULL)  
31 - {  
32 - return parent::one($db);  
33 - }  
34 - }