Skip to content

Commit 039ba2b

Browse files
implement hierarchical categories
1 parent 1ee14be commit 039ba2b

File tree

19 files changed

+253
-27
lines changed

19 files changed

+253
-27
lines changed

Diff for: backend/assets/AppAsset.php

+1
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ class AppAsset extends AssetBundle
2525
public $depends = [
2626
'yii\web\YiiAsset',
2727
'yii\bootstrap\BootstrapAsset',
28+
'rmrevin\yii\fontawesome\AssetBundle',
2829
];
2930
}

Diff for: backend/controllers/ProductController.php

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace backend\controllers;
44

55
use backend\models\ProductSearch;
6+
use common\models\Category;
67
use common\models\Product;
78
use Yii;
89
use yii\filters\VerbFilter;
@@ -71,6 +72,10 @@ public function actionCreate()
7172
if ($model->file->saveAs($filename)) {
7273
$model->image = Url::to($filename, true);
7374
$model->save(false);
75+
foreach ($model->categories as $categoryId) {
76+
$category = Category::findOne($categoryId);
77+
$model->link('categories', $category);
78+
}
7479
return $this->redirect(['view', 'id' => $model->id]);
7580
}
7681
}
@@ -98,6 +103,10 @@ public function actionUpdate($id)
98103
if ($model->file->saveAs($filename)) {
99104
$model->image = Url::to($filename, true);
100105
$model->save(false);
106+
foreach ($model->categories as $categoryId) {
107+
$category = Category::findOne($categoryId);
108+
$model->link('categories', $category);
109+
}
101110
return $this->redirect(['view', 'id' => $model->id]);
102111
}
103112
}

Diff for: backend/views/category/_form.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
<?php
22

3+
use common\models\Category;
34
use kartik\widgets\Select2;
45
use yii\helpers\Html;
56
use yii\widgets\ActiveForm;
67

78
/* @var $this yii\web\View */
89
/* @var $model common\models\Category */
910
/* @var $form yii\widgets\ActiveForm */
11+
12+
$query = Category::find();
13+
if (!$model->isNewRecord)
14+
$query->where(['!=', 'id', $model->id]);
15+
$categories = \yii\helpers\ArrayHelper::map($query->all(), 'id', 'name')
1016
?>
1117

1218
<div class="category-form">
@@ -16,7 +22,7 @@
1622
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
1723

1824
<?= $form->field($model, 'parent_id')->widget(Select2::className(), [
19-
'data' => \yii\helpers\ArrayHelper::map(\common\models\Category::find()->all(), 'id', 'name'),
25+
'data' => $categories,
2026
'options' => ['placeholder' => 'Select a parent category ...'],
2127
'pluginOptions' => [
2228
'allowClear' => true,

Diff for: backend/views/category/view.php

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<h1><?= Html::encode($this->title) ?></h1>
1616

1717
<p>
18+
<?= Html::a('Create', ['create'], ['class' => 'btn btn-success']) ?>
1819
<?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
1920
<?= Html::a('Delete', ['delete', 'id' => $model->id], [
2021
'class' => 'btn btn-danger',

Diff for: backend/views/product/_form.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
<?php
22

3+
use common\models\Category;
34
use kartik\widgets\Select2;
45
use yii\helpers\Html;
56
use yii\widgets\ActiveForm;
67

78
/* @var $this yii\web\View */
89
/* @var $model common\models\Product */
910
/* @var $form yii\widgets\ActiveForm */
11+
$categories = \yii\helpers\ArrayHelper::map(Category::find()->all(), 'id', 'name', function (Category $object, $defaultValue) {
12+
if ($object instanceof Category) {
13+
$parent = $object->getParentCategory()->one();
14+
return empty($parent) ? $defaultValue : $parent->name;
15+
}
16+
return $defaultValue;
17+
});
1018
?>
1119

1220
<div class="product-form">
@@ -16,7 +24,7 @@
1624
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
1725

1826
<?= $form->field($model, 'categories')->widget(Select2::className(), [
19-
'data' => \yii\helpers\ArrayHelper::map(\common\models\Category::find()->all(), 'id', 'name'),
27+
'data' => $categories,
2028
'options' => [
2129
'placeholder' => 'Select a category ...',
2230
'multiple' => true,

Diff for: common/components/Catalog.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace common\components;
4+
5+
6+
use common\models\Category;
7+
use yii\base\Component;
8+
9+
class Catalog extends Component
10+
{
11+
/**
12+
* @return Category[]
13+
*/
14+
public function getCategories()
15+
{
16+
$categories = Category::find()->orderBy(['parent_id' => SORT_ASC, 'id' => SORT_ASC])->all();
17+
$tree = $this->buildTree($categories, null);
18+
19+
return $tree;
20+
}
21+
22+
protected function buildTree(array $elements, $parentId = 0)
23+
{
24+
$branch = [];
25+
26+
foreach ($elements as $element) {
27+
if ($element['parent_id'] == $parentId) {
28+
$children = $this->buildTree($elements, $element['id']);
29+
if ($children) {
30+
$element['children'] = $children;
31+
}
32+
$branch[] = $element;
33+
}
34+
}
35+
36+
return $branch;
37+
}
38+
39+
}

Diff for: common/models/Category.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
*/
1515
class Category extends ActiveRecord
1616
{
17+
/**
18+
* @var Category[]
19+
*/
20+
public $children;
1721
/**
1822
* @inheritdoc
1923
*/
@@ -28,7 +32,8 @@ public static function tableName()
2832
public function rules()
2933
{
3034
return [
31-
[['name'], 'string', 'max' => 255]
35+
[['name'], 'string', 'max' => 255],
36+
[['parent_id'], 'exist', 'targetClass' => self::className(), 'targetAttribute' => 'id'],
3237
];
3338
}
3439

@@ -49,4 +54,14 @@ public function getProducts()
4954
->viaTable('category_product', ['category_id' => 'id']);
5055
}
5156

57+
public function getParentCategory()
58+
{
59+
return self::find()->where(['id' => $this->parent_id]);
60+
}
61+
62+
public function getChildCategories()
63+
{
64+
return self::find()->where(['parent_id' => $this->id]);
65+
}
66+
5267
}

Diff for: common/models/Product.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function rules()
3939
[['short_description', 'long_description'], 'string'],
4040
[['name', 'image'], 'string', 'max' => 255],
4141
[['file'], 'file', 'extensions' => ['jpg', 'png']],
42+
[['categories'], 'safe'],
4243
];
4344
}
4445

@@ -64,7 +65,7 @@ public function getImage()
6465
public function getCategories()
6566
{
6667
return $this->hasMany(Category::className(), ['id' => 'category_id'])
67-
->viaTable('category_product', ['product_id', 'id']);
68+
->viaTable('category_product', ['product_id' => 'id']);
6869
}
6970

7071
}

Diff for: composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"yiisoft/yii2": "2.0.0",
2222
"yiisoft/yii2-bootstrap": "2.0.0",
2323
"yiisoft/yii2-swiftmailer": "2.0.0",
24+
"rmrevin/yii2-fontawesome": "2.5.0",
2425
"kartik-v/yii2-widgets": "*"
2526
},
2627
"require-dev": {

Diff for: console/migrations/m141109_111923_create_categories.php

+2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ public function up()
1010
$this->createTable('{{%category}}', [
1111
'id' => Schema::TYPE_PK,
1212
'name' => Schema::TYPE_STRING,
13+
'parent_id' => Schema::TYPE_INTEGER,
1314
]);
15+
$this->createIndex('parent', '{{%category}}', ['parent_id']);
1416
}
1517

1618
public function down()

Diff for: frontend/config/main.php

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
'enablePrettyUrl' => true,
3333
'showScriptName' => false,
3434
],
35+
'catalog' => [
36+
'class' => 'common\components\Catalog',
37+
]
3538
],
3639
'params' => $params,
3740
];

Diff for: frontend/controllers/CategoryController.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace frontend\controllers;
4+
5+
use common\models\Product;
6+
use yii\data\ActiveDataProvider;
7+
8+
class CategoryController extends \yii\web\Controller
9+
{
10+
public function actionIndex()
11+
{
12+
return $this->render('index');
13+
}
14+
15+
public function actionView($id)
16+
{
17+
$dataProvider = new ActiveDataProvider(['query' => Product::find()]);
18+
19+
return $this->render('view', ['dataProvider' => $dataProvider]);
20+
}
21+
22+
}

Diff for: frontend/controllers/SiteController.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use frontend\models\SignupForm;
1010
use Yii;
1111
use yii\base\InvalidParamException;
12+
use yii\data\ActiveDataProvider;
1213
use yii\filters\AccessControl;
1314
use yii\filters\VerbFilter;
1415
use yii\web\BadRequestHttpException;
@@ -69,7 +70,8 @@ public function actions()
6970

7071
public function actionIndex()
7172
{
72-
return $this->render('index');
73+
$dataProvider = new ActiveDataProvider(['query' => Product::find()]);
74+
return $this->render('index', ['dataProvider' => $dataProvider]);
7375
}
7476

7577
public function actionLogin()

Diff for: frontend/views/category/index.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
/* @var $this yii\web\View */
3+
?>
4+
<h1>category/index</h1>
5+
6+
<p>
7+
You may change the content of this page by modifying
8+
the file <code><?= __FILE__; ?></code>.
9+
</p>

Diff for: frontend/views/category/view.php

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
use frontend\widgets\CategoriesList;
3+
use yii\helpers\Html;
4+
5+
/* @var $this yii\web\View */
6+
$this->title = 'Shop Homepage';
7+
?>
8+
<div class="site-index">
9+
10+
<div class="body-content">
11+
12+
<div class="row">
13+
14+
<?= CategoriesList::widget() ?>
15+
16+
<div class="col-md-9">
17+
18+
<?php $items = [];
19+
foreach (\common\models\Product::find()->limit(3)->all() as $product)
20+
$items[] = [
21+
'content' => Html::a(Html::img($product->getImage(), ['alt' => $product->name]), ['site/product', 'id' => $product->id]),
22+
'caption' => $product->name . ' $' . $product->price,
23+
]; ?>
24+
<div class="row carousel-holder">
25+
<div class="col-md-12">
26+
<?= \yii\bootstrap\Carousel::widget([
27+
'items' => $items,
28+
'options' => ['class' => 'slide'],
29+
'controls' => [
30+
Html::tag('span', '', ['class' => 'glyphicon glyphicon-chevron-left']),
31+
Html::tag('span', '', ['class' => 'glyphicon glyphicon-chevron-right']),
32+
],
33+
]) ?>
34+
</div>
35+
</div>
36+
37+
<div class="row">
38+
39+
<?php foreach (\common\models\Product::find()->all() as $product) : ?>
40+
<div class="col-sm-4 col-lg-4 col-md-4">
41+
<div class="thumbnail">
42+
<?= Html::img($product->getImage(), ['alt' => $product->name]) ?>
43+
44+
<div class="caption">
45+
<h4 class="pull-right">$<?= $product->price ?></h4>
46+
<h4><?= Html::a($product->name, ['site/product', 'id' => $product->id]) ?></h4>
47+
48+
<p><?= $product->short_description ?></p>
49+
</div>
50+
<div class="ratings">
51+
<p class="pull-right">15 reviews</p>
52+
53+
<p>
54+
<span class="glyphicon glyphicon-star"></span>
55+
<span class="glyphicon glyphicon-star"></span>
56+
<span class="glyphicon glyphicon-star"></span>
57+
<span class="glyphicon glyphicon-star"></span>
58+
<span class="glyphicon glyphicon-star-empty"></span>
59+
</p>
60+
</div>
61+
</div>
62+
</div>
63+
<?php endforeach; ?>
64+
65+
</div>
66+
67+
</div>
68+
69+
</div>
70+
71+
</div>
72+
73+
</div>

Diff for: frontend/views/site/index.php

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
<?php
2+
use frontend\widgets\CategoriesList;
23
use yii\helpers\Html;
3-
use yii\helpers\Url;
44

5-
/* @var $this yii\web\View */
5+
/**
6+
* @var $this yii\web\View
7+
* @var $dataProvider \yii\data\ActiveDataProvider
8+
*/
69
$this->title = 'Shop Homepage';
710
?>
811
<div class="site-index">
@@ -11,15 +14,7 @@
1114

1215
<div class="row">
1316

14-
<div class="col-md-3">
15-
<p class="lead">Shop Name</p>
16-
17-
<div class="list-group">
18-
<?php foreach (\common\models\Category::find()->all() as $category): ?>
19-
<?= Html::a($category->name, Url::toRoute(['site/category', 'id' => $category->id]), ['class' => 'list-group-item']) ?>
20-
<?php endforeach; ?>
21-
</div>
22-
</div>
17+
<?= CategoriesList::widget() ?>
2318

2419
<div class="col-md-9">
2520

@@ -44,7 +39,7 @@
4439

4540
<div class="row">
4641

47-
<?php foreach (\common\models\Product::find()->all() as $product) : ?>
42+
<?php foreach ($dataProvider->getModels() as $product) : ?>
4843
<div class="col-sm-4 col-lg-4 col-md-4">
4944
<div class="thumbnail">
5045
<?= Html::img($product->getImage(), ['alt' => $product->name]) ?>

0 commit comments

Comments
 (0)