Commit 5aa7418e91ad61ac033b90ef0263d30c2aec5160
1 parent
b15c889e
Base-product#3 functional
Showing
17 changed files
with
463 additions
and
22 deletions
 
Show diff stats
common/config/main.php
| ... | ... | @@ -100,6 +100,27 @@ return [ | 
| 100 | 100 | 'model' => '\common\modules\product\models\ProductCategory', | 
| 101 | 101 | ] | 
| 102 | 102 | ], | 
| 103 | + 'product_option' => [ | |
| 104 | + 'name' => Yii::t('product', 'Properties'), | |
| 105 | + 'field' => 'options', | |
| 106 | + 'entity1' => [ | |
| 107 | + 'model' => '\common\modules\product\models\Product', | |
| 108 | + 'label' => 'Product', | |
| 109 | + 'listField' => 'fullname', | |
| 110 | + 'key' => 'product_id', | |
| 111 | + 'linked_key' => 'product_id', | |
| 112 | + ], | |
| 113 | + 'entity2' => [ | |
| 114 | + 'model' => '\common\modules\rubrication\models\TaxOption', | |
| 115 | + 'label' => 'Option', | |
| 116 | + 'listField' => 'ValueeRenderFlash', | |
| 117 | + 'key' => 'tax_option_id', | |
| 118 | + 'linked_key' => 'option_id', | |
| 119 | + ], | |
| 120 | + 'via' => [ | |
| 121 | + 'model' => 'common\modules\product\models\ProductOption', | |
| 122 | + ] | |
| 123 | + ], | |
| 103 | 124 | 'tax_group_to_category' => [ | 
| 104 | 125 | 'name' => Yii::t('product', 'Характеристики по категориям'), | 
| 105 | 126 | 'field' => 'group_to_category', | ... | ... | 
common/modules/product/controllers/ManageController.php
| ... | ... | @@ -33,6 +33,24 @@ class ManageController extends Controller | 
| 33 | 33 | } | 
| 34 | 34 | |
| 35 | 35 | public function actionTest() { | 
| 36 | + return; | |
| 37 | + foreach(Product::find()->all() as $product) { | |
| 38 | + if (!$product->variant) { | |
| 39 | + $product->save(); | |
| 40 | + $variantModel = new ProductVariant(); | |
| 41 | + $variantModel->product_id = $product->product_id; | |
| 42 | + $variantModel->name = 'test-'. uniqid(); | |
| 43 | + $variantModel->sku = $variantModel->name; | |
| 44 | + $variantModel->price = rand(5, 200000); | |
| 45 | + $variantModel->price_old = rand(0, 5) > 3 ? $variantModel->price* (1+rand(0, 10) / 10) : $variantModel->price; | |
| 46 | + $variantModel->product_unit_id = rand(1, 5); | |
| 47 | + $variantModel->stock = rand(0, 50); | |
| 48 | + $variantModel->save(); | |
| 49 | + } | |
| 50 | + } | |
| 51 | + | |
| 52 | + return; | |
| 53 | + | |
| 36 | 54 | $categories = Category::find()->where(['depth' => 2])->all(); | 
| 37 | 55 | $cats_ids = []; | 
| 38 | 56 | foreach($categories as $cat) { | 
| ... | ... | @@ -122,8 +140,11 @@ class ManageController extends Controller | 
| 122 | 140 | if ($model->load(Yii::$app->request->post()) && $model->save()) { | 
| 123 | 141 | return $this->redirect(['view', 'id' => $model->product_id]); | 
| 124 | 142 | } else { | 
| 143 | + $groups = $model->category->getTaxGroups(); | |
| 144 | + | |
| 125 | 145 | return $this->render('update', [ | 
| 126 | 146 | 'model' => $model, | 
| 147 | + 'groups' => $groups, | |
| 127 | 148 | ]); | 
| 128 | 149 | } | 
| 129 | 150 | } | ... | ... | 
common/modules/product/models/Product.php
| ... | ... | @@ -2,6 +2,7 @@ | 
| 2 | 2 | |
| 3 | 3 | namespace common\modules\product\models; | 
| 4 | 4 | |
| 5 | +use common\behaviors\Slug; | |
| 5 | 6 | use common\modules\rubrication\models\TaxOption; | 
| 6 | 7 | use Yii; | 
| 7 | 8 | use common\modules\relation\relationBehavior; | 
| ... | ... | @@ -23,6 +24,9 @@ class Product extends \yii\db\ActiveRecord | 
| 23 | 24 | { | 
| 24 | 25 | /** @var array $variants */ | 
| 25 | 26 | public $_variants = []; | 
| 27 | + | |
| 28 | + /** @var array $_images */ | |
| 29 | + public $imagesUpload = []; | |
| 26 | 30 | /** | 
| 27 | 31 | * @inheritdoc | 
| 28 | 32 | */ | 
| ... | ... | @@ -32,9 +36,16 @@ class Product extends \yii\db\ActiveRecord | 
| 32 | 36 | [ | 
| 33 | 37 | 'class' => relationBehavior::className(), | 
| 34 | 38 | 'relations' => [ | 
| 35 | - 'product_categories' => 'entity1' // Product category | |
| 39 | + 'product_categories' => 'entity1', // Product category | |
| 40 | + 'product_option' => 'entity1' // Product category | |
| 36 | 41 | ] | 
| 37 | 42 | ], | 
| 43 | + [ | |
| 44 | + 'class' => Slug::className(), | |
| 45 | + 'in_attribute' => 'name', | |
| 46 | + 'out_attribute' => 'alias', | |
| 47 | + 'translit' => true | |
| 48 | + ] | |
| 38 | 49 | ]; | 
| 39 | 50 | } | 
| 40 | 51 | |
| ... | ... | @@ -54,7 +65,10 @@ class Product extends \yii\db\ActiveRecord | 
| 54 | 65 | return [ | 
| 55 | 66 | [['brand_id'], 'integer'], | 
| 56 | 67 | [['name'], 'string', 'max' => 150], | 
| 57 | - [['categories', 'variants'], 'safe'], | |
| 68 | + [['alias'], 'string', 'max' => 250], | |
| 69 | + [['categories', 'variants', 'options'], 'safe'], | |
| 70 | + [['imagesUpload'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg, gif'], | |
| 71 | + [['description', 'video'], 'safe'], | |
| 58 | 72 | // [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']], | 
| 59 | 73 | ]; | 
| 60 | 74 | } | 
| ... | ... | @@ -70,6 +84,8 @@ class Product extends \yii\db\ActiveRecord | 
| 70 | 84 | 'brand_id' => Yii::t('product', 'Brand'), | 
| 71 | 85 | 'categories' => Yii::t('product', 'Categories'), // relation behavior field | 
| 72 | 86 | 'category' => Yii::t('product', 'Category'), // relation behavior field | 
| 87 | + 'image' => Yii::t('product', 'Image'), | |
| 88 | + 'images' => Yii::t('product', 'Images'), | |
| 73 | 89 | ]; | 
| 74 | 90 | } | 
| 75 | 91 | |
| ... | ... | @@ -97,6 +113,10 @@ class Product extends \yii\db\ActiveRecord | 
| 97 | 113 | return $this->hasMany(ProductImage::className(), ['product_id' => 'product_id']); | 
| 98 | 114 | } | 
| 99 | 115 | |
| 116 | + public function setImages($images) { | |
| 117 | + $this->_images = $images; | |
| 118 | + } | |
| 119 | + | |
| 100 | 120 | /** | 
| 101 | 121 | * @return \yii\db\ActiveQuery | 
| 102 | 122 | */ | 
| ... | ... | @@ -146,6 +166,10 @@ class Product extends \yii\db\ActiveRecord | 
| 146 | 166 | return $categories->one(); | 
| 147 | 167 | } | 
| 148 | 168 | |
| 169 | + public function getOptions() { | |
| 170 | + return $this->getRelations('product_option'); | |
| 171 | + } | |
| 172 | + | |
| 149 | 173 | /** | 
| 150 | 174 | * @inheritdoc | 
| 151 | 175 | * @return ProductQuery the active query used by this AR class. | 
| ... | ... | @@ -159,11 +183,18 @@ class Product extends \yii\db\ActiveRecord | 
| 159 | 183 | { | 
| 160 | 184 | parent::afterSave($insert, $changedAttributes); | 
| 161 | 185 | |
| 186 | + foreach($this->imagesUpload as $image) { | |
| 187 | + $image->saveAs('/images/items/' . $image->baseName .'_'. uniqid() . '.' . $image->extension); | |
| 188 | + } | |
| 189 | + | |
| 162 | 190 | $todel = []; | 
| 163 | 191 | foreach ($this->variants ? : [] as $_variant) { | 
| 164 | 192 | $todel[$_variant->product_variant_id] = $_variant->product_variant_id; | 
| 165 | 193 | } | 
| 166 | 194 | foreach ($this->_variants as $_variant) { | 
| 195 | + if (!is_array($_variant)) { | |
| 196 | + return; | |
| 197 | + } | |
| 167 | 198 | if (!empty($_variant['product_variant_id'])) { | 
| 168 | 199 | unset($todel[$_variant['product_variant_id']]); | 
| 169 | 200 | $model = ProductVariant::findOne($_variant['product_variant_id']); | ... | ... | 
| 1 | +<?php | |
| 2 | + | |
| 3 | +namespace common\modules\product\models; | |
| 4 | + | |
| 5 | +use Yii; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * This is the model class for table "product_option". | |
| 9 | + * | |
| 10 | + * @property integer $product_id | |
| 11 | + * @property integer $option_id | |
| 12 | + * | |
| 13 | + * @property Product $product | |
| 14 | + * @property TaxOption $option | |
| 15 | + */ | |
| 16 | +class ProductOption extends \yii\db\ActiveRecord | |
| 17 | +{ | |
| 18 | + /** | |
| 19 | + * @inheritdoc | |
| 20 | + */ | |
| 21 | + public static function tableName() | |
| 22 | + { | |
| 23 | + return 'product_option'; | |
| 24 | + } | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * @inheritdoc | |
| 28 | + */ | |
| 29 | + public function rules() | |
| 30 | + { | |
| 31 | + return [ | |
| 32 | + [['product_id', 'option_id'], 'required'], | |
| 33 | + [['product_id', 'option_id'], 'integer'], | |
| 34 | + [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']], | |
| 35 | + [['option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['option_id' => 'tax_option_id']], | |
| 36 | + ]; | |
| 37 | + } | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * @inheritdoc | |
| 41 | + */ | |
| 42 | + public function attributeLabels() | |
| 43 | + { | |
| 44 | + return [ | |
| 45 | + 'product_id' => Yii::t('product', 'Product ID'), | |
| 46 | + 'option_id' => Yii::t('product', 'Option ID'), | |
| 47 | + ]; | |
| 48 | + } | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * @return \yii\db\ActiveQuery | |
| 52 | + */ | |
| 53 | + public function getProduct() | |
| 54 | + { | |
| 55 | + return $this->hasOne(Product::className(), ['product_id' => 'product_id']); | |
| 56 | + } | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * @return \yii\db\ActiveQuery | |
| 60 | + */ | |
| 61 | + public function getOption() | |
| 62 | + { | |
| 63 | + return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'option_id']); | |
| 64 | + } | |
| 65 | +} | ... | ... | 
common/modules/product/models/ProductSearch.php
| ... | ... | @@ -6,6 +6,7 @@ use Yii; | 
| 6 | 6 | use yii\base\Model; | 
| 7 | 7 | use yii\data\ActiveDataProvider; | 
| 8 | 8 | use common\modules\product\models\Product; | 
| 9 | +use yii\web\NotFoundHttpException; | |
| 9 | 10 | |
| 10 | 11 | /** | 
| 11 | 12 | * ProductSearch represents the model behind the search form about `common\modules\product\models\Product`. | 
| ... | ... | @@ -70,7 +71,7 @@ class ProductSearch extends Product | 
| 70 | 71 | |
| 71 | 72 | public static function findByAlias($alias) { | 
| 72 | 73 | /** @var ProductQuery $query */ | 
| 73 | - $query = Category::find(); | |
| 74 | + $query = Product::find(); | |
| 74 | 75 | $query->byAlias($alias); | 
| 75 | 76 | if (($model = $query->one()) !== null) { | 
| 76 | 77 | return $model; | ... | ... | 
common/modules/product/views/manage/_form.php
| ... | ... | @@ -20,6 +20,9 @@ use unclead\widgets\MultipleInputColumn; | 
| 20 | 20 | |
| 21 | 21 | <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?> | 
| 22 | 22 | |
| 23 | + <?= $form->field($model, 'description')->widget(\mihaildev\ckeditor\CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?> | |
| 24 | + <?= $form->field($model, 'video')->textarea()->label('Video embeded'); ?> | |
| 25 | + | |
| 23 | 26 | <?= $form->field($model, 'brand_id')->dropDownList( | 
| 24 | 27 | ArrayHelper::map(ProductHelper::getBrands()->all(), 'brand_id', 'name'), | 
| 25 | 28 | [ | 
| ... | ... | @@ -30,21 +33,18 @@ use unclead\widgets\MultipleInputColumn; | 
| 30 | 33 | <?= $form->field($model, 'categories')->dropDownList( | 
| 31 | 34 | ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'category_id', 'name'), | 
| 32 | 35 | [ | 
| 33 | -// 'prompt' => Yii::t('product', 'Select category'), | |
| 34 | 36 | 'multiple' => true | 
| 35 | 37 | ] | 
| 36 | 38 | ) ?> | 
| 37 | 39 | |
| 38 | - <?php /*= $form->field($model, 'images[]')->widget(FileInput::classname(), [ | |
| 40 | + | |
| 41 | + | |
| 42 | + <?php /*= $form->field($model, 'imagesUpload[]')->widget(FileInput::classname(), [ | |
| 39 | 43 | 'options' => [ | 
| 40 | 44 | 'accept' => 'image/*', | 
| 41 | 45 | 'multiple' => true, | 
| 42 | 46 | ], | 
| 43 | - 'pluginOptions' => [ | |
| 44 | -// 'uploadUrl' => \yii\helpers\Url::to(['/site/file-upload']), | |
| 45 | - ] | |
| 46 | - ]); | |
| 47 | - */?> | |
| 47 | + ]);*/?> | |
| 48 | 48 | |
| 49 | 49 | <?= $form->field($model, 'variants')->widget(MultipleInput::className(), [ | 
| 50 | 50 | 'columns' => [ | 
| ... | ... | @@ -90,6 +90,16 @@ use unclead\widgets\MultipleInputColumn; | 
| 90 | 90 | ]); | 
| 91 | 91 | ?> | 
| 92 | 92 | |
| 93 | + <?php foreach($groups->all() as $group) :?> | |
| 94 | + <?= $form->field($model, 'options')->checkboxList( | |
| 95 | + ArrayHelper::map($group->options, 'tax_option_id', 'ValueRenderFlash'), | |
| 96 | + [ | |
| 97 | + 'multiple' => true, | |
| 98 | + 'unselect' => null, | |
| 99 | + ] | |
| 100 | + )->label($group->name);?> | |
| 101 | + <?php endforeach?> | |
| 102 | + | |
| 93 | 103 | <div class="form-group"> | 
| 94 | 104 | <?= Html::submitButton($model->isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> | 
| 95 | 105 | </div> | ... | ... | 
common/modules/product/views/manage/update.php
common/modules/rubrication/helpers/RubricationHelper.php
| ... | ... | @@ -40,4 +40,12 @@ class RubricationHelper { | 
| 40 | 40 | return $module->types; | 
| 41 | 41 | |
| 42 | 42 | } | 
| 43 | + | |
| 44 | + public function checkboxList($items, $options = []) | |
| 45 | + { | |
| 46 | + $this->adjustLabelFor($options); | |
| 47 | + $this->parts['{input}'] = Html::activeCheckboxList($this->model, $this->attribute, $items, $options); | |
| 48 | + | |
| 49 | + return $this; | |
| 50 | + } | |
| 43 | 51 | } | 
| 44 | 52 | \ No newline at end of file | ... | ... | 
common/modules/rubrication/models/TaxGroup.php
console/migrations/m160304_065108_product.php
| ... | ... | @@ -69,7 +69,10 @@ class m160304_065108_product extends Migration | 
| 69 | 69 | $this->createTable('{{%product}}', [ | 
| 70 | 70 | 'product_id' => $this->primaryKey(), | 
| 71 | 71 | 'name' => $this->string(255)->notNull(), | 
| 72 | + 'alias' => $this->string(255), | |
| 72 | 73 | 'brand_id' => $this->integer(), | 
| 74 | + 'description' => $this->text(), | |
| 75 | + 'video' => $this->text(), | |
| 73 | 76 | ], $tableOptions); | 
| 74 | 77 | |
| 75 | 78 | $this->addForeignKey('fki_product_id', 'product_category', 'product_id', 'product', 'product_id', 'NO ACTION', 'NO ACTION'); | ... | ... | 
console/migrations/m160324_075409_product_option.php
0 → 100644
| 1 | +<?php | |
| 2 | + | |
| 3 | +use yii\db\Migration; | |
| 4 | + | |
| 5 | +class m160324_075409_product_option extends Migration | |
| 6 | +{ | |
| 7 | + public function up() | |
| 8 | + { | |
| 9 | + $tableOptions = null; | |
| 10 | + if ($this->db->driverName === 'mysql') { | |
| 11 | + // Only for MySQL | |
| 12 | + $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; | |
| 13 | + | |
| 14 | + // @todo https://habrahabr.ru/post/138947/ | |
| 15 | + } elseif ($this->db->driverName === 'pgsql') { | |
| 16 | + // Only for PostgreSQL | |
| 17 | + // @todo use intarray field for tax_options | |
| 18 | + } | |
| 19 | + | |
| 20 | + $this->createTable('{{%product_option}}', [ | |
| 21 | + 'product_id' => $this->integer()->notNull(), | |
| 22 | + 'option_id' => $this->integer()->notNull(), | |
| 23 | + ], $tableOptions); | |
| 24 | + $this->addPrimaryKey('product_option_pkey', 'product_option', ['product_id', 'option_id']); | |
| 25 | + $this->addForeignKey('product_option_product_fkey', 'product_option', 'product_id', 'product', 'product_id', 'NO ACTION', 'NO ACTION'); | |
| 26 | + $this->addForeignKey('product_option_option_fkey', 'product_option', 'option_id', 'tax_option', 'tax_option_id', 'NO ACTION', 'NO ACTION'); | |
| 27 | + } | |
| 28 | + | |
| 29 | + public function down() | |
| 30 | + { | |
| 31 | + $this->dropTable('{{%product_option}}'); | |
| 32 | + } | |
| 33 | + | |
| 34 | + /* | |
| 35 | + // Use safeUp/safeDown to run migration code within a transaction | |
| 36 | + public function safeUp() | |
| 37 | + { | |
| 38 | + } | |
| 39 | + | |
| 40 | + public function safeDown() | |
| 41 | + { | |
| 42 | + } | |
| 43 | + */ | |
| 44 | +} | ... | ... | 
frontend/controllers/CatalogController.php
| ... | ... | @@ -7,8 +7,11 @@ use common\modules\product\models\Category; | 
| 7 | 7 | use common\modules\product\models\CategorySearch; | 
| 8 | 8 | use common\modules\product\models\Product; | 
| 9 | 9 | use common\modules\product\models\ProductCategory; | 
| 10 | +use common\modules\product\models\ProductOption; | |
| 10 | 11 | use common\modules\product\models\ProductSearch; | 
| 11 | 12 | use common\modules\product\models\ProductVariant; | 
| 13 | +use common\modules\rubrication\models\TaxGroup; | |
| 14 | +use common\modules\rubrication\models\TaxOption; | |
| 12 | 15 | use yii\data\ActiveDataProvider; | 
| 13 | 16 | use yii\data\Pagination; | 
| 14 | 17 | use yii\data\Sort; | 
| ... | ... | @@ -56,10 +59,34 @@ class CatalogController extends \yii\web\Controller | 
| 56 | 59 | ]); | 
| 57 | 60 | $all_count = $query->count(); | 
| 58 | 61 | |
| 62 | + $brandsQuery = Brand::find() | |
| 63 | + ->innerJoinWith('products') | |
| 64 | + ->innerJoin(ProductCategory::tableName(), ProductCategory::tableName() .'.product_id='. Product::tableName() .'.product_id') | |
| 65 | + ->where([ | |
| 66 | + ProductCategory::tableName() .'.category_id' => $category->category_id | |
| 67 | + ]) | |
| 68 | + ->groupBy(Brand::tableName() .'.brand_id'); | |
| 69 | + $brands = $brandsQuery->all(); | |
| 70 | + $brands_count = $brandsQuery->count(); | |
| 71 | + | |
| 72 | + $optionsQuery = TaxOption::find() | |
| 73 | +// ->select([TaxOption::tableName() .'.tax_option_id', TaxOption::tableName() .'.alias']) | |
| 74 | + ->innerJoin(ProductOption::tableName(), ProductOption::tableName() .'.option_id='. TaxOption::tableName() .'.tax_option_id') | |
| 75 | + ->innerJoin(ProductCategory::tableName(), ProductCategory::tableName() .'.product_id='. ProductOption::tableName() .'.product_id') | |
| 76 | + ->where([ | |
| 77 | + ProductCategory::tableName() .'.category_id' => $category->category_id | |
| 78 | + ]) | |
| 79 | + ->groupBy(TaxOption::tableName() .'.tax_option_id'); | |
| 80 | + $all_options = []; | |
| 81 | + foreach($optionsQuery->all() as $_option) { | |
| 82 | + $all_options[] = $_option; | |
| 83 | + } | |
| 84 | + | |
| 59 | 85 | $priceQuery = clone $query; | 
| 60 | 86 | $priceMin = $priceMinCurr = $priceQuery->min(ProductVariant::tableName() .'.price'); | 
| 61 | 87 | $priceMax = $priceMaxCurr = $priceQuery->max(ProductVariant::tableName() .'.price'); | 
| 62 | 88 | |
| 89 | + // Prices | |
| 63 | 90 | if (($price_interval = \Yii::$app->request->get('price_interval')) != false) { | 
| 64 | 91 | $price_interval = explode(';', $price_interval); | 
| 65 | 92 | $price_interval = [ | 
| ... | ... | @@ -75,21 +102,40 @@ class CatalogController extends \yii\web\Controller | 
| 75 | 102 | $priceMaxCurr = $price_interval[1]; | 
| 76 | 103 | } | 
| 77 | 104 | } | 
| 105 | + | |
| 106 | + $groups = []; | |
| 107 | + foreach($category->getTaxGroups()->all() as $_group) { | |
| 108 | + $groups[$_group->tax_group_id] = $_group; | |
| 109 | + } | |
| 110 | + foreach ($all_options as $option) { | |
| 111 | + $groups[$option->tax_group_id]->_options[] = $option; | |
| 112 | + } | |
| 113 | + foreach($groups as $i => $group) { | |
| 114 | + if (empty($group->_options)) | |
| 115 | + unset($groups[$i]); | |
| 116 | + } | |
| 117 | + | |
| 118 | + // Options | |
| 119 | + if (($options = \Yii::$app->request->get('option')) != false) { | |
| 120 | + $query->innerJoin(ProductOption::tableName(), ProductOption::tableName() .'.product_id='. Product::tableName() .'.product_id'); | |
| 121 | + $query->innerJoin(TaxOption::tableName(), TaxOption::tableName() .'.tax_option_id='. ProductOption::tableName() .'.option_id'); | |
| 122 | + foreach($options as $group_alias => $option_alias) { | |
| 123 | + $group = TaxGroup::find()->where(['like', 'alias', $group_alias])->one(); | |
| 124 | + if (!$group) { | |
| 125 | + continue; | |
| 126 | + } | |
| 127 | + $query->andWhere([TaxOption::tableName() .'.tax_group_id' => $group->tax_group_id, TaxOption::tableName() .'.alias' => $option_alias]); | |
| 128 | + } | |
| 129 | + } | |
| 130 | + | |
| 78 | 131 | $count = $query->count(); | 
| 132 | + | |
| 79 | 133 | $pages = new Pagination(['totalCount' => $count, 'pageSize' => $per_page]); | 
| 80 | 134 | $query->offset($pages->offset) | 
| 81 | 135 | ->orderBy($sort->orders) | 
| 82 | 136 | ->limit($pages->limit); | 
| 83 | 137 | $products = $query->all(); | 
| 84 | 138 | |
| 85 | - $brandsQuery = Brand::find()->innerJoinWith('products')->innerJoin(ProductCategory::tableName(), ProductCategory::tableName() .'.product_id='. Product::tableName() .'.product_id')->where([ | |
| 86 | - ProductCategory::tableName() .'.category_id' => $category->category_id | |
| 87 | - ])->groupBy(Brand::tableName() .'.brand_id'); | |
| 88 | - $brands = $brandsQuery->all(); | |
| 89 | - $brands_count = $brandsQuery->count(); | |
| 90 | - | |
| 91 | - $groups = $category->getTaxGroups()->all(); | |
| 92 | - | |
| 93 | 139 | return $this->render( | 
| 94 | 140 | 'products', | 
| 95 | 141 | [ | 
| ... | ... | @@ -107,6 +153,7 @@ class CatalogController extends \yii\web\Controller | 
| 107 | 153 | 'brands' => $brands, | 
| 108 | 154 | 'brands_count' => $brands_count, | 
| 109 | 155 | 'groups' => $groups, | 
| 156 | + 'options' => $options, | |
| 110 | 157 | ] | 
| 111 | 158 | ); | 
| 112 | 159 | } | 
| ... | ... | @@ -118,7 +165,22 @@ class CatalogController extends \yii\web\Controller | 
| 118 | 165 | if (empty($product->product_id)) { | 
| 119 | 166 | throw new HttpException(404 ,'Page not found'); | 
| 120 | 167 | } | 
| 121 | - return $this->render('product'); | |
| 168 | + $groups = []; | |
| 169 | + foreach($product->category->getTaxGroups()->all() as $_group) { | |
| 170 | + $groups[$_group->tax_group_id] = $_group; | |
| 171 | + } | |
| 172 | + foreach ($product->options as $option) { | |
| 173 | + $groups[$option->tax_group_id]->_options[] = $option; | |
| 174 | + } | |
| 175 | + foreach($groups as $i => $group) { | |
| 176 | + if (empty($group->_options)) | |
| 177 | + unset($groups[$i]); | |
| 178 | + } | |
| 179 | + | |
| 180 | + return $this->render('product', [ | |
| 181 | + 'product' => $product, | |
| 182 | + 'properties' => $groups, | |
| 183 | + ]); | |
| 122 | 184 | } | 
| 123 | 185 | |
| 124 | 186 | public function actionBrands() | ... | ... | 
| 1 | +<?php | |
| 2 | +/** @var $this \yii\web\View */ | |
| 3 | +/** @var $dataProvider \yii\data\ActiveDataProvider */ | |
| 4 | + | |
| 5 | +$this->title = $product->name; | |
| 6 | +foreach($product->category->getParents()->all() as $parent) { | |
| 7 | + $this->params['breadcrumbs'][] = ['label' => $parent->name, 'url' => ['catalog/category', 'alias' => $parent->alias]]; | |
| 8 | +} | |
| 9 | +$this->params['breadcrumbs'][] = ['label' => $product->category->name, 'url' => ['catalog/category', 'alias' => $product->category->alias]]; | |
| 10 | +$this->params['breadcrumbs'][] = $product->name .' #'. $product->variant->sku; | |
| 11 | +?> | |
| 12 | +<h1 class="open_card_item_title"><?= $product->name .' '. $product->variant->name?></h1> | |
| 13 | + | |
| 14 | +<div class="item_3_blocks_wrap"> <!-- flex container --> | |
| 15 | + <div class="item_img_block"> <!-- блок с фотографиями --> | |
| 16 | + <div class="main_img"> | |
| 17 | + <?php if (empty($product->image)) :?> | |
| 18 | + <img src="/images/no_photo_big.png" alt="<?= $product->name?>"> | |
| 19 | + <?php else :?> | |
| 20 | + <img src="/images/<?= $product->image->image?>" alt="<?= $product->image->alt ? $product->image->alt : $product->name?>"> | |
| 21 | + <?php endif?> | |
| 22 | + <!--<span class="new">НОВИНКА</span> | |
| 23 | + <span class="top">ТОП</span>--> | |
| 24 | + </div> | |
| 25 | + <?php if (!empty($product->images)) :?> | |
| 26 | + <div class="main_img_slide"> | |
| 27 | + <?php foreach($product->images as $image) :?> | |
| 28 | + <div class="small_img_block active"> | |
| 29 | + <img src="/images/<?= $image->image?>" alt="<?= $image->alt ? $image->alt : $product->name?>"> | |
| 30 | + </div> | |
| 31 | + <?php endforeach?> | |
| 32 | + | |
| 33 | + <img class="slider_arrow_right" src="/images/slider_right.png" alt=""> | |
| 34 | + <img class="slider_arrow_left" src="/images/slider_left.png" alt=""> | |
| 35 | + </div> | |
| 36 | + <?php endif?> | |
| 37 | + | |
| 38 | + </div> <!-- конец блока с фотографиями --> | |
| 39 | + | |
| 40 | + | |
| 41 | + <div class="busket_block"> <!-- блок с счетчиком и кнопкой добавить в корзину --> | |
| 42 | + <div class="top_code"> | |
| 43 | + <span class="code">Код: <?= $product->variant->sku?></span> | |
| 44 | + <span class="have"><img src="/images/ok_icon_green.png" alt=""><?= $product->stock !== 0 ? ' есть в наличии' : ' нет в наличии'?></span> | |
| 45 | + </div> | |
| 46 | + | |
| 47 | + <div class="grey_bg"> | |
| 48 | + <div class="counter"> | |
| 49 | + <div class="price"><?= $product->variant->price?></div> | |
| 50 | + <div class="sign">грн.</div> | |
| 51 | + <div class="count_block"> | |
| 52 | + <div class="count_number">1</div> | |
| 53 | + <div class="count_buttons"> | |
| 54 | + <div class="button_plus">+</div> | |
| 55 | + <div class="button_minus">-</div> | |
| 56 | + </div> | |
| 57 | + </div> | |
| 58 | + </div> | |
| 59 | + | |
| 60 | + <div class="in_cart_btn"> | |
| 61 | + <a href="#"> | |
| 62 | + <button class="cart_btn" data-id="<?= $product->variant->product_variant_id?>"> в корзину <img src="/images/ico_basket_white.png" alt=""></button> | |
| 63 | + </a> | |
| 64 | + </div> | |
| 65 | + | |
| 66 | + <!--<div class="to_compare_link"> | |
| 67 | + <img src="/images/ico_scales.png" alt=""> | |
| 68 | + <a href="#" class="add_to_compare">добавить к сравнению</a> | |
| 69 | + </div>--> | |
| 70 | + </div> | |
| 71 | + <div class="quick_order"> | |
| 72 | + <form action=""> | |
| 73 | + <span class="text">БЫСТРЫЙ ЗАКАЗ</span> | |
| 74 | + <input type="text" class="quick_order_phone" name="quick_order_phone" placeholder="(0XX) XXX-XX-XX"> | |
| 75 | + <button type="submit">заказать</button> | |
| 76 | + </form> | |
| 77 | + </div> | |
| 78 | + | |
| 79 | + <div class="delivery"> | |
| 80 | + <p> | |
| 81 | + Доставка товара на следующий день после выставления счета. Мы доставим “День в <br> день” — уточните это у менеджера. | |
| 82 | + </p> | |
| 83 | + <a href="#">Подробно о доставке</a> | |
| 84 | + </div> | |
| 85 | + | |
| 86 | + </div><!-- конец блока с счетчиком и кнопкой добавить в корзину --> | |
| 87 | + | |
| 88 | + <div class="character_block"> <!-- блок с характеристиками --> | |
| 89 | + <?php if (!empty($properties)) :?> | |
| 90 | + <h3>Характеристики</h3> | |
| 91 | + <ul> | |
| 92 | + <?php foreach($properties as $group) :?> | |
| 93 | + <li> | |
| 94 | + <div class="each"> | |
| 95 | + <div class="title"><?= $group->name?></div> | |
| 96 | + <div class="tech"> | |
| 97 | + <?php foreach($group->_options as $option) :?> <?= $option->ValueRenderHTML?><?php endforeach?> | |
| 98 | + </div> | |
| 99 | + </div> | |
| 100 | + </li> | |
| 101 | + <?php endforeach?> | |
| 102 | + </ul> | |
| 103 | + <?php endif?> | |
| 104 | + | |
| 105 | + <!--<div class="tech_links"> | |
| 106 | + <a href="#">Описание</a> | |
| 107 | + <a href="#">Видео</a> | |
| 108 | + <a href="#">Отзывы(12)</a> | |
| 109 | + </div>--> | |
| 110 | + | |
| 111 | + </div><!-- закрытие блока с характеристиками --> | |
| 112 | + | |
| 113 | + <div class="tabs_block"> <!-- Табы с описанием видео и отзывами --> | |
| 114 | + <div class="ionTabs" id="tabs_1" data-name="Tabs_Group_name"> | |
| 115 | + <ul class="ionTabs__head"> | |
| 116 | + <?php if (!empty($properties)) :?> | |
| 117 | + <li class="ionTabs__tab" data-target="Tab_1_name">Характеристики</li> | |
| 118 | + <?php endif?> | |
| 119 | + <?php if (!empty($product->description)) :?> | |
| 120 | + <li class="ionTabs__tab" data-target="Tab_2_name">Описание</li> | |
| 121 | + <?php endif?> | |
| 122 | + <?php if (!empty($product->description)) :?> | |
| 123 | + <li class="ionTabs__tab" data-target="Tab_3_name">Видео</li> | |
| 124 | + <?php endif?> | |
| 125 | +<!-- <li class="ionTabs__tab" data-target="Tab_4_name">Отзывы(12)</li>--> | |
| 126 | + </ul> | |
| 127 | + <div class="ionTabs__body"> | |
| 128 | + <?php if (!empty($properties)) :?> | |
| 129 | + <div class="ionTabs__item" data-name="Tab_1_name"> | |
| 130 | + <ul> | |
| 131 | + <?php foreach($properties as $group) :?> | |
| 132 | + <li> | |
| 133 | + <div class="each"> | |
| 134 | + <div class="title"><?= $group->name?></div> | |
| 135 | + <div class="tech"> | |
| 136 | + <?php foreach($group->_options as $option) :?> <?= $option->ValueRenderHTML?><?php endforeach?> | |
| 137 | + </div> | |
| 138 | + </div> | |
| 139 | + </li> | |
| 140 | + <?php endforeach?> | |
| 141 | + </ul> | |
| 142 | + </div> | |
| 143 | + <?php endif?> | |
| 144 | + <?php if (!empty($product->description)) :?> | |
| 145 | + <div class="ionTabs__item" data-name="Tab_2_name"> | |
| 146 | + <?= $product->description?> | |
| 147 | + </div> | |
| 148 | + <?php endif?> | |
| 149 | + <?php if (!empty($product->video)) :?> | |
| 150 | + <div class="ionTabs__item" data-name="Tab_3_name"> | |
| 151 | + <?= $product->video?> | |
| 152 | + </div> | |
| 153 | + <?php endif?> | |
| 154 | + <!--<div class="ionTabs__item" data-name="Tab_4_name"> | |
| 155 | + <span class="tabs_item_name">Отзывы</span> | |
| 156 | + </div>--> | |
| 157 | + | |
| 158 | + <div class="ionTabs__preloader"></div> | |
| 159 | + </div> | |
| 160 | + </div> | |
| 161 | + | |
| 162 | + </div> <!-- конец табов с описанием видео и отзывами --> | |
| 163 | + | |
| 164 | +</div> <!-- end flex container --> | ... | ... | 
frontend/views/catalog/product_item.php
| ... | ... | @@ -4,12 +4,21 @@ | 
| 4 | 4 | <div class="item" data-id="<?= $product->product_id?>"> | 
| 5 | 5 | <!--<div class="new">АКЦИЯ</div> | 
| 6 | 6 | <div class="top">Toп</div>--> | 
| 7 | - <a href="#" class="item_link"><div class="pic"><img src="/images/items/01.jpg"></div> | |
| 7 | + <a href="<?= \yii\helpers\Url::to(['catalog/product', 'alias' => $product->alias])?>" class="item_link"> | |
| 8 | + <div class="pic"> | |
| 9 | + <?php if (empty($product->image)) :?> | |
| 10 | + <img src="/images/no_photo.png"> | |
| 11 | + <?php else :?> | |
| 12 | + <img src="/images/<?= $product->image->image?>" alt="<?= $product->image->alt ? $product->image->alt : $product->name?>"> | |
| 13 | + <?php endif?> | |
| 14 | + </div> | |
| 8 | 15 | <div class="title_item"><?= $product->name?></div></a> | 
| 9 | 16 | <div class="brand">Бренд: <span><?= $product->brand->name?></span></div> | 
| 10 | 17 | <div class="type"><?= implode(', ', $product->categoriesNames)?></div> | 
| 18 | + <?php if($product->variant) :?> | |
| 11 | 19 | <div class="price"><?= $product->variant->price?> <span>грн.</span></div> | 
| 12 | 20 | <button class="basket_add_but" data-id="<?= $product->variant->product_variant_id?>">в корзину</button> | 
| 21 | + <?php endif?> | |
| 13 | 22 | <a href="#" class="compare_add_but" data-id="<?= $product->product_id?>"><span>добавить к сравнению</span></a> | 
| 14 | 23 | <img class="item_bottom_img" src="/images/nc_item_bottom.png" alt=""> | 
| 15 | 24 | </div> | 
| 16 | 25 | \ No newline at end of file | ... | ... | 
frontend/views/catalog/products.php
| ... | ... | @@ -74,9 +74,9 @@ $this->params['breadcrumbs'][] = $category->name; | 
| 74 | 74 | <li><?= $group->name?> | 
| 75 | 75 | <div class="arrow"><img src="/images/head_down.png" alt=""></i></div> | 
| 76 | 76 | <div class="price_filter"> | 
| 77 | - <?php foreach($group->options as $option) :?> | |
| 77 | + <?php foreach($group->_options as $option) :?> | |
| 78 | 78 | <div class="checkbox"> | 
| 79 | - <label><input type="checkbox" name="option[<?= $group->alias?>][]" value="<?= $option->alias?>" /></label> | |
| 79 | + <label><input type="checkbox" name="option[<?= $group->alias?>][]" value="<?= $option->alias?>"<?= (isset($options[$group->alias]) && in_array($option->alias, $options[$group->alias])) ? ' checked' : ''?> /></label> | |
| 80 | 80 | <a href="#"><?= $option->ValueRenderHTML?></a> | 
| 81 | 81 | </div> | 
| 82 | 82 | <?php endforeach?> | ... | ... | 
23.4 KB
9.21 KB
