Commit 01799a55c0794bc2c127a29747e09cda10a915e1
0 parents
first commit
Showing
33 changed files
with
4302 additions
and
0 deletions
Show diff stats
1 | +++ a/behaviors/ArtBoxAccessBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use Yii; | ||
6 | + use yii\base\Action; | ||
7 | + use yii\base\Event; | ||
8 | + use yii\behaviors\AttributeBehavior; | ||
9 | + use yii\di\Instance; | ||
10 | + use yii\base\Module; | ||
11 | + use yii\filters\AccessRule; | ||
12 | + use yii\web\Request; | ||
13 | + use yii\web\User; | ||
14 | + use yii\web\ForbiddenHttpException; | ||
15 | + | ||
16 | + class ArtBoxAccessBehavior extends AttributeBehavior | ||
17 | + { | ||
18 | + | ||
19 | + public $rules = []; | ||
20 | + | ||
21 | + /** | ||
22 | + * @var AccessRule[] $ruleList | ||
23 | + */ | ||
24 | + private $ruleList = []; | ||
25 | + | ||
26 | + public function events() | ||
27 | + { | ||
28 | + return [ | ||
29 | + Module::EVENT_BEFORE_ACTION => 'interception', | ||
30 | + ]; | ||
31 | + } | ||
32 | + | ||
33 | + /** | ||
34 | + * Check whether current user have access to current action. | ||
35 | + * | ||
36 | + * @param Event $event | ||
37 | + * | ||
38 | + * @return void | ||
39 | + * @throws \yii\web\ForbiddenHttpException | ||
40 | + */ | ||
41 | + public function interception($event) | ||
42 | + { | ||
43 | + if (!isset( Yii::$app->i18n->translations[ 'db_rbac' ] )) { | ||
44 | + Yii::$app->i18n->translations[ 'db_rbac' ] = [ | ||
45 | + 'class' => 'yii\i18n\PhpMessageSource', | ||
46 | + 'sourceLanguage' => 'ru-Ru', | ||
47 | + 'basePath' => '@developeruz/db_rbac/messages', | ||
48 | + ]; | ||
49 | + } | ||
50 | + | ||
51 | + $route = Yii::$app->getRequest() | ||
52 | + ->resolve(); | ||
53 | + //Проверяем права по конфигу | ||
54 | + $this->createRule(); | ||
55 | + $user = Instance::ensure(Yii::$app->user, User::className()); | ||
56 | + $request = Yii::$app->getRequest(); | ||
57 | + $action = $event->action; | ||
58 | + | ||
59 | + if (!$this->cheсkByRule($action, $user, $request)) { | ||
60 | + //И по AuthManager | ||
61 | + if (!$this->checkPermission($route)) { | ||
62 | + if ($user->getIsGuest()) { | ||
63 | + $user->loginRequired(); | ||
64 | + } else { | ||
65 | + throw new ForbiddenHttpException(Yii::t('db_rbac', 'Недостаточно прав')); | ||
66 | + } | ||
67 | + } | ||
68 | + | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * Fill $ruleList with AccessRules | ||
74 | + * | ||
75 | + * @return void | ||
76 | + */ | ||
77 | + protected function createRule() | ||
78 | + { | ||
79 | + foreach ($this->rules as $controller => $rule) { | ||
80 | + foreach ($rule as $singleRule) { | ||
81 | + if (is_array($singleRule)) { | ||
82 | + $option = [ | ||
83 | + 'controllers' => [ $controller ], | ||
84 | + 'class' => 'yii\filters\AccessRule', | ||
85 | + ]; | ||
86 | + $this->ruleList[] = Yii::createObject(array_merge($option, $singleRule)); | ||
87 | + } | ||
88 | + } | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + /** | ||
93 | + * Check whether the User allowed to perform action | ||
94 | + * | ||
95 | + * @param Action $action | ||
96 | + * @param User $user | ||
97 | + * @param Request $request | ||
98 | + * | ||
99 | + * @return bool | ||
100 | + */ | ||
101 | + protected function cheсkByRule($action, $user, $request) | ||
102 | + { | ||
103 | + | ||
104 | + foreach ($this->ruleList as $rule) { | ||
105 | + | ||
106 | + if ($rule->allows($action, $user, $request)) { | ||
107 | + return true; | ||
108 | + } | ||
109 | + } | ||
110 | + return false; | ||
111 | + } | ||
112 | + | ||
113 | + /** | ||
114 | + * Check whether the User have permission for current operation | ||
115 | + * | ||
116 | + * @param array $route | ||
117 | + * | ||
118 | + * @return bool | ||
119 | + */ | ||
120 | + protected function checkPermission($route) | ||
121 | + { | ||
122 | + //$route[0] - is the route, $route[1] - is the associated parameters | ||
123 | + $routePathTmp = explode('/', $route[ 0 ]); | ||
124 | + $routeVariant = array_shift($routePathTmp); | ||
125 | + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) { | ||
126 | + return true; | ||
127 | + } | ||
128 | + /** | ||
129 | + * @var string $routePart | ||
130 | + */ | ||
131 | + foreach ($routePathTmp as $routePart) { | ||
132 | + $routeVariant .= '/' . $routePart; | ||
133 | + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) { | ||
134 | + return true; | ||
135 | + } | ||
136 | + } | ||
137 | + return false; | ||
138 | + } | ||
139 | + | ||
140 | + } | ||
141 | + | ||
0 | \ No newline at end of file | 142 | \ No newline at end of file |
1 | +++ a/behaviors/ImageBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use yii\base\Behavior; | ||
6 | + use yii\base\Event; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + | ||
9 | + /** | ||
10 | + * Class ImageBehavior | ||
11 | + * @package common\behaviors | ||
12 | + */ | ||
13 | + class ImageBehavior extends Behavior | ||
14 | + { | ||
15 | + | ||
16 | + /** | ||
17 | + * @var string column where file name is stored | ||
18 | + */ | ||
19 | + public $link; | ||
20 | + | ||
21 | + /** | ||
22 | + * @var string directory name | ||
23 | + */ | ||
24 | + public $directory; | ||
25 | + | ||
26 | + /** | ||
27 | + * @inheritdoc | ||
28 | + */ | ||
29 | + public function events() | ||
30 | + { | ||
31 | + return [ | ||
32 | + ActiveRecord::EVENT_BEFORE_DELETE => 'beforeDelete', | ||
33 | + ]; | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * @param Event $event | ||
38 | + */ | ||
39 | + public function beforeDelete($event) | ||
40 | + { | ||
41 | + $file = $this->getImageFile(); | ||
42 | + if(file_exists($file)) { | ||
43 | + unlink($file); | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + /** | ||
48 | + * Get image file path | ||
49 | + * | ||
50 | + * @return null|string | ||
51 | + */ | ||
52 | + public function getImageFile() | ||
53 | + { | ||
54 | + $link = $this->link; | ||
55 | + return empty( $this->owner->$link ) ? NULL : \Yii::getAlias('@storage/' . $this->directory . '/' . $this->owner->$link); | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * Get image file url | ||
60 | + * | ||
61 | + * @return null|string | ||
62 | + */ | ||
63 | + public function getImageUrl() | ||
64 | + { | ||
65 | + $link = $this->link; | ||
66 | + return empty( $this->owner->$link ) ? NULL : '/storage/' . $this->directory . '/' . $this->owner->$link; | ||
67 | + } | ||
68 | + } | ||
0 | \ No newline at end of file | 69 | \ No newline at end of file |
1 | +++ a/behaviors/MultipleImgBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use common\components\artboximage\ArtboxImageHelper; | ||
6 | + use yii\base\Behavior; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + use yii\helpers\Url; | ||
9 | + | ||
10 | + /** | ||
11 | + * Class MultipleImgBehavior | ||
12 | + * @todo Write validation | ||
13 | + * @property ActiveRecord $owner | ||
14 | + * @property ActiveRecord $image | ||
15 | + * @property ActiveRecord[] $images | ||
16 | + * @package common\behaviors | ||
17 | + */ | ||
18 | + class MultipleImgBehavior extends Behavior | ||
19 | + { | ||
20 | + | ||
21 | + /** | ||
22 | + * key - $model foreign key, value - $owner link key (usual ID) | ||
23 | + * @var array | ||
24 | + */ | ||
25 | + public $links = []; | ||
26 | + | ||
27 | + /** | ||
28 | + * Will be passed to get image and get images queries | ||
29 | + * | ||
30 | + * @var array | ||
31 | + */ | ||
32 | + public $conditions = []; | ||
33 | + | ||
34 | + /** | ||
35 | + * Full namespaced image model | ||
36 | + * @var string | ||
37 | + */ | ||
38 | + public $model; | ||
39 | + | ||
40 | + /** | ||
41 | + * Image config array: | ||
42 | + * 'caption' - $model caption attribute | ||
43 | + * 'delete_action' - url to image delete action, will be passed as first argument to | ||
44 | + * Url::to(); | ||
45 | + * 'id' - $model primary key | ||
46 | + * @var array | ||
47 | + */ | ||
48 | + public $config = []; | ||
49 | + | ||
50 | + /** | ||
51 | + * One image query | ||
52 | + * | ||
53 | + * @return \yii\db\ActiveQuery | ||
54 | + */ | ||
55 | + public function getImage() | ||
56 | + { | ||
57 | + /** | ||
58 | + * @var ActiveRecord $owner | ||
59 | + */ | ||
60 | + $owner = $this->owner; | ||
61 | + $query = $owner->hasOne($this->model, $this->links); | ||
62 | + $conditions = $this->conditions; | ||
63 | + foreach($conditions as $condition) { | ||
64 | + $query->andWhere($condition); | ||
65 | + } | ||
66 | + return $query; | ||
67 | + } | ||
68 | + | ||
69 | + /** | ||
70 | + * All images query | ||
71 | + * | ||
72 | + * @return \yii\db\ActiveQuery | ||
73 | + */ | ||
74 | + public function getImages() | ||
75 | + { | ||
76 | + /** | ||
77 | + * @var ActiveRecord $owner | ||
78 | + */ | ||
79 | + $owner = $this->owner; | ||
80 | + $query = $owner->hasMany($this->model, $this->links); | ||
81 | + $conditions = $this->conditions; | ||
82 | + foreach($conditions as $left => $right) { | ||
83 | + $query->andWhere([$left => $right]); | ||
84 | + } | ||
85 | + return $query; | ||
86 | + } | ||
87 | + | ||
88 | + /** | ||
89 | + * Get images config array for FileInput | ||
90 | + * | ||
91 | + * @return array | ||
92 | + */ | ||
93 | + public function getImagesConfig() | ||
94 | + { | ||
95 | + $op = []; | ||
96 | + $images = $this->getImages()->all(); | ||
97 | + $config = $this->config; | ||
98 | + if(!isset( $config[ 'id' ] )) { | ||
99 | + return $op; | ||
100 | + } | ||
101 | + foreach($images as $image) { | ||
102 | + $op[] = [ | ||
103 | + 'caption' => ( isset( $config[ 'caption' ] ) ) ? $image->{$config[ 'caption' ]} : '', | ||
104 | + 'url' => ( isset( $config[ 'delete_action' ] ) ) ? Url::to([ | ||
105 | + $config[ 'delete_action' ], | ||
106 | + 'id' => $image->{$config[ 'id' ]}, | ||
107 | + ]) : '', | ||
108 | + 'key' => $image->{$config[ 'id' ]}, | ||
109 | + 'extra' => [ | ||
110 | + 'id' => $image->{$config[ 'id' ]}, | ||
111 | + ], | ||
112 | + ]; | ||
113 | + } | ||
114 | + return $op; | ||
115 | + } | ||
116 | + | ||
117 | + /** | ||
118 | + * Get images HTML | ||
119 | + * | ||
120 | + * @param string $preset | ||
121 | + * | ||
122 | + * @return array | ||
123 | + */ | ||
124 | + public function getImagesHTML($preset = 'admin_thumb') | ||
125 | + { | ||
126 | + $op = []; | ||
127 | + $images = $this->getImages()->all(); | ||
128 | + foreach($images as $image) { | ||
129 | + $op[] = ArtboxImageHelper::getImage($image->imageUrl, $preset); | ||
130 | + } | ||
131 | + return $op; | ||
132 | + } | ||
133 | + | ||
134 | + public function getImageUrl() | ||
135 | + { | ||
136 | + $image = $this->getImage()->one(); | ||
137 | + if(!empty($image)) { | ||
138 | + return $image->getImageUrl(); | ||
139 | + } else { | ||
140 | + return NULL; | ||
141 | + } | ||
142 | + } | ||
143 | + } | ||
0 | \ No newline at end of file | 144 | \ No newline at end of file |
1 | +++ a/behaviors/SaveImgBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use yii\base\Behavior; | ||
6 | + use yii\base\ModelEvent; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + use yii\web\UploadedFile; | ||
9 | + | ||
10 | + /** | ||
11 | + * Class Save Image Behavior | ||
12 | + * @property ActiveRecord $owner | ||
13 | + * @package common\behaviors | ||
14 | + */ | ||
15 | + class SaveImgBehavior extends Behavior | ||
16 | + { | ||
17 | + | ||
18 | + public $fields; | ||
19 | + | ||
20 | + public $isLanguage = false; | ||
21 | + | ||
22 | + public function events() | ||
23 | + { | ||
24 | + return [ | ||
25 | + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave', | ||
26 | + ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', | ||
27 | + ]; | ||
28 | + } | ||
29 | + | ||
30 | + /** | ||
31 | + * @param ModelEvent $event | ||
32 | + */ | ||
33 | + public function beforeSave($event) | ||
34 | + { | ||
35 | + foreach($this->fields as $field) { | ||
36 | + $field_name = $field[ 'name' ]; | ||
37 | + $name = $field_name; | ||
38 | + if($this->isLanguage) { | ||
39 | + $name = '[' . $this->owner->language_id . ']' . $name; | ||
40 | + } | ||
41 | + | ||
42 | + $image = UploadedFile::getInstance($this->owner, $name); | ||
43 | + | ||
44 | + if(empty( $image ) && $event->name == ActiveRecord::EVENT_BEFORE_UPDATE) { | ||
45 | + $this->owner->$field_name = $this->owner->getOldAttribute($field_name); | ||
46 | + } elseif(!empty( $image )) { | ||
47 | + $imgDir = \Yii::getAlias('@storage/' . $field[ 'directory' ] . '/'); | ||
48 | + | ||
49 | + if(!is_dir($imgDir)) { | ||
50 | + mkdir($imgDir, 0755, true); | ||
51 | + } | ||
52 | + | ||
53 | + $baseName = $image->baseName; | ||
54 | + | ||
55 | + $iteration = 0; | ||
56 | + $file_name = $imgDir . $baseName . '.' . $image->extension; | ||
57 | + while(file_exists($file_name)) { | ||
58 | + $baseName = $image->baseName . '_' . ++$iteration; | ||
59 | + $file_name = $imgDir . $baseName . '.' . $image->extension; | ||
60 | + } | ||
61 | + unset( $iteration ); | ||
62 | + | ||
63 | + $this->owner->$field_name = $baseName . '.' . $image->extension; | ||
64 | + | ||
65 | + $image->saveAs($file_name); | ||
66 | + } | ||
67 | + } | ||
68 | + } | ||
69 | + | ||
70 | + /** | ||
71 | + * @param int $field | ||
72 | + * | ||
73 | + * @return null|string | ||
74 | + */ | ||
75 | + public function getImageFile($field = 0) | ||
76 | + { | ||
77 | + $fieldset = $this->fields[ $field ]; | ||
78 | + $name = $fieldset[ 'name' ]; | ||
79 | + $directory = $fieldset[ 'directory' ]; | ||
80 | + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name; | ||
81 | + } | ||
82 | + | ||
83 | + /** | ||
84 | + * @param int $field | ||
85 | + * | ||
86 | + * @return null|string | ||
87 | + */ | ||
88 | + public function getImageUrl($field = 0) | ||
89 | + { | ||
90 | + $fieldset = $this->fields[ $field ]; | ||
91 | + $name = $fieldset[ 'name' ]; | ||
92 | + $directory = $fieldset[ 'directory' ]; | ||
93 | + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name; | ||
94 | + } | ||
95 | + } | ||
0 | \ No newline at end of file | 96 | \ No newline at end of file |
1 | +++ a/behaviors/SaveMultipleFileBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use yii\base\Behavior; | ||
6 | + use yii\base\Event; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + use yii\web\UploadedFile; | ||
9 | + | ||
10 | + /** | ||
11 | + * Class SaveMultipleFileBehavior | ||
12 | + * | ||
13 | + * @todo Write validators | ||
14 | + * | ||
15 | + * @package common\behaviors | ||
16 | + */ | ||
17 | + class SaveMultipleFileBehavior extends Behavior | ||
18 | + { | ||
19 | + | ||
20 | + /** | ||
21 | + * $owner attribute to write files | ||
22 | + * @var string | ||
23 | + */ | ||
24 | + public $name; | ||
25 | + | ||
26 | + /** | ||
27 | + * Directory to store files | ||
28 | + * @var string | ||
29 | + */ | ||
30 | + public $directory; | ||
31 | + | ||
32 | + /** | ||
33 | + * Column in $model where to store filename | ||
34 | + * @var string | ||
35 | + */ | ||
36 | + public $column; | ||
37 | + | ||
38 | + /** | ||
39 | + * key - owner link attribute (usual ID), value - $model link attribute | ||
40 | + * @var array | ||
41 | + */ | ||
42 | + public $links = []; | ||
43 | + | ||
44 | + /** | ||
45 | + * Full namespaced classname of file table | ||
46 | + * @var string | ||
47 | + */ | ||
48 | + public $model; | ||
49 | + | ||
50 | + public function events() | ||
51 | + { | ||
52 | + return [ | ||
53 | + ActiveRecord::EVENT_AFTER_UPDATE => 'downloadFiles', | ||
54 | + ActiveRecord::EVENT_AFTER_INSERT => 'downloadFiles', | ||
55 | + ]; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * Save files to file table | ||
60 | + * | ||
61 | + * @param Event $event | ||
62 | + */ | ||
63 | + public function downloadFiles($event) | ||
64 | + { | ||
65 | + /** | ||
66 | + * @var ActiveRecord $owner | ||
67 | + */ | ||
68 | + $owner = $this->owner; | ||
69 | + $name = $this->name; | ||
70 | + $owner->$name = UploadedFile::getInstances($owner, $name); | ||
71 | + if(!empty( $files = $this->filesUpload() )) { | ||
72 | + $model = $this->model; | ||
73 | + $links = $this->links; | ||
74 | + $column = $this->column; | ||
75 | + foreach($files as $file) { | ||
76 | + /** | ||
77 | + * @var ActiveRecord $fileModel | ||
78 | + */ | ||
79 | + $fileModel = new $model(); | ||
80 | + foreach($links as $link_owner => $link) { | ||
81 | + $fileModel->$link = $owner->$link_owner; | ||
82 | + } | ||
83 | + $fileModel->$column = $file; | ||
84 | + $fileModel->save(); | ||
85 | + } | ||
86 | + } | ||
87 | + $this->detach(); | ||
88 | + } | ||
89 | + | ||
90 | + /** | ||
91 | + * Save files to file system | ||
92 | + * | ||
93 | + * @return array | ||
94 | + */ | ||
95 | + private function filesUpload() | ||
96 | + { | ||
97 | + $owner = $this->owner; | ||
98 | + $name = $this->name; | ||
99 | + $directory = $this->directory; | ||
100 | + $fileDir = \Yii::getAlias('@storage/' . $directory . '/'); | ||
101 | + if(!is_dir($fileDir)) { | ||
102 | + mkdir($fileDir, 0755, true); | ||
103 | + } | ||
104 | + $files = []; | ||
105 | + /** | ||
106 | + * @var UploadedFile $file | ||
107 | + */ | ||
108 | + foreach($owner->$name as $file) { | ||
109 | + $fileName = $file->baseName . '.' . $file->extension; | ||
110 | + $i = 0; | ||
111 | + while(file_exists(\Yii::getAlias($fileDir . $fileName))) { | ||
112 | + $fileName = $file->baseName . '_' . ++$i . '.' . $file->extension; | ||
113 | + } | ||
114 | + $file->saveAs($fileDir . $fileName); | ||
115 | + $files[] = $fileName; | ||
116 | + } | ||
117 | + return $files; | ||
118 | + } | ||
119 | + } | ||
0 | \ No newline at end of file | 120 | \ No newline at end of file |
1 | +++ a/behaviors/Slug.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\behaviors; | ||
4 | + | ||
5 | + use yii; | ||
6 | + use yii\base\Behavior; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + use dosamigos\transliterator\TransliteratorHelper; | ||
9 | + | ||
10 | + class Slug extends Behavior | ||
11 | + { | ||
12 | + | ||
13 | + /** | ||
14 | + * @var string | ||
15 | + */ | ||
16 | + public $inAttribute = 'title'; | ||
17 | + | ||
18 | + /** | ||
19 | + * @var string | ||
20 | + */ | ||
21 | + public $outAttribute = 'alias'; | ||
22 | + | ||
23 | + /** | ||
24 | + * @var bool | ||
25 | + */ | ||
26 | + public $translit = true; | ||
27 | + | ||
28 | + /** | ||
29 | + * @inheritdoc | ||
30 | + */ | ||
31 | + public function events() | ||
32 | + { | ||
33 | + return [ | ||
34 | + ActiveRecord::EVENT_BEFORE_INSERT => 'getSlug', | ||
35 | + ActiveRecord::EVENT_BEFORE_UPDATE => 'getSlug', | ||
36 | + ]; | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * Generate slug | ||
41 | + * | ||
42 | + * @param yii\base\Event $event | ||
43 | + * | ||
44 | + * @return void | ||
45 | + */ | ||
46 | + public function getSlug($event) | ||
47 | + { | ||
48 | + if(!empty( $this->owner->{$this->inAttribute} )) { | ||
49 | + if(empty( $this->owner->{$this->outAttribute} )) { | ||
50 | + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->inAttribute}); | ||
51 | + } else { | ||
52 | + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->outAttribute}); | ||
53 | + } | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * @param string $slug | ||
59 | + * | ||
60 | + * @return string | ||
61 | + */ | ||
62 | + private function generateSlug($slug) | ||
63 | + { | ||
64 | + $slug = $this->slugify($slug); | ||
65 | + if($this->checkUniqueSlug($slug)) { | ||
66 | + return $slug; | ||
67 | + } else { | ||
68 | + for($suffix = 2; !$this->checkUniqueSlug($new_slug = $slug . '-' . $suffix); $suffix++) { | ||
69 | + } | ||
70 | + return $new_slug; | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + /** | ||
75 | + * @param string $slug | ||
76 | + * | ||
77 | + * @return string | ||
78 | + */ | ||
79 | + private function slugify($slug) | ||
80 | + { | ||
81 | + if($this->translit) { | ||
82 | + return yii\helpers\Inflector::slug( $this->translit( $slug ), '-', true ); | ||
83 | + } else { | ||
84 | + return $this->slug($slug, '-', true); | ||
85 | + } | ||
86 | + } | ||
87 | + | ||
88 | + /** | ||
89 | + * @param string $string | ||
90 | + * @param string $replacement | ||
91 | + * @param bool $lowercase | ||
92 | + * | ||
93 | + * @return string | ||
94 | + */ | ||
95 | + private function slug($string, $replacement = '-', $lowercase = true) | ||
96 | + { | ||
97 | + $string = preg_replace('/[^\p{L}\p{Nd}]+/u', $replacement, $string); | ||
98 | + $string = trim($string, $replacement); | ||
99 | + return $lowercase ? strtolower($string) : $string; | ||
100 | + } | ||
101 | + | ||
102 | + /** | ||
103 | + * Check whether current slug is unique | ||
104 | + * | ||
105 | + * @param string $slug | ||
106 | + * | ||
107 | + * @return bool | ||
108 | + */ | ||
109 | + private function checkUniqueSlug($slug) | ||
110 | + { | ||
111 | + /** | ||
112 | + * @var ActiveRecord $owner | ||
113 | + */ | ||
114 | + $owner = $this->owner; | ||
115 | + $query = $owner->find() | ||
116 | + ->where([ | ||
117 | + $this->outAttribute => $slug, | ||
118 | + ]); | ||
119 | + if(!$owner->isNewRecord) { | ||
120 | + $pks = $owner->primaryKey(); | ||
121 | + if(!empty( $pks )) { | ||
122 | + $pk_rules = [ 'and' ]; | ||
123 | + foreach($pks as $pk) { | ||
124 | + $pk_rules[] = [ $pk => $owner->$pk ]; | ||
125 | + } | ||
126 | + $query->andWhere([ | ||
127 | + 'not', | ||
128 | + $pk_rules, | ||
129 | + ]); | ||
130 | + } | ||
131 | + } | ||
132 | + return !$query->exists(); | ||
133 | + } | ||
134 | + | ||
135 | + static function translit ($string, $setting = 'all') | ||
136 | + { | ||
137 | + $letter = array ( | ||
138 | + | ||
139 | + 'а' => 'a', 'б' => 'b', 'в' => 'v', | ||
140 | + 'г' => 'g', 'д' => 'd', 'е' => 'e', | ||
141 | + 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', | ||
142 | + 'и' => 'i', 'й' => 'y', 'к' => 'k', | ||
143 | + 'л' => 'l', 'м' => 'm', 'н' => 'n', | ||
144 | + 'о' => 'o', 'п' => 'p', 'р' => 'r', | ||
145 | + 'с' => 's', 'т' => 't', 'у' => 'u', | ||
146 | + 'ф' => 'f', 'х' => 'h', 'ц' => 'c', | ||
147 | + 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch', | ||
148 | + 'ь' => "", 'ы' => 'y', 'ъ' => "", | ||
149 | + 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', | ||
150 | + 'ї' => 'yi', 'є' => 'ye', 'і' => 'ee', | ||
151 | + | ||
152 | + 'А' => 'A', 'Б' => 'B', 'В' => 'V', | ||
153 | + 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', | ||
154 | + 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z', | ||
155 | + 'И' => 'I', 'Й' => 'Y', 'К' => 'K', | ||
156 | + 'Л' => 'L', 'М' => 'M', 'Н' => 'N', | ||
157 | + 'О' => 'O', 'П' => 'P', 'Р' => 'R', | ||
158 | + 'С' => 'S', 'Т' => 'T', 'У' => 'U', | ||
159 | + 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C', | ||
160 | + 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch', | ||
161 | + 'Ь' => "", 'Ы' => 'Y', 'Ъ' => "", | ||
162 | + 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya', | ||
163 | + 'Ї' => 'Yi', 'Є' => 'Ye', 'І' => 'Ee' | ||
164 | + ); | ||
165 | + | ||
166 | + $symbol = array ( | ||
167 | + ' ' => '-', "'" => '', '"' => '', | ||
168 | + '!' => '', "@" => '', '#' => '', | ||
169 | + '$' => '', "%" => '', '^' => '', | ||
170 | + ';' => '', "*" => '', '(' => '', | ||
171 | + ')' => '', "+" => '', '~' => '', | ||
172 | + '.' => '', ',' => '-', '?' => '', | ||
173 | + '…' => '', '№' => 'N', '°' => '', | ||
174 | + '`' => '', '|' => '', '&' => '-and-', | ||
175 | + '<' => '', '>' => '' | ||
176 | + ); | ||
177 | + | ||
178 | + if ($setting == 'all') | ||
179 | + { | ||
180 | + $converter = $letter + $symbol; | ||
181 | + } | ||
182 | + else if ($setting == 'letter') | ||
183 | + { | ||
184 | + $converter = $letter; | ||
185 | + } | ||
186 | + else if ($setting == 'symbol') | ||
187 | + { | ||
188 | + $converter = $symbol; | ||
189 | + } | ||
190 | + | ||
191 | + $url = strtr ($string, $converter); | ||
192 | + | ||
193 | + $url = str_replace ("---", '-', $url); | ||
194 | + $url = str_replace ("--", '-', $url); | ||
195 | + | ||
196 | + return $url; | ||
197 | + } | ||
198 | + | ||
199 | + } | ||
200 | + | ||
0 | \ No newline at end of file | 201 | \ No newline at end of file |
1 | +++ a/components/SmsSender.php | ||
1 | +<?php | ||
2 | +namespace common\components; | ||
3 | + | ||
4 | +use yii\base\Component; | ||
5 | + | ||
6 | +class SmsSender extends Component | ||
7 | +{ | ||
8 | + | ||
9 | + public $tel; | ||
10 | + public $text; | ||
11 | + | ||
12 | + public function send($tel, $text) | ||
13 | + { | ||
14 | + | ||
15 | + // $text = iconv('windows-1251', 'utf-8', htmlspecialchars($text)); | ||
16 | + $description = iconv('windows-1251', 'utf-8', htmlspecialchars($tel . ' description')); | ||
17 | + $start_time = 'AUTO'; // отправить немедленно или ставим дату и время в формате YYYY-MM-DD HH:MM:SS | ||
18 | + $end_time = 'AUTO'; // автоматически рассчитать системой или ставим дату и время в формате YYYY-MM-DD HH:MM:SS | ||
19 | + $rate = 1; // скорость отправки сообщений (1 = 1 смс минута). Одиночные СМС сообщения отправляются всегда с максимальной скоростью. | ||
20 | + $lifetime = 4; // срок жизни сообщения 4 часа | ||
21 | + | ||
22 | + $source = 'rukzachok'; // Alfaname | ||
23 | + $recipient = $tel; | ||
24 | + $user = '380674064008'; | ||
25 | + $password = 'smsartweb2012'; | ||
26 | + | ||
27 | + $myXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; | ||
28 | + $myXML .= "<request>"; | ||
29 | + $myXML .= "<operation>SENDSMS</operation>"; | ||
30 | + $myXML .= ' <message start_time="' . $start_time . '" end_time="' . $end_time . '" lifetime="' . $lifetime . '" rate="' . $rate . '" desc="' . $description . '" source="' . $source . '">' . "\n"; | ||
31 | + $myXML .= " <body>" . $text . "</body>"; | ||
32 | + $myXML .= " <recipient>" . $recipient . "</recipient>"; | ||
33 | + $myXML .= "</message>"; | ||
34 | + $myXML .= "</request>"; | ||
35 | + | ||
36 | + $ch = curl_init(); | ||
37 | + curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $password); | ||
38 | + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); | ||
39 | + curl_setopt($ch, CURLOPT_POST, 1); | ||
40 | + curl_setopt($ch, CURLOPT_URL, 'http://sms-fly.com/api/api.php'); | ||
41 | + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml", "Accept: text/xml")); | ||
42 | + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | ||
43 | + curl_setopt($ch, CURLOPT_POSTFIELDS, $myXML); | ||
44 | + $response = curl_exec($ch); | ||
45 | + curl_close($ch); | ||
46 | + | ||
47 | + return $response; | ||
48 | + } | ||
49 | +} |
1 | +++ a/components/artboximage/ArtboxImage.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\components\artboximage; | ||
4 | + | ||
5 | + use yii\base\Component; | ||
6 | + use yii\base\ErrorException; | ||
7 | + use yii\image\drivers\Image; | ||
8 | + | ||
9 | + class ArtboxImage extends Component | ||
10 | + { | ||
11 | + /** | ||
12 | + * @var string $driver GD, Imagick ... | ||
13 | + */ | ||
14 | + public $driver; | ||
15 | + | ||
16 | + public $presets = []; | ||
17 | + | ||
18 | + /** | ||
19 | + * File path to image locations | ||
20 | + * | ||
21 | + * @var string $rootPath | ||
22 | + */ | ||
23 | + public $rootPath; | ||
24 | + | ||
25 | + /** | ||
26 | + * Web path to image locations | ||
27 | + * | ||
28 | + * @var string $rootUrl | ||
29 | + */ | ||
30 | + public $rootUrl; | ||
31 | + | ||
32 | + public $extensions = [ | ||
33 | + 'jpg' => 'jpeg', | ||
34 | + 'jpeg' => 'jpeg', | ||
35 | + 'png' => 'png', | ||
36 | + 'gif' => 'gif', | ||
37 | + 'bmp' => 'bmp', | ||
38 | + ]; | ||
39 | + | ||
40 | + /** | ||
41 | + * Try to load image and prepare it to manipulation. | ||
42 | + * | ||
43 | + * @param null|string $file | ||
44 | + * @param null|string $driver | ||
45 | + * | ||
46 | + * @return \yii\image\drivers\Image | ||
47 | + * @throws \yii\base\ErrorException | ||
48 | + */ | ||
49 | + public function load($file = null, $driver = null) | ||
50 | + { | ||
51 | + if (empty( $file ) || !realpath($file)) { | ||
52 | + throw new ErrorException('File name can not be empty and exists'); | ||
53 | + } | ||
54 | + return Image::factory($file, $driver ? $driver : $this->driver); | ||
55 | + } | ||
56 | + } | ||
0 | \ No newline at end of file | 57 | \ No newline at end of file |
1 | +++ a/components/artboximage/ArtboxImageHelper.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\components\artboximage; | ||
4 | + | ||
5 | + use Yii; | ||
6 | + use yii\base\Object; | ||
7 | + use yii\helpers\Html; | ||
8 | + | ||
9 | + class ArtboxImageHelper extends Object | ||
10 | + { | ||
11 | + | ||
12 | + /** | ||
13 | + * @var ArtboxImage $imageDriver | ||
14 | + */ | ||
15 | + private static $imageDriver; | ||
16 | + | ||
17 | + /** | ||
18 | + * @var array $presets | ||
19 | + */ | ||
20 | + private static $presets; | ||
21 | + | ||
22 | + /** | ||
23 | + * Get image manipulation driver | ||
24 | + * | ||
25 | + * @return \common\components\artboximage\ArtboxImage | ||
26 | + */ | ||
27 | + public static function getDriver() | ||
28 | + { | ||
29 | + if (empty( self::$imageDriver )) { | ||
30 | + self::$imageDriver = Yii::$app->get('artboximage'); | ||
31 | + } | ||
32 | + return self::$imageDriver; | ||
33 | + } | ||
34 | + | ||
35 | + /** | ||
36 | + * Get named preset from driver preset list. | ||
37 | + * | ||
38 | + * @param string $preset | ||
39 | + * | ||
40 | + * @return array|null | ||
41 | + */ | ||
42 | + public static function getPreset($preset) | ||
43 | + { | ||
44 | + if (empty( self::$presets )) { | ||
45 | + self::$presets = self::getDriver()->presets; | ||
46 | + } | ||
47 | + return empty( self::$presets[ $preset ] ) ? null : self::$presets[ $preset ]; | ||
48 | + } | ||
49 | + | ||
50 | + /** | ||
51 | + * Get image HTML for image | ||
52 | + * | ||
53 | + * @param string $file | ||
54 | + * @param array|string $preset | ||
55 | + * @param array $imgOptions | ||
56 | + * | ||
57 | + * @see Html::img() | ||
58 | + * @return string | ||
59 | + */ | ||
60 | + public static function getImage($file, $preset, $imgOptions = []) | ||
61 | + { | ||
62 | + $preset_alias = is_array($preset) ? array_keys($preset)[ 0 ] : null; | ||
63 | + return Html::img(self::getImageSrc($file, $preset, $preset_alias), $imgOptions); | ||
64 | + } | ||
65 | + | ||
66 | + /** | ||
67 | + * Get src for image | ||
68 | + * | ||
69 | + * @param string $file | ||
70 | + * @param string $preset | ||
71 | + * @param null|string $preset_alias | ||
72 | + * | ||
73 | + * @return bool|string | ||
74 | + */ | ||
75 | + public static function getImageSrc($file, $preset, $preset_alias = null) | ||
76 | + { | ||
77 | + if (is_string($preset)) { | ||
78 | + $preset_alias = $preset; | ||
79 | + $preset = self::getPreset($preset); | ||
80 | + } | ||
81 | + if (empty( $preset ) || empty( $preset_alias )) { | ||
82 | + return $file; | ||
83 | + } | ||
84 | + | ||
85 | + $filePath = self::getPathFromUrl($file); | ||
86 | + if (!file_exists($filePath) || !preg_match( | ||
87 | + '#^(.*)\.(' . self::getExtensionsRegexp() . ')$#', | ||
88 | + $file, | ||
89 | + $matches | ||
90 | + ) | ||
91 | + ) { | ||
92 | + return $file; | ||
93 | + } | ||
94 | + return self::getPresetUrl($filePath, $preset, $preset_alias); | ||
95 | + } | ||
96 | + | ||
97 | + /** | ||
98 | + * Replace web path with file path | ||
99 | + * | ||
100 | + * @param string $url | ||
101 | + * | ||
102 | + * @return string | ||
103 | + */ | ||
104 | + private static function getPathFromUrl($url) | ||
105 | + { | ||
106 | + return substr_replace($url, self::getDriver()->rootPath, 0, strlen(self::getDriver()->rootUrl)); | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Replace file path with web path | ||
111 | + * | ||
112 | + * @param string $path | ||
113 | + * | ||
114 | + * @return string | ||
115 | + */ | ||
116 | + private static function getUrlFromPath($path) | ||
117 | + { | ||
118 | + return substr_replace($path, self::getDriver()->rootUrl, 0, strlen(self::getDriver()->rootPath)); | ||
119 | + } | ||
120 | + | ||
121 | + /** | ||
122 | + * Get formatted file url or create it if not exist | ||
123 | + * | ||
124 | + * @param string $filePath | ||
125 | + * @param array $preset | ||
126 | + * @param string $preset_alias | ||
127 | + * | ||
128 | + * @return bool|string | ||
129 | + */ | ||
130 | + private static function getPresetUrl($filePath, $preset, $preset_alias) | ||
131 | + { | ||
132 | + $pathinfo = pathinfo($filePath); | ||
133 | + $presetPath = $pathinfo[ 'dirname' ] . '/styles/' . strtolower($preset_alias); | ||
134 | + $presetFilePath = $presetPath . '/' . $pathinfo[ 'basename' ]; | ||
135 | + $presetUrl = self::getUrlFromPath($presetFilePath); | ||
136 | + if (file_exists($presetFilePath)) { | ||
137 | + return $presetUrl; | ||
138 | + } | ||
139 | + if (!file_exists($presetPath)) { | ||
140 | + @mkdir($presetPath, 0777, true); | ||
141 | + } | ||
142 | + $output = self::createPresetImage($filePath, $preset, $preset_alias); | ||
143 | + if (!empty( $output )) { | ||
144 | + $f = fopen($presetFilePath, 'w'); | ||
145 | + fwrite($f, $output); | ||
146 | + fclose($f); | ||
147 | + return $presetUrl; | ||
148 | + } | ||
149 | + return false; | ||
150 | + } | ||
151 | + | ||
152 | + /** | ||
153 | + * Create formatted image. | ||
154 | + * Available manipulations: | ||
155 | + * * resize | ||
156 | + * * flip | ||
157 | + * | ||
158 | + * @param string $filePath | ||
159 | + * @param array $preset | ||
160 | + * @param string $preset_alias | ||
161 | + * | ||
162 | + * @return string | ||
163 | + */ | ||
164 | + private static function createPresetImage($filePath, $preset, $preset_alias) | ||
165 | + { | ||
166 | + $image = self::getDriver() | ||
167 | + ->load($filePath); | ||
168 | + foreach ($preset as $action => $data) { | ||
169 | + switch ($action) { | ||
170 | + case 'resize': | ||
171 | + $width = empty( $data[ 'width' ] ) ? null : $data[ 'width' ]; | ||
172 | + $height = empty( $data[ 'height' ] ) ? null : $data[ 'height' ]; | ||
173 | + $master = empty( $data[ 'master' ] ) ? null : $data[ 'master' ]; | ||
174 | + $image->resize($width, $height, $master); | ||
175 | + break; | ||
176 | + case 'flip': | ||
177 | + $image->flip(@$data[ 'direction' ]); | ||
178 | + break; | ||
179 | + default: | ||
180 | + break; | ||
181 | + } | ||
182 | + } | ||
183 | + return $image->render(); | ||
184 | + } | ||
185 | + | ||
186 | + /** | ||
187 | + * Get extensions regexp | ||
188 | + * | ||
189 | + * @return string regexp | ||
190 | + */ | ||
191 | + private static function getExtensionsRegexp() | ||
192 | + { | ||
193 | + $keys = array_keys(self::getDriver()->extensions); | ||
194 | + return '(?i)' . join('|', $keys); | ||
195 | + } | ||
196 | + } | ||
0 | \ No newline at end of file | 197 | \ No newline at end of file |
1 | +++ a/components/artboxtree/ArtboxTreeBehavior.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree; | ||
4 | + | ||
5 | +use yii\base\Behavior; | ||
6 | +use yii\base\Exception; | ||
7 | +use yii\base\NotSupportedException; | ||
8 | +use yii\db\ActiveRecord; | ||
9 | +use yii\db\Expression; | ||
10 | + | ||
11 | +class ArtboxTreeBehavior extends Behavior { | ||
12 | + | ||
13 | + /** @var ActiveRecord $owner */ | ||
14 | + public $owner; | ||
15 | + | ||
16 | + public $keyNameId; | ||
17 | + public $keyNameParentId = 'parent_id'; | ||
18 | + public $keyNameGroup = 'group'; | ||
19 | + public $keyNamePath = 'path_int'; | ||
20 | + public $keyNameDepth = 'depth'; // @todo -> $keyNameDepth; | ||
21 | + public $primaryKeyMode = true; | ||
22 | + | ||
23 | + /** | ||
24 | + * @var string | ||
25 | + */ | ||
26 | + public $delimiter = '|'; | ||
27 | + | ||
28 | + /** | ||
29 | + * @var ActiveRecord|self|null | ||
30 | + */ | ||
31 | + protected $entity; | ||
32 | + | ||
33 | + /** | ||
34 | + * @param ActiveRecord $owner | ||
35 | + * @throws Exception | ||
36 | + */ | ||
37 | + public function attach($owner) | ||
38 | + { | ||
39 | + parent::attach($owner); | ||
40 | + if ($this->keyNameId === null) { | ||
41 | + $primaryKey = $owner->primaryKey(); | ||
42 | + if (!isset($primaryKey[0])) { | ||
43 | + throw new Exception('"' . $owner->className() . '" must have a primary key.'); | ||
44 | + } | ||
45 | + $this->keyNameId = $primaryKey[0]; | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + public function events() | ||
50 | + { | ||
51 | + return [ | ||
52 | + // @todo Use beforeSave for automatic set MP-params | ||
53 | + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', | ||
54 | + ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert', | ||
55 | + ]; | ||
56 | + } | ||
57 | + | ||
58 | + /* | ||
59 | + * Main methods | ||
60 | + */ | ||
61 | + | ||
62 | + /* | ||
63 | + * get one parent | ||
64 | + * use AL-method | ||
65 | + */ | ||
66 | + public function getParent() { | ||
67 | + return $this->getParentAL(); | ||
68 | + } | ||
69 | + | ||
70 | + /* | ||
71 | + * get all parents | ||
72 | + * use MP-method | ||
73 | + */ | ||
74 | + public function getParents() { | ||
75 | + return $this->getParentsMP(); | ||
76 | + } | ||
77 | + | ||
78 | + /* | ||
79 | + * get one-level children items | ||
80 | + * use AL-method | ||
81 | + */ | ||
82 | + public function getChildren() { | ||
83 | + return $this->getChildrenAL(); | ||
84 | + } | ||
85 | + | ||
86 | + /* | ||
87 | + * get all-level children items | ||
88 | + * use MP-method | ||
89 | + */ | ||
90 | + public function getAllChildren($depth = null, $where = [], $with = null) { | ||
91 | + return $this->getAllChildrenMP($depth, $where, $with); | ||
92 | + } | ||
93 | + | ||
94 | + /* | ||
95 | + * get all-level children items | ||
96 | + * use MP-method | ||
97 | + */ | ||
98 | + public function getAllChildrenTree($depth = null, $where = [], $with = null) { | ||
99 | + $query = $this->getAllChildrenMP($depth, $where, $with); | ||
100 | + return $this->buildTree($query->all(), $this->owner->getAttribute($this->keyNameId)); | ||
101 | + } | ||
102 | + | ||
103 | + // @todo Check algorytm | ||
104 | + public function buildTree(array $data, $parentId = 0) { | ||
105 | + $result = []; | ||
106 | + foreach ($data as $key => $element) { | ||
107 | + if ($element->getAttribute($this->keyNameParentId) == $parentId) { | ||
108 | + unset($data[$key]); | ||
109 | + $children = $this->buildTree($data, $element->getAttribute($this->keyNameId)); | ||
110 | + $result[] = [ | ||
111 | + 'item' => $element, | ||
112 | + 'children' => $children | ||
113 | + ]; | ||
114 | + } | ||
115 | + } | ||
116 | + return $result; | ||
117 | + } | ||
118 | + | ||
119 | + | ||
120 | + /* | ||
121 | + * ================================ | ||
122 | + * MP-methods | ||
123 | + * ================================ | ||
124 | + */ | ||
125 | + | ||
126 | + /* | ||
127 | + * Full-path (use MP-method) | ||
128 | + */ | ||
129 | + public function getParentsMP($depth = null) { | ||
130 | + $tableName = $this->owner->tableName(); | ||
131 | + $path = $this->owner->getAttribute($this->keyNamePath); | ||
132 | + $query = $this->owner->find() | ||
133 | + ->andWhere(['<@', "{$tableName}.[[{$this->keyNamePath}]]", $path]); | ||
134 | + if ($depth > 0) { | ||
135 | + $query->andWhere(['>=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) - $depth]); | ||
136 | + } | ||
137 | + $query->andWhere(['<', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth)]); | ||
138 | + | ||
139 | + $orderBy = []; | ||
140 | + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC; | ||
141 | + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC; | ||
142 | + | ||
143 | + $query | ||
144 | + ->andWhere($this->groupWhere()) | ||
145 | + ->addOrderBy($orderBy); | ||
146 | + $query->multiple = true; | ||
147 | + | ||
148 | + return $query; | ||
149 | + } | ||
150 | + /*public function getParentsMP($depth = null) { | ||
151 | + $path = $this->getParentPath(); | ||
152 | + if ($path !== null) { | ||
153 | + $paths = explode(',', trim($path, '{}')); | ||
154 | + if (!$this->primaryKeyMode) { | ||
155 | + $path = null; | ||
156 | + $paths = array_map( | ||
157 | + function ($value) use (&$path) { | ||
158 | + return $path = ($path !== null ? $path . ',' : '') . $value; | ||
159 | + }, | ||
160 | + $paths | ||
161 | + ); | ||
162 | + } | ||
163 | + if ($depth !== null) { | ||
164 | + $paths = array_slice($paths, -$depth); | ||
165 | + } | ||
166 | + } else { | ||
167 | + $paths = []; | ||
168 | + } | ||
169 | + | ||
170 | + $tableName = $this->owner->tableName(); | ||
171 | + if ($this->primaryKeyMode) { | ||
172 | + $condition[] = ["{$tableName}.[[{$this->keyNameId}]]" => $paths]; | ||
173 | + } else { | ||
174 | + $condition[] = ["{$tableName}.[[{$this->keyNamePath}]]" => $paths]; | ||
175 | + } | ||
176 | + | ||
177 | + $query = $this->owner->find() | ||
178 | + ->andWhere($condition) | ||
179 | + ->andWhere($this->groupWhere()) | ||
180 | + ->addOrderBy(["{$tableName}.[[{$this->keyNamePath}]]" => SORT_ASC]); | ||
181 | + $query->multiple = true; | ||
182 | + | ||
183 | + return $query; | ||
184 | + }*/ | ||
185 | + | ||
186 | + /** | ||
187 | + * @param bool $asArray = false | ||
188 | + * @return null|string|array | ||
189 | + */ | ||
190 | + public function getParentPath($asArray = false) | ||
191 | + { | ||
192 | + return static::getParentPathInternal($this->owner->getAttribute($this->keyNamePath), $asArray); | ||
193 | + } | ||
194 | + /** | ||
195 | + * @return array | ||
196 | + */ | ||
197 | + protected function groupWhere() | ||
198 | + { | ||
199 | + $tableName = $this->owner->tableName(); | ||
200 | + if ($this->keyNameGroup === null) { | ||
201 | + return []; | ||
202 | + } else { | ||
203 | + return ["{$tableName}.[[{$this->keyNameGroup}]]" => $this->owner->getAttribute($this->keyNameGroup)]; | ||
204 | + } | ||
205 | + } | ||
206 | + | ||
207 | + | ||
208 | + public function getAllChildrenMP($depth = null, $where = [], $with = null) | ||
209 | + { | ||
210 | + $tableName = $this->owner->tableName(); | ||
211 | + $path = $this->owner->getAttribute($this->keyNamePath); | ||
212 | + $query = $this->owner->find() | ||
213 | + ->andWhere(['@>', "{$tableName}.[[{$this->keyNamePath}]]", $path]); | ||
214 | + | ||
215 | + if ($depth > 0) { | ||
216 | + $query->andWhere(['<=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) + $depth]); | ||
217 | + } | ||
218 | + | ||
219 | + $orderBy = []; | ||
220 | + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC; | ||
221 | + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC; | ||
222 | + | ||
223 | + if ($where) { | ||
224 | + $query->andWhere($where); | ||
225 | + } | ||
226 | + if ($with) { | ||
227 | + $query->with($with); | ||
228 | + } | ||
229 | + | ||
230 | + $query | ||
231 | + ->andWhere($this->groupWhere()) | ||
232 | + ->addOrderBy($orderBy); | ||
233 | + $query->multiple = true; | ||
234 | + | ||
235 | + return $query; | ||
236 | + } | ||
237 | + | ||
238 | + /* | ||
239 | + * ================================ | ||
240 | + * AL methods | ||
241 | + * ================================ | ||
242 | + */ | ||
243 | + | ||
244 | + /* | ||
245 | + * Parent entity (use AL-method) | ||
246 | + * @return \yii\db\ActiveRecord | ||
247 | + */ | ||
248 | + public function getParentAL() { | ||
249 | + $parent_id = $this->owner->getAttribute($this->keyNameParentId); | ||
250 | + if (empty($parent_id)) | ||
251 | + return null; | ||
252 | + | ||
253 | + $where = [$this->keyNameId => $parent_id]; | ||
254 | + if ($this->keyNameGroup) { | ||
255 | + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); | ||
256 | + } | ||
257 | + | ||
258 | + return $this->owner->find()->where($where)->one(); | ||
259 | + } | ||
260 | + | ||
261 | + /* | ||
262 | + * Get parents by AL-method | ||
263 | + * @return array | ||
264 | + */ | ||
265 | + public function getParentsAL() { | ||
266 | + $parent_id = $this->owner->getAttribute($this->keyNameParentId); | ||
267 | + if ($parent_id == 0) { | ||
268 | + return []; | ||
269 | + } | ||
270 | + | ||
271 | + $parent = $this->owner; | ||
272 | + $parents = []; | ||
273 | + while(true) { | ||
274 | + $parent = $parent->getParentAL(); | ||
275 | + if (is_null($parent)) | ||
276 | + break; | ||
277 | + $parents[] = $parent; | ||
278 | + } | ||
279 | + | ||
280 | + return array_reverse($parents); | ||
281 | + } | ||
282 | + | ||
283 | + /* | ||
284 | + * Children entities (one-step) (use AL-method) | ||
285 | + * @return ActiveQuery | ||
286 | + */ | ||
287 | + public function getChildrenAL() { | ||
288 | + $where = [$this->keyNameParentId => $this->owner->getAttribute($this->keyNameId)]; | ||
289 | + if ($this->keyNameGroup) { | ||
290 | + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); | ||
291 | + } | ||
292 | + return $this->owner->find()->where($where); | ||
293 | + } | ||
294 | + | ||
295 | + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
296 | + | ||
297 | + /** | ||
298 | + * @param array $changedAttributes | ||
299 | + * @throws Exception | ||
300 | + */ | ||
301 | + protected function rebuildChildren($changedAttributes) | ||
302 | + { | ||
303 | + $path = isset($changedAttributes[$this->keyNamePath]) ? $changedAttributes[$this->keyNamePath] : $this->owner->getAttribute($this->keyNamePath); | ||
304 | + $update = []; | ||
305 | + $condition = [ | ||
306 | + 'and', | ||
307 | + ['@>', "[[{$this->keyNamePath}]]", $path, false], | ||
308 | + ]; | ||
309 | + if ($this->keyNameGroup !== null) { | ||
310 | + $group = isset($changedAttributes[$this->keyNameGroup]) ? $changedAttributes[$this->keyNameGroup] : $this->owner->getAttribute($this->keyNameGroup); | ||
311 | + $condition[] = [$this->keyNameGroup => $group]; | ||
312 | + } | ||
313 | + $params = []; | ||
314 | + | ||
315 | + if (isset($changedAttributes[$this->keyNamePath])) { | ||
316 | + $substringExpr = $this->substringExpression( | ||
317 | + "[[{$this->keyNamePath}]]", | ||
318 | + 'array_length(:pathOld) + 1', | ||
319 | + "array_length([[{$this->keyNamePath}]]) - array_length(:pathOld)" | ||
320 | + ); | ||
321 | + $update[$this->keyNamePath] = new Expression($this->concatExpression([':pathNew', $substringExpr])); | ||
322 | + $params[':pathOld'] = $path; | ||
323 | + $params[':pathNew'] = $this->owner->getAttribute($this->keyNamePath); | ||
324 | + } | ||
325 | + | ||
326 | + if ($this->keyNameGroup !== null && isset($changedAttributes[$this->keyNameGroup])) { | ||
327 | + $update[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); | ||
328 | + } | ||
329 | + | ||
330 | + if ($this->keyNameDepth !== null && isset($changedAttributes[$this->keyNameDepth])) { | ||
331 | + $delta = $this->owner->getAttribute($this->keyNameDepth) - $changedAttributes[$this->keyNameDepth]; | ||
332 | + $update[$this->keyNameDepth] = new Expression("[[{$this->keyNameDepth}]]" . sprintf('%+d', $delta)); | ||
333 | + } | ||
334 | + if (!empty($update)) { | ||
335 | + $this->owner->updateAll($update, $condition, $params); | ||
336 | + } | ||
337 | + } | ||
338 | + | ||
339 | + /** | ||
340 | + * @param string $path | ||
341 | + * @param string $delimiter | ||
342 | + * @param bool $asArray = false | ||
343 | + * @return null|string|array | ||
344 | + */ | ||
345 | + protected static function getParentPathInternal($path, $asArray = false) | ||
346 | + { | ||
347 | + $path = explode(',', trim($path, '{}')); | ||
348 | + array_pop($path); | ||
349 | + if ($asArray) { | ||
350 | + return $path; | ||
351 | + } | ||
352 | + return count($path) > 0 ? implode(',', $path) : null; | ||
353 | + } | ||
354 | + | ||
355 | + protected function toLike($path) { | ||
356 | + return strtr($path . ',', ['%' => '\%', '_' => '\_', '\\' => '\\\\']) . '%'; | ||
357 | + } | ||
358 | + | ||
359 | + protected function concatExpression($items) | ||
360 | + { | ||
361 | + if ($this->owner->getDb()->driverName === 'sqlite' || $this->owner->getDb()->driverName === 'pgsql') { | ||
362 | + return implode(' || ', $items); | ||
363 | + } | ||
364 | + return 'CONCAT(' . implode(',', $items) . ')'; | ||
365 | + } | ||
366 | + | ||
367 | + protected function substringExpression($string, $from, $length) | ||
368 | + { | ||
369 | + if ($this->owner->getDb()->driverName === 'sqlite') { | ||
370 | + return "SUBSTR({$string}, {$from}, {$length})"; | ||
371 | + } | ||
372 | + return "SUBSTRING({$string}, {$from}, {$length})"; | ||
373 | + } | ||
374 | + | ||
375 | + // ======================================================= | ||
376 | + public function afterInsert() { | ||
377 | + $this->withSave(); | ||
378 | + $this->owner->updateAttributes([$this->keyNamePath => $this->owner->getAttribute($this->keyNamePath), $this->keyNameDepth => $this->owner->getAttribute($this->keyNameDepth)]); | ||
379 | + } | ||
380 | + | ||
381 | + public function beforeUpdate() | ||
382 | + { | ||
383 | + if ($this->owner->getIsNewRecord()) { | ||
384 | + throw new NotSupportedException('Method "' . $this->owner->className() . '::insert" is not supported for inserting new entitys.'); | ||
385 | + } | ||
386 | + $this->withSave(); | ||
387 | + } | ||
388 | + | ||
389 | + protected function withSave() { | ||
390 | + $id = $this->owner->getAttribute($this->keyNameId); | ||
391 | + $parent_id = $this->owner->getAttribute($this->keyNameParentId); | ||
392 | + | ||
393 | + if (is_null($parent_id)) { | ||
394 | + $parent_id = 0; | ||
395 | + } | ||
396 | + | ||
397 | + // check parent_id value is changed! | ||
398 | + /*if ($this->owner->getOldAttribute($this->keyNameParentId) == $parent_id) { | ||
399 | + return; | ||
400 | + }*/ | ||
401 | + | ||
402 | + // rebuild parents entities | ||
403 | + if ($parent_id == 0) { | ||
404 | + $depth = 0; | ||
405 | + $path = [intval($id)]; | ||
406 | + } else { | ||
407 | + $parents = $this->getParentsAL(); | ||
408 | + $path = []; | ||
409 | + $depth = 0; | ||
410 | + foreach ($parents as $entity) { | ||
411 | + $path[] = $entity->getAttribute($this->keyNameId); | ||
412 | + $depth++; | ||
413 | + } | ||
414 | + $path[] = intval($id); | ||
415 | + } | ||
416 | + | ||
417 | + $path = '{'. implode(',', $path) .'}'; | ||
418 | + | ||
419 | + // rebuild children entities (recurcive) | ||
420 | +// $this->rebuildChildren([ | ||
421 | +// $this->keyNamePath => $path | ||
422 | +// ]); | ||
423 | + | ||
424 | + $this->owner->setAttribute($this->keyNamePath, $path); | ||
425 | +// $this->owner->setAttribute($this->keyNamePath, $path); | ||
426 | + $this->owner->setAttribute($this->keyNameDepth, $depth); | ||
427 | + } | ||
428 | + | ||
429 | + public function recursiveRebuildChildren() { | ||
430 | + $children = $this->getChildrenAL()->all(); | ||
431 | + $root_path = explode(',', $this->owner->getAttribute($this->keyNamePath)); | ||
432 | + $root_depth = $this->owner->getAttribute($this->keyNameDepth); | ||
433 | + | ||
434 | + /** @var $child ActiveRecord */ | ||
435 | + foreach ($children as $child) { | ||
436 | + $path = $root_path; | ||
437 | + $path[] = $child->getAttribute($this->keyNameId); | ||
438 | + $depth = $root_depth + 1; | ||
439 | + | ||
440 | + $child->recursiveRebuildChildren(); | ||
441 | + } | ||
442 | + } | ||
443 | +} | ||
0 | \ No newline at end of file | 444 | \ No newline at end of file |
1 | +++ a/components/artboxtree/ArtboxTreeHelper.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree; | ||
4 | + | ||
5 | +use yii\base\Object; | ||
6 | +use yii\helpers\ArrayHelper; | ||
7 | + | ||
8 | +class ArtboxTreeHelper extends Object { | ||
9 | + public static function treeMap($tree, $from, $to, $symbol = '.') | ||
10 | + { | ||
11 | + $result = []; | ||
12 | + | ||
13 | + self::recursiveTreeMap($result, $tree, $from, $to, $symbol); | ||
14 | + | ||
15 | + return $result; | ||
16 | + } | ||
17 | + | ||
18 | + public static function setArrayField($path, $as_string = false) { | ||
19 | + if (is_array($path)) { | ||
20 | + if ($as_string) { | ||
21 | + foreach ($path as &$item) { | ||
22 | + $item = "'$item'"; | ||
23 | + } | ||
24 | + } | ||
25 | + $path = implode(',', $path); | ||
26 | + } | ||
27 | + return '{'. $path .'}'; | ||
28 | + } | ||
29 | + | ||
30 | + public static function getArrayField($path) { | ||
31 | + $path = trim($path, '{}'); | ||
32 | + return empty($path) ? [] : explode(',', $path); | ||
33 | + } | ||
34 | + | ||
35 | + protected static function recursiveTreeMap(&$result, $tree, $from, $to, $symbol = '–') { | ||
36 | + foreach ($tree as $item) { | ||
37 | + $element = $item['item']; | ||
38 | + $key = ArrayHelper::getValue($element, $from); | ||
39 | + $value = ArrayHelper::getValue($element, $to); | ||
40 | + $row = str_repeat($symbol, $element->depth+1) . $value; | ||
41 | + $result[$key] = $row; | ||
42 | + if (!empty($item['children'])) { | ||
43 | + self::recursiveTreeMap($result, $item['children'], $from, $to, $symbol); | ||
44 | + } | ||
45 | + } | ||
46 | + } | ||
47 | +} | ||
0 | \ No newline at end of file | 48 | \ No newline at end of file |
1 | +++ a/components/artboxtree/ArtboxTreeQueryTrait.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\components\artboxtree; | ||
4 | + | ||
5 | + trait ArtboxTreeQueryTrait | ||
6 | + { | ||
7 | + | ||
8 | + public static $cacheTree = []; | ||
9 | + | ||
10 | + /** @var \yii\db\ActiveQuery $this */ | ||
11 | + static $model; | ||
12 | + | ||
13 | + /* | ||
14 | + * @return \yii\db\ActiveQuery | ||
15 | + */ | ||
16 | + private function getModel() | ||
17 | + { | ||
18 | + if(empty( self::$model )) { | ||
19 | + $class = $this->modelClass; | ||
20 | + self::$model = new $class; | ||
21 | + } | ||
22 | + return self::$model; | ||
23 | + } | ||
24 | + | ||
25 | + public function getTree($group = NULL, $with = NULL) | ||
26 | + { | ||
27 | + $model = $this->getModel(); | ||
28 | + if($group !== NULL) { | ||
29 | + $this->andWhere([ $model->keyNameGroup => $group ]); | ||
30 | + } | ||
31 | + if($with) { | ||
32 | + $this->with($with); | ||
33 | + } | ||
34 | + $data = $this->all(); | ||
35 | + if(empty( $data )) { | ||
36 | + return []; | ||
37 | + } | ||
38 | + | ||
39 | + return $this->buildTree($data); | ||
40 | + } | ||
41 | + | ||
42 | + private function recursiveRebuild($tree, $parentPath = NULL, $depth = 0) | ||
43 | + { | ||
44 | + $model = $this->getModel(); | ||
45 | + | ||
46 | + foreach($tree as $row) { | ||
47 | + $path = ( is_null($parentPath) ? '' : $parentPath . $model->delimiter ) . $row[ 'item' ]->getAttribute($model->keyNameId); | ||
48 | + $row[ 'item' ]->setAttribute($model->keyNamePath, $path); | ||
49 | + $row[ 'item' ]->setAttribute($model->keyNameDepth, $depth); | ||
50 | + $row[ 'item' ]->save(); | ||
51 | + if(!empty( $row[ 'children' ] )) { | ||
52 | + $this->recursiveRebuild($row[ 'children' ], $path, $depth + 1); | ||
53 | + } | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * @param int $group | ||
59 | + */ | ||
60 | + public function rebuildMP($group, $with = NULL) | ||
61 | + { | ||
62 | + $tree = $this->getTree($group, $with); | ||
63 | + | ||
64 | + $this->recursiveRebuild($tree); | ||
65 | + } | ||
66 | + | ||
67 | + protected function buildTree(array $data, $parentId = 0) | ||
68 | + { | ||
69 | + $model = $this->getModel(); | ||
70 | + | ||
71 | + $result = []; | ||
72 | + foreach($data as $element) { | ||
73 | + if($element[ $model->keyNameParentId ] == $parentId) { | ||
74 | + $children = $this->buildTree($data, $element[ $model->keyNameId ]); | ||
75 | + $result[] = [ | ||
76 | + 'item' => $element, | ||
77 | + 'children' => $children, | ||
78 | + ]; | ||
79 | + } | ||
80 | + } | ||
81 | + return $result; | ||
82 | + } | ||
83 | + | ||
84 | + public function normalizeTreeData(array $data, $parentId = NULL) | ||
85 | + { | ||
86 | + $model = $this->getModel(); | ||
87 | + | ||
88 | + $result = []; | ||
89 | + foreach($data as $element) { | ||
90 | + if($element[ $model->keyNameParentId ] == $parentId) { | ||
91 | + $result[] = $element; | ||
92 | + $children = $this->normalizeTreeData($data, $element[ $model->keyNameId ]); | ||
93 | + if($children) { | ||
94 | + $result = array_merge($result, $children); | ||
95 | + } | ||
96 | + } | ||
97 | + } | ||
98 | + return $result; | ||
99 | + } | ||
100 | + } | ||
0 | \ No newline at end of file | 101 | \ No newline at end of file |
1 | +++ a/components/artboxtree/ArtboxTreeWidget.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree; | ||
4 | + | ||
5 | +use Yii; | ||
6 | +use yii\base\Widget; | ||
7 | +use yii\i18n\Formatter; | ||
8 | +use yii\base\InvalidConfigException; | ||
9 | + | ||
10 | +class ArtboxTreeWidget extends Widget | ||
11 | +{ | ||
12 | + | ||
13 | + /** | ||
14 | + * @var \yii\data\DataProviderInterface the data provider for the view. This property is required. | ||
15 | + */ | ||
16 | + public $dataProvider; | ||
17 | + | ||
18 | + /** | ||
19 | + * @var string | ||
20 | + */ | ||
21 | + public $keyNameId = 'id'; | ||
22 | + | ||
23 | + /** | ||
24 | + * @var string | ||
25 | + */ | ||
26 | + public $keyNameParentId = 'parent_id'; | ||
27 | + | ||
28 | + /** | ||
29 | + * @var integer or null | ||
30 | + */ | ||
31 | + public $maxLevel = null; | ||
32 | + | ||
33 | + /** | ||
34 | + * @var integer | ||
35 | + */ | ||
36 | + public $rootParentId = 0; | ||
37 | + | ||
38 | + /** | ||
39 | + * @var string | ||
40 | + */ | ||
41 | + public $emptyResult; | ||
42 | + | ||
43 | + /** | ||
44 | + * @var boolean include the CSS and JS files. Default is true. | ||
45 | + * If this is set false, you are responsible to explicitly include the necessary CSS and JS files in your page. | ||
46 | + */ | ||
47 | + public $assetBundle; | ||
48 | + | ||
49 | + /** | ||
50 | + * @var array|Formatter the formatter used to format model attribute values into displayable texts. | ||
51 | + * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]] | ||
52 | + * instance. If this property is not set, the "formatter" application component will be used. | ||
53 | + */ | ||
54 | + public $formatter; | ||
55 | + | ||
56 | + /** | ||
57 | + * Init the widget object. | ||
58 | + */ | ||
59 | + public function init() | ||
60 | + { | ||
61 | + parent::init(); | ||
62 | + if ($this->dataProvider === null) { | ||
63 | + throw new InvalidConfigException('The "dataProvider" property must be set.'); | ||
64 | + } | ||
65 | + if ($this->keyNameId === null) { | ||
66 | + throw new InvalidConfigException('The "keyNameId" property must be set.'); | ||
67 | + } | ||
68 | + if ($this->formatter == null) { | ||
69 | + $this->formatter = Yii::$app->getFormatter(); | ||
70 | + } elseif (is_array($this->formatter)) { | ||
71 | + $this->formatter = Yii::createObject($this->formatter); | ||
72 | + } | ||
73 | + if (!$this->formatter instanceof Formatter) { | ||
74 | + throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); | ||
75 | + } | ||
76 | + } | ||
77 | + | ||
78 | + /** | ||
79 | + * Runs the widget. | ||
80 | + */ | ||
81 | + public function run() | ||
82 | + { | ||
83 | + if (!empty($this->assetBundle) && class_exists($this->assetBundle)) { | ||
84 | + $view = $this->getView(); | ||
85 | + $assetBundle = $this->assetBundle; | ||
86 | + $assetBundle::register($view); | ||
87 | + } | ||
88 | + if ($this->dataProvider->getCount() == 0) { | ||
89 | + return $this->renderEmptyResult(); | ||
90 | + } | ||
91 | + | ||
92 | + parent::run(); | ||
93 | + } | ||
94 | + | ||
95 | + protected function renderEmptyResult() { | ||
96 | + return empty($this->emptyResult) ? Yii::t('artbox', 'TreeViewEmptyResult') : Yii::t('artbox', $this->emptyResult); | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * Normalize tree data | ||
101 | + * @param array $data | ||
102 | + * @param string $parentId | ||
103 | + * @return array | ||
104 | + */ | ||
105 | + protected function normalizeTreeData(array $data, $parentId = null) { | ||
106 | + $result = []; | ||
107 | + foreach ($data as $element) { | ||
108 | + if ($element[$this->keyNameParentId] == $parentId) { | ||
109 | + $result[] = $element; | ||
110 | + $children = $this->normalizeTreeData($data, $element[$this->keyNameId]); | ||
111 | + if ($children) { | ||
112 | + $result = array_merge($result, $children); | ||
113 | + } | ||
114 | + } | ||
115 | + } | ||
116 | + return $result; | ||
117 | + } | ||
118 | + | ||
119 | + /** | ||
120 | + * Hierarchy tree data | ||
121 | + * @param array $data | ||
122 | + * @param string $parentId | ||
123 | + * @return array | ||
124 | + */ | ||
125 | + protected function hierarchyTreeData(array $data, $parentId = null) { | ||
126 | + $result = []; | ||
127 | + foreach ($data as $element) { | ||
128 | + if ($element[$this->keyNameParentId] == $parentId) { | ||
129 | + $children = $this->hierarchyTreeData($data, $element[$this->keyNameId]); | ||
130 | + $result[] = [ | ||
131 | + 'item' => $element, | ||
132 | + 'children' => $children | ||
133 | + ]; | ||
134 | + } | ||
135 | + } | ||
136 | + return $result; | ||
137 | + } | ||
138 | +} | ||
0 | \ No newline at end of file | 139 | \ No newline at end of file |
1 | +++ a/components/artboxtree/treegrid/TreeGridColumn.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree\treegrid; | ||
4 | + | ||
5 | +use Closure; | ||
6 | +use Yii; | ||
7 | +use yii\base\Model; | ||
8 | +use yii\base\Object; | ||
9 | +use yii\data\ActiveDataProvider; | ||
10 | +use yii\db\ActiveQueryInterface; | ||
11 | +use yii\helpers\ArrayHelper; | ||
12 | +use yii\helpers\Html; | ||
13 | +use yii\helpers\Inflector; | ||
14 | + | ||
15 | +/** | ||
16 | + * Column is the base class of all [[TreeGrid]] column classes. | ||
17 | + * The code was based in: https://github.com/yiisoft/yii2/blob/master/framework/grid/DataColumn.php | ||
18 | + * | ||
19 | + * @author Leandro Gehlen <leandrogehlen@gmail.com> | ||
20 | + */ | ||
21 | +class TreeGridColumn extends Object { | ||
22 | + | ||
23 | + /** | ||
24 | + * @var TreeGrid the grid view object that owns this column. | ||
25 | + */ | ||
26 | + public $grid; | ||
27 | + | ||
28 | + /** | ||
29 | + * @var string the header cell content. Note that it will not be HTML-encoded. | ||
30 | + */ | ||
31 | + public $header; | ||
32 | + | ||
33 | + /** | ||
34 | + * @var string the footer cell content. Note that it will not be HTML-encoded. | ||
35 | + */ | ||
36 | + public $footer; | ||
37 | + | ||
38 | + /** | ||
39 | + * @var callable This is a callable that will be used to generate the content of each cell. | ||
40 | + * The signature of the function should be the following: `function ($model, $key, $index, $column)`. | ||
41 | + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered | ||
42 | + * and `$column` is a reference to the [[TreeColumn]] object. | ||
43 | + */ | ||
44 | + public $content; | ||
45 | + | ||
46 | + /** | ||
47 | + * @var boolean whether this column is visible. Defaults to true. | ||
48 | + */ | ||
49 | + public $visible = true; | ||
50 | + | ||
51 | + /** | ||
52 | + * @var array the HTML attributes for the column group tag. | ||
53 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
54 | + */ | ||
55 | + public $options = []; | ||
56 | + | ||
57 | + /** | ||
58 | + * @var array the HTML attributes for the header cell tag. | ||
59 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
60 | + */ | ||
61 | + public $headerOptions = []; | ||
62 | + | ||
63 | + /** | ||
64 | + * @var array|\Closure the HTML attributes for the data cell tag. This can either be an array of | ||
65 | + * attributes or an anonymous function ([[Closure]]) that returns such an array. | ||
66 | + * The signature of the function should be the following: `function ($model, $key, $index, $column)`. | ||
67 | + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered | ||
68 | + * and `$column` is a reference to the [[Column]] object. | ||
69 | + * A function may be used to assign different attributes to different rows based on the data in that row. | ||
70 | + * | ||
71 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
72 | + */ | ||
73 | + public $contentOptions = []; | ||
74 | + | ||
75 | + /** | ||
76 | + * @var array the HTML attributes for the footer cell tag. | ||
77 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
78 | + */ | ||
79 | + public $footerOptions = []; | ||
80 | + | ||
81 | + /** | ||
82 | + * @var string the attribute name associated with this column. When neither [[content]] nor [[value]] | ||
83 | + * is specified, the value of the specified attribute will be retrieved from each data model and displayed. | ||
84 | + * | ||
85 | + * Also, if [[label]] is not specified, the label associated with the attribute will be displayed. | ||
86 | + */ | ||
87 | + public $attribute; | ||
88 | + | ||
89 | + /** | ||
90 | + * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting | ||
91 | + * link label when sorting is enabled for this column. | ||
92 | + * If it is not set and the models provided by the GridViews data provider are instances | ||
93 | + * of [[\yii\db\ActiveRecord]], the label will be determined using [[\yii\db\ActiveRecord::getAttributeLabel()]]. | ||
94 | + * Otherwise [[\yii\helpers\Inflector::camel2words()]] will be used to get a label. | ||
95 | + */ | ||
96 | + public $label; | ||
97 | + | ||
98 | + /** | ||
99 | + * @var boolean whether the header label should be HTML-encoded. | ||
100 | + * @see label | ||
101 | + */ | ||
102 | + public $encodeLabel = true; | ||
103 | + | ||
104 | + /** | ||
105 | + * @var string|\Closure an anonymous function or a string that is used to determine the value to display in the current column. | ||
106 | + * | ||
107 | + * If this is an anonymous function, it will be called for each row and the return value will be used as the value to | ||
108 | + * display for every data model. The signature of this function should be: `function ($model, $key, $index, $column)`. | ||
109 | + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered | ||
110 | + * and `$column` is a reference to the [[DataColumn]] object. | ||
111 | + * | ||
112 | + * You may also set this property to a string representing the attribute name to be displayed in this column. | ||
113 | + * This can be used when the attribute to be displayed is different from the [[attribute]] that is used for | ||
114 | + * sorting and filtering. | ||
115 | + * | ||
116 | + * If this is not set, `$model[$attribute]` will be used to obtain the value, where `$attribute` is the value of [[attribute]]. | ||
117 | + */ | ||
118 | + public $value; | ||
119 | + | ||
120 | + /** | ||
121 | + * @var string|array in which format should the value of each data model be displayed as (e.g. `"raw"`, `"text"`, `"html"`, | ||
122 | + * `['date', 'php:Y-m-d']`). Supported formats are determined by the [[GridView::formatter|formatter]] used by | ||
123 | + * the [[GridView]]. Default format is "text" which will format the value as an HTML-encoded plain text when | ||
124 | + * [[\yii\i18n\Formatter]] is used as the [[GridView::$formatter|formatter]] of the GridView. | ||
125 | + */ | ||
126 | + public $format = 'text'; | ||
127 | + | ||
128 | + /** | ||
129 | + * Renders the header cell. | ||
130 | + */ | ||
131 | + public function renderHeaderCell() | ||
132 | + { | ||
133 | + return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions); | ||
134 | + } | ||
135 | + | ||
136 | + /** | ||
137 | + * Renders the footer cell. | ||
138 | + */ | ||
139 | + public function renderFooterCell() | ||
140 | + { | ||
141 | + return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions); | ||
142 | + } | ||
143 | + | ||
144 | + /** | ||
145 | + * Renders a data cell. | ||
146 | + * @param mixed $model the data model being rendered | ||
147 | + * @param mixed $key the key associated with the data model | ||
148 | + * @param integer $index the zero-based index of the data item among the item array returned by [[GridView::dataProvider]]. | ||
149 | + * @return string the rendering result | ||
150 | + */ | ||
151 | + public function renderDataCell($model, $key, $index, $is_first = false, $symbol = '–') | ||
152 | + { | ||
153 | + if ($this->contentOptions instanceof Closure) { | ||
154 | + $options = call_user_func($this->contentOptions, $model, $key, $index, $this); | ||
155 | + } else { | ||
156 | + $options = $this->contentOptions; | ||
157 | + } | ||
158 | + return Html::tag('td', ($is_first ? str_repeat($symbol, $model->depth) : '') . $this->renderDataCellContent($model, $key, $index), $options); | ||
159 | + } | ||
160 | + | ||
161 | + /** | ||
162 | + * Renders the header cell content. | ||
163 | + * The default implementation simply renders [[header]]. | ||
164 | + * This method may be overridden to customize the rendering of the header cell. | ||
165 | + * @return string the rendering result | ||
166 | + */ | ||
167 | + protected function renderHeaderCellContent() | ||
168 | + { | ||
169 | + if ($this->header !== null || $this->label === null && $this->attribute === null) { | ||
170 | + return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell; | ||
171 | + } | ||
172 | + | ||
173 | + $provider = $this->grid->dataProvider; | ||
174 | + | ||
175 | + if ($this->label === null) { | ||
176 | + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) { | ||
177 | + /* @var $model Model */ | ||
178 | + $model = new $provider->query->modelClass; | ||
179 | + $label = $model->getAttributeLabel($this->attribute); | ||
180 | + } else { | ||
181 | + $models = $provider->getModels(); | ||
182 | + if (($model = reset($models)) instanceof Model) { | ||
183 | + /* @var $model Model */ | ||
184 | + $label = $model->getAttributeLabel($this->attribute); | ||
185 | + } else { | ||
186 | + $label = Inflector::camel2words($this->attribute); | ||
187 | + } | ||
188 | + } | ||
189 | + } else { | ||
190 | + $label = $this->label; | ||
191 | + } | ||
192 | + | ||
193 | + return $this->encodeLabel ? Html::encode($label) : $label; | ||
194 | + } | ||
195 | + | ||
196 | + /** | ||
197 | + * Renders the footer cell content. | ||
198 | + * The default implementation simply renders [[footer]]. | ||
199 | + * This method may be overridden to customize the rendering of the footer cell. | ||
200 | + * @return string the rendering result | ||
201 | + */ | ||
202 | + protected function renderFooterCellContent() | ||
203 | + { | ||
204 | + return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell; | ||
205 | + } | ||
206 | + | ||
207 | + /** | ||
208 | + * Renders the data cell content. | ||
209 | + * @param mixed $model the data model | ||
210 | + * @param mixed $key the key associated with the data model | ||
211 | + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. | ||
212 | + * @return string the rendering result | ||
213 | + */ | ||
214 | + protected function renderDataCellContent($model, $key, $index) | ||
215 | + { | ||
216 | + if ($this->content === null) { | ||
217 | + return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format); | ||
218 | + } else { | ||
219 | + if ($this->content !== null) { | ||
220 | + return call_user_func($this->content, $model, $key, $index, $this); | ||
221 | + } else { | ||
222 | + return $this->grid->emptyCell; | ||
223 | + } | ||
224 | + } | ||
225 | + | ||
226 | + | ||
227 | + } | ||
228 | + | ||
229 | + /** | ||
230 | + * Returns the data cell value. | ||
231 | + * @param mixed $model the data model | ||
232 | + * @param mixed $key the key associated with the data model | ||
233 | + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. | ||
234 | + * @return string the data cell value | ||
235 | + */ | ||
236 | + public function getDataCellValue($model, $key, $index) | ||
237 | + { | ||
238 | + if ($this->value !== null) { | ||
239 | + if (is_string($this->value)) { | ||
240 | + return ArrayHelper::getValue($model, $this->value); | ||
241 | + } else { | ||
242 | + return call_user_func($this->value, $model, $key, $index, $this); | ||
243 | + } | ||
244 | + } elseif ($this->attribute !== null) { | ||
245 | + return ArrayHelper::getValue($model, $this->attribute); | ||
246 | + } | ||
247 | + return null; | ||
248 | + } | ||
249 | + | ||
250 | +} | ||
0 | \ No newline at end of file | 251 | \ No newline at end of file |
1 | +++ a/components/artboxtree/treegrid/TreeGridWidget.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree\treegrid; | ||
4 | + | ||
5 | +use common\modules\rubrication\models\TaxOption; | ||
6 | +use Yii; | ||
7 | +use yii\helpers\Html; | ||
8 | +use yii\helpers\ArrayHelper; | ||
9 | + | ||
10 | +class TreeGridWidget extends \common\components\artboxtree\ArtboxTreeWidget { | ||
11 | + | ||
12 | + /** | ||
13 | + * @var array grid column configuration. Each array element represents the configuration | ||
14 | + * for one particular grid column. | ||
15 | + * @see \yii\grid::$columns for details. | ||
16 | + */ | ||
17 | + public $columns = []; | ||
18 | + | ||
19 | + /** | ||
20 | + * @var string the default data column class if the class name is not explicitly specified when configuring a data column. | ||
21 | + * Defaults to 'leandrogehlen\treegrid\TreeGridColumn'. | ||
22 | + */ | ||
23 | + public $dataColumnClass; | ||
24 | + | ||
25 | + /** | ||
26 | + * @var array the HTML attributes for the container tag of the grid view. | ||
27 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
28 | + */ | ||
29 | + public $options = ['class' => 'table table-striped table-bordered']; | ||
30 | + | ||
31 | + /** | ||
32 | + * @var array The plugin options | ||
33 | + */ | ||
34 | + public $pluginOptions = []; | ||
35 | + | ||
36 | + /** | ||
37 | + * @var boolean whether to show the grid view if [[dataProvider]] returns no data. | ||
38 | + */ | ||
39 | + public $showOnEmpty = true; | ||
40 | + | ||
41 | + public $rowOptions = []; | ||
42 | + | ||
43 | + /** | ||
44 | + * @var Closure an anonymous function that is called once BEFORE rendering each data model. | ||
45 | + * It should have the similar signature as [[rowOptions]]. The return result of the function | ||
46 | + * will be rendered directly. | ||
47 | + */ | ||
48 | + public $beforeRow; | ||
49 | + | ||
50 | + /** | ||
51 | + * @var Closure an anonymous function that is called once AFTER rendering each data model. | ||
52 | + * It should have the similar signature as [[rowOptions]]. The return result of the function | ||
53 | + * will be rendered directly. | ||
54 | + */ | ||
55 | + public $afterRow; | ||
56 | + | ||
57 | + /** | ||
58 | + * @var boolean whether to show the header section of the grid table. | ||
59 | + */ | ||
60 | + public $showHeader = true; | ||
61 | + | ||
62 | + /** | ||
63 | + * @var array the HTML attributes for the table header row. | ||
64 | + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. | ||
65 | + */ | ||
66 | + public $headerRowOptions = []; | ||
67 | + | ||
68 | + /** | ||
69 | + * @var boolean whether to show the footer section of the grid table. | ||
70 | + */ | ||
71 | + public $showFooter = false; | ||
72 | + | ||
73 | + /** | ||
74 | + * @var string the HTML display when the content of a cell is empty | ||
75 | + */ | ||
76 | + public $emptyCell = ' '; | ||
77 | + | ||
78 | + public $levelSymbol = '–'; | ||
79 | + | ||
80 | + /** | ||
81 | + * Init the widget object. | ||
82 | + */ | ||
83 | + public function init() { | ||
84 | + parent::init(); | ||
85 | + | ||
86 | + $this->initColumns(); | ||
87 | + } | ||
88 | + | ||
89 | + /** | ||
90 | + * Runs the widget. | ||
91 | + */ | ||
92 | + public function run() { | ||
93 | + $run = parent::run(); | ||
94 | + if (!is_null($run)) | ||
95 | + return $run; | ||
96 | + | ||
97 | + if ($this->showOnEmpty || $this->dataProvider->getCount() > 0) { | ||
98 | + $pagination = $this->dataProvider->getPagination(); | ||
99 | + $pagination->setPageSize($this->dataProvider->getTotalCount()); | ||
100 | + | ||
101 | + $header = $this->showHeader ? $this->renderTableHeader() : false; | ||
102 | + $body = $this->renderItems(); | ||
103 | + $footer = $this->showFooter ? $this->renderTableFooter() : false; | ||
104 | + | ||
105 | + $content = array_filter([ | ||
106 | + $header, | ||
107 | + $body, | ||
108 | + $footer | ||
109 | + ]); | ||
110 | + | ||
111 | + return Html::tag('table', implode("\n", $content), $this->options); | ||
112 | + } else { | ||
113 | + return $this->renderEmptyResult(); | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + /** | ||
118 | + * Renders the table header. | ||
119 | + * @return string the rendering result. | ||
120 | + */ | ||
121 | + public function renderTableHeader() | ||
122 | + { | ||
123 | + $cells = []; | ||
124 | + foreach ($this->columns as $column) { | ||
125 | + /* @var $column TreeGridColumn */ | ||
126 | + $cells[] = $column->renderHeaderCell(); | ||
127 | + } | ||
128 | + $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions); | ||
129 | + return "<thead>\n" . $content . "\n</thead>"; | ||
130 | + } | ||
131 | + | ||
132 | + /** | ||
133 | + * Renders the table footer. | ||
134 | + * @return string the rendering result. | ||
135 | + */ | ||
136 | + public function renderTableFooter() | ||
137 | + { | ||
138 | + $cells = []; | ||
139 | + foreach ($this->columns as $column) { | ||
140 | + /* @var $column TreeGridColumn */ | ||
141 | + $cells[] = $column->renderFooterCell(); | ||
142 | + } | ||
143 | + $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions); | ||
144 | + return "<tfoot>\n" . $content . "\n</tfoot>"; | ||
145 | + } | ||
146 | + | ||
147 | + /** | ||
148 | + * Renders the data models for the grid view. | ||
149 | + */ | ||
150 | + public function renderItems() | ||
151 | + { | ||
152 | + $rows = []; | ||
153 | + $models = array_values($this->dataProvider->getModels()); | ||
154 | + $keys = $this->dataProvider->getKeys(); | ||
155 | + $models = TaxOption::find()->normalizeTreeData($models, $this->rootParentId); | ||
156 | + foreach ($models as $index => $model) { | ||
157 | + $key = $keys[$index]; | ||
158 | + if ($this->beforeRow !== null) { | ||
159 | + $row = call_user_func($this->beforeRow, $model, $key, $index, $this); | ||
160 | + if (!empty($row)) { | ||
161 | + $rows[] = $row; | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + $rows[] = $this->renderTableRow($model, $key, $index); | ||
166 | + | ||
167 | + if ($this->afterRow !== null) { | ||
168 | + $row = call_user_func($this->afterRow, $model, $key, $index, $this); | ||
169 | + if (!empty($row)) { | ||
170 | + $rows[] = $row; | ||
171 | + } | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + if (empty($rows)) { | ||
176 | + $colspan = count($this->columns); | ||
177 | + return "<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>"; | ||
178 | + } else { | ||
179 | + return implode("\n", $rows); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + /** | ||
184 | + * Renders a table row with the given data model and key. | ||
185 | + * @param mixed $model the data model to be rendered | ||
186 | + * @param mixed $key the key associated with the data model | ||
187 | + * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]]. | ||
188 | + * @return string the rendering result | ||
189 | + */ | ||
190 | + public function renderTableRow($model, $key, $index) | ||
191 | + { | ||
192 | + $cells = []; | ||
193 | + /* @var $column TreeGridColumn */ | ||
194 | + $i = 0; | ||
195 | + foreach ($this->columns as $column) { | ||
196 | + $cells[] = $column->renderDataCell($model, $key, $index, $i == 0, $this->levelSymbol); | ||
197 | + $i++; | ||
198 | + } | ||
199 | + if ($this->rowOptions instanceof Closure) { | ||
200 | + $options = call_user_func($this->rowOptions, $model, $key, $index, $this); | ||
201 | + } else { | ||
202 | + $options = $this->rowOptions; | ||
203 | + } | ||
204 | + $options['data-key'] = is_array($key) ? json_encode($key) : (string) $key; | ||
205 | + | ||
206 | + $id = ArrayHelper::getValue($model, $this->keyNameId); | ||
207 | + Html::addCssClass($options, "treegrid-$id"); | ||
208 | + | ||
209 | + $parentId = ArrayHelper::getValue($model, $this->keyNameParentId); | ||
210 | + if ($parentId) { | ||
211 | + Html::addCssClass($options, "treegrid-parent-$parentId"); | ||
212 | + } | ||
213 | + | ||
214 | + return Html::tag('tr', implode('', $cells), $options); | ||
215 | + } | ||
216 | + | ||
217 | + /** | ||
218 | + * Creates column objects and initializes them. | ||
219 | + */ | ||
220 | + protected function initColumns() | ||
221 | + { | ||
222 | + if (empty($this->columns)) { | ||
223 | + $this->guessColumns(); | ||
224 | + } | ||
225 | + foreach ($this->columns as $i => $column) { | ||
226 | + if (is_string($column)) { | ||
227 | + $column = $this->createDataColumn($column); | ||
228 | + } else { | ||
229 | + $column = Yii::createObject(array_merge([ | ||
230 | + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(), | ||
231 | + 'grid' => $this, | ||
232 | + ], $column)); | ||
233 | + } | ||
234 | + if (!$column->visible) { | ||
235 | + unset($this->columns[$i]); | ||
236 | + continue; | ||
237 | + } | ||
238 | + $this->columns[$i] = $column; | ||
239 | + } | ||
240 | + } | ||
241 | + | ||
242 | + /** | ||
243 | + * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label". | ||
244 | + * @param string $text the column specification string | ||
245 | + * @return DataColumn the column instance | ||
246 | + * @throws InvalidConfigException if the column specification is invalid | ||
247 | + */ | ||
248 | + protected function createDataColumn($text) | ||
249 | + { | ||
250 | + if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { | ||
251 | + throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); | ||
252 | + } | ||
253 | + | ||
254 | + return Yii::createObject([ | ||
255 | + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(), | ||
256 | + 'grid' => $this, | ||
257 | + 'attribute' => $matches[1], | ||
258 | + 'format' => isset($matches[3]) ? $matches[3] : 'text', | ||
259 | + 'label' => isset($matches[5]) ? $matches[5] : null, | ||
260 | + ]); | ||
261 | + } | ||
262 | + | ||
263 | + /** | ||
264 | + * This function tries to guess the columns to show from the given data | ||
265 | + * if [[columns]] are not explicitly specified. | ||
266 | + */ | ||
267 | + protected function guessColumns() | ||
268 | + { | ||
269 | + $models = $this->dataProvider->getModels(); | ||
270 | + $model = reset($models); | ||
271 | + if (is_array($model) || is_object($model)) { | ||
272 | + foreach ($model as $name => $value) { | ||
273 | + $this->columns[] = $name; | ||
274 | + } | ||
275 | + } | ||
276 | + } | ||
277 | +} | ||
0 | \ No newline at end of file | 278 | \ No newline at end of file |
1 | +++ a/components/artboxtree/treelist/TreeListWidget.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree\treelist; | ||
4 | + | ||
5 | +use Yii; | ||
6 | +use yii\helpers\Html; | ||
7 | +use yii\helpers\ArrayHelper; | ||
8 | + | ||
9 | +class TreeListWidget extends \common\components\artboxtree\ArtboxTreeWidget { | ||
10 | + | ||
11 | + public $displayField = 'title'; | ||
12 | + | ||
13 | + /** | ||
14 | + * Init the widget object. | ||
15 | + */ | ||
16 | + public function init() { | ||
17 | + parent::init(); | ||
18 | + } | ||
19 | + | ||
20 | + /** | ||
21 | + * Runs the widget. | ||
22 | + */ | ||
23 | + public function run() { | ||
24 | + $run = parent::run(); | ||
25 | + if (!is_null($run)) | ||
26 | + return $run; | ||
27 | + | ||
28 | + $models = $this->hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId); | ||
29 | + return $this->renderTreelist($models); | ||
30 | + } | ||
31 | + | ||
32 | + protected function renderTreelist($models) { | ||
33 | + foreach ($models as $index => $model) { | ||
34 | + $row = $this->renderTreelistItem($model['item']); | ||
35 | + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']); | ||
36 | + $output[] = '<li>'. $row . $children .'</li>'; | ||
37 | + } | ||
38 | + | ||
39 | + if (!empty($output)) | ||
40 | + return '<ul>'. implode("\n", $output) .'</ul>'; | ||
41 | + } | ||
42 | + | ||
43 | + protected function renderTreelistItem($model) | ||
44 | + { | ||
45 | + $options = []; | ||
46 | + $id = ArrayHelper::getValue($model, $this->keyNameId); | ||
47 | + Html::addCssClass($options, "treelistitem-$id"); | ||
48 | + | ||
49 | + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId); | ||
50 | + if ($parent_id) { | ||
51 | + Html::addCssClass($options, "treelistitem-parent-$parent_id"); | ||
52 | + } | ||
53 | + | ||
54 | +// if (is_string($this->value)) { | ||
55 | +// return ArrayHelper::getValue($model, $this->value); | ||
56 | +// } else { | ||
57 | +// return call_user_func($this->value, $model, $key, $index, $this); | ||
58 | +// } | ||
59 | + | ||
60 | + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options); | ||
61 | + } | ||
62 | +} | ||
0 | \ No newline at end of file | 63 | \ No newline at end of file |
1 | +++ a/components/artboxtree/treemenu/TreeMenuWidget.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace common\components\artboxtree\treemenu; | ||
4 | + | ||
5 | +use Yii; | ||
6 | +use yii\helpers\Html; | ||
7 | +use yii\helpers\ArrayHelper; | ||
8 | + | ||
9 | +class TreeMenuWidget extends \common\components\artboxtree\ArtboxTreeWidget { | ||
10 | + | ||
11 | + public $displayField = 'title'; | ||
12 | + | ||
13 | + /** | ||
14 | + * Init the widget object. | ||
15 | + */ | ||
16 | + public function init() { | ||
17 | + parent::init(); | ||
18 | + } | ||
19 | + | ||
20 | + /** | ||
21 | + * Runs the widget. | ||
22 | + */ | ||
23 | + public function run() { | ||
24 | + $run = parent::run(); | ||
25 | + if (!is_null($run)) | ||
26 | + return $run; | ||
27 | + | ||
28 | + $models = $this->hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId); | ||
29 | + return $this->renderTreelist($models); | ||
30 | + } | ||
31 | + | ||
32 | + protected function renderTreelist($models) { | ||
33 | + foreach ($models as $index => $model) { | ||
34 | + $row = $this->renderTreelistItem($model['item']); | ||
35 | + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']); | ||
36 | + $output[] = '<li>'. $row . $children .'</li>'; | ||
37 | + } | ||
38 | + | ||
39 | + if (!empty($output)) | ||
40 | + return '<ul>'. implode("\n", $output) .'</ul>'; | ||
41 | + } | ||
42 | + | ||
43 | + protected function renderTreelistItem($model) | ||
44 | + { | ||
45 | + $options = []; | ||
46 | + $id = ArrayHelper::getValue($model, $this->keyNameId); | ||
47 | + Html::addCssClass($options, "treelistitem-$id"); | ||
48 | + | ||
49 | + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId); | ||
50 | + if ($parent_id) { | ||
51 | + Html::addCssClass($options, "treelistitem-parent-$parent_id"); | ||
52 | + } | ||
53 | + | ||
54 | +// if (is_string($this->value)) { | ||
55 | +// return ArrayHelper::getValue($model, $this->value); | ||
56 | +// } else { | ||
57 | +// return call_user_func($this->value, $model, $key, $index, $this); | ||
58 | +// } | ||
59 | + | ||
60 | + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options); | ||
61 | + } | ||
62 | +} | ||
0 | \ No newline at end of file | 63 | \ No newline at end of file |
1 | +++ a/composer.json | ||
1 | +{ | ||
2 | + "name": "artweb/artbox", | ||
3 | + "description": "Yii2 light-weight CMS", | ||
4 | + "license": "BSD-3-Clause", | ||
5 | + "require": { | ||
6 | + "php": ">=7.0", | ||
7 | + "yiisoft/yii2": "*", | ||
8 | + "developeruz/yii2-db-rbac": "*" | ||
9 | + }, | ||
10 | + "autoload": { | ||
11 | + "psr-4": { | ||
12 | + "artweb\\artbox\\": "" | ||
13 | + } | ||
14 | + } | ||
15 | +} | ||
0 | \ No newline at end of file | 16 | \ No newline at end of file |
1 | +++ a/console/GenerateController.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace console\controllers; | ||
4 | + | ||
5 | + use common\modules\product\models\Brand; | ||
6 | + use common\modules\product\models\Category; | ||
7 | + use common\modules\product\models\Product; | ||
8 | + use common\modules\product\models\ProductVariant; | ||
9 | + use common\modules\rubrication\models\TaxGroup; | ||
10 | + use common\modules\rubrication\models\TaxOption; | ||
11 | + use Faker\Factory; | ||
12 | + use Faker\Generator; | ||
13 | + use yii\console\Controller; | ||
14 | + use yii\helpers\Console; | ||
15 | + | ||
16 | + /** | ||
17 | + * Class GenerateController to generate ArtBox fake data | ||
18 | + * | ||
19 | + * @package console\controllers | ||
20 | + */ | ||
21 | + class GenerateController extends Controller | ||
22 | + { | ||
23 | + /** | ||
24 | + * Faker Generator locales | ||
25 | + * | ||
26 | + * @var array | ||
27 | + */ | ||
28 | + private $locales = [ | ||
29 | + 2 => 'ru_RU', | ||
30 | + 3 => 'uk_UA', | ||
31 | + ]; | ||
32 | + | ||
33 | + /** | ||
34 | + * Faker Generators instances | ||
35 | + * | ||
36 | + * @var Generator[] $fakers | ||
37 | + */ | ||
38 | + private $fakers = []; | ||
39 | + | ||
40 | + /** | ||
41 | + * Create faker Generators for all $locales | ||
42 | + * | ||
43 | + * @param \yii\base\Action $action | ||
44 | + * | ||
45 | + * @return bool | ||
46 | + */ | ||
47 | + public function beforeAction($action) | ||
48 | + { | ||
49 | + $parent = parent::beforeAction($action); | ||
50 | + $fakers = []; | ||
51 | + foreach ($this->locales as $locale_id => $locale) { | ||
52 | + $fakers[ $locale_id ] = Factory::create($locale); | ||
53 | + } | ||
54 | + $this->fakers = $fakers; | ||
55 | + return $parent; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * Generate fake Brands | ||
60 | + * | ||
61 | + * @param int $count Brands count | ||
62 | + * | ||
63 | + * @return int | ||
64 | + */ | ||
65 | + public function actionBrand(int $count = 10): int | ||
66 | + { | ||
67 | + /** | ||
68 | + * @var Brand[] $models | ||
69 | + */ | ||
70 | + $models = []; | ||
71 | + $fakers = $this->fakers; | ||
72 | + for ($i = 0; $i < $count; $i++) { | ||
73 | + $models[ $i ] = \Yii::createObject(Brand::className()); | ||
74 | + $models[ $i ]->generateLangs(); | ||
75 | + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { | ||
76 | + $modelLang->title = $fakers[ $language_id ]->company; | ||
77 | + } | ||
78 | + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { | ||
79 | + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); | ||
80 | + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); | ||
81 | + echo "Brand '$title' inserted under $id ID.\n"; | ||
82 | + }; | ||
83 | + } | ||
84 | + return 0; | ||
85 | + } | ||
86 | + | ||
87 | + /** | ||
88 | + * Generate fake categories | ||
89 | + * | ||
90 | + * @param int $count Category count | ||
91 | + * | ||
92 | + * @return int | ||
93 | + */ | ||
94 | + public function actionCategory(int $count = 10):int | ||
95 | + { | ||
96 | + /** | ||
97 | + * @var Category[] $models | ||
98 | + */ | ||
99 | + $models = []; | ||
100 | + $fakers = $this->fakers; | ||
101 | + for ($i = 0; $i < $count; $i++) { | ||
102 | + $models[ $i ] = \Yii::createObject( | ||
103 | + [ | ||
104 | + 'class' => Category::className(), | ||
105 | + 'depth' => 0, | ||
106 | + 'parent_id' => 0, | ||
107 | + ] | ||
108 | + ); | ||
109 | + $models[ $i ]->generateLangs(); | ||
110 | + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { | ||
111 | + $modelLang->title = ucfirst($fakers[ $language_id ]->word); | ||
112 | + } | ||
113 | + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { | ||
114 | + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); | ||
115 | + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); | ||
116 | + echo "Category '$title' inserted under $id ID.\n"; | ||
117 | + }; | ||
118 | + } | ||
119 | + return 0; | ||
120 | + } | ||
121 | + | ||
122 | + /** | ||
123 | + * Generate fake products with variants, categories and tax options | ||
124 | + * | ||
125 | + * @param int $count Product count | ||
126 | + * | ||
127 | + * @return int | ||
128 | + */ | ||
129 | + public function actionProduct(int $count = 10):int | ||
130 | + { | ||
131 | + /** | ||
132 | + * @var Product[] $models | ||
133 | + */ | ||
134 | + $models = []; | ||
135 | + $fakers = $this->fakers; | ||
136 | + $brands = Brand::find() | ||
137 | + ->limit(20) | ||
138 | + ->asArray() | ||
139 | + ->column(); | ||
140 | + $categories = Category::find() | ||
141 | + ->limit(100) | ||
142 | + ->asArray() | ||
143 | + ->column(); | ||
144 | + $product_options = TaxOption::find() | ||
145 | + ->joinWith('taxGroup') | ||
146 | + ->where([ 'tax_group.level' => TaxGroup::GROUP_PRODUCT ]) | ||
147 | + ->limit(50) | ||
148 | + ->asArray() | ||
149 | + ->column(); | ||
150 | + $variant_options = TaxOption::find() | ||
151 | + ->joinWith('taxGroup') | ||
152 | + ->where([ 'tax_group.level' => TaxGroup::GROUP_VARIANT ]) | ||
153 | + ->limit(50) | ||
154 | + ->asArray() | ||
155 | + ->column(); | ||
156 | + for ($i = 0; $i < $count; $i++) { | ||
157 | + $models[ $i ] = \Yii::createObject( | ||
158 | + [ | ||
159 | + 'class' => Product::className(), | ||
160 | + 'brand_id' => $fakers[ 2 ]->randomElement($brands), | ||
161 | + ] | ||
162 | + ); | ||
163 | + $models[ $i ]->setCategories( | ||
164 | + $fakers[ 2 ]->randomElements($categories, $fakers[ 2 ]->randomDigitNotNull) | ||
165 | + ); | ||
166 | + $models[ $i ]->setOptions( | ||
167 | + $fakers[ 2 ]->randomElements($product_options, $fakers[ 2 ]->randomDigitNotNull) | ||
168 | + ); | ||
169 | + $models[ $i ]->generateLangs(); | ||
170 | + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { | ||
171 | + $modelLang->title = ucfirst($fakers[ $language_id ]->word); | ||
172 | + } | ||
173 | + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { | ||
174 | + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); | ||
175 | + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); | ||
176 | + echo "Product '$title' inserted under $id ID.\n"; | ||
177 | + $variant_count = $fakers[ 2 ]->numberBetween(4, 10); | ||
178 | + for ($j = 0; $j < $variant_count; $j++) { | ||
179 | + /** | ||
180 | + * @var ProductVariant $variant | ||
181 | + */ | ||
182 | + $variant = \Yii::createObject( | ||
183 | + [ | ||
184 | + 'class' => ProductVariant::className(), | ||
185 | + 'product_id' => $models[ $i ]->id, | ||
186 | + 'sku' => $fakers[ 2 ]->uuid, | ||
187 | + 'price' => $fakers[ 2 ]->randomFloat(2, 100, 10000), | ||
188 | + 'stock' => 10, | ||
189 | + 'product_unit_id' => 1, | ||
190 | + ] | ||
191 | + ); | ||
192 | + $variant->setOptions( | ||
193 | + $fakers[ 2 ]->randomElements($variant_options, $fakers[ 2 ]->randomDigitNotNull) | ||
194 | + ); | ||
195 | + $variant->generateLangs(); | ||
196 | + foreach ($variant->modelLangs as $variant_language_id => $variantLang) { | ||
197 | + $variantLang->title = ucfirst($fakers[ $variant_language_id ]->word); | ||
198 | + } | ||
199 | + if ($variant->save() && $variant->transactionStatus) { | ||
200 | + $variant_title = $this->ansiFormat($variant->modelLangs[ 2 ]->title, Console::FG_YELLOW); | ||
201 | + $variant_id = $this->ansiFormat($variant->id, Console::FG_YELLOW); | ||
202 | + echo "Variant '$variant_title' inserted under $variant_id ID for product $title.\n"; | ||
203 | + } | ||
204 | + } | ||
205 | + }; | ||
206 | + } | ||
207 | + return 0; | ||
208 | + } | ||
209 | + | ||
210 | + /** | ||
211 | + * Generate fake tax groups with tax options. | ||
212 | + * | ||
213 | + * @param int $count_product Tax Groups for Product | ||
214 | + * @param int $count_variant Tax Groups for ProductVariant | ||
215 | + * | ||
216 | + * @return int | ||
217 | + */ | ||
218 | + public function actionTax(int $count_product = 10, int $count_variant = 10):int | ||
219 | + { | ||
220 | + $categories = Category::find() | ||
221 | + ->asArray() | ||
222 | + ->column(); | ||
223 | + $this->generateTax(TaxGroup::GROUP_PRODUCT, $count_product, $categories); | ||
224 | + $this->generateTax(TaxGroup::GROUP_VARIANT, $count_variant, $categories); | ||
225 | + return 0; | ||
226 | + } | ||
227 | + | ||
228 | + /** | ||
229 | + * Generates tax groups amd tax options for actionTax | ||
230 | + * | ||
231 | + * @param int $level Tax Group for Product or ProductVariant | ||
232 | + * @param int $count Tax Group count | ||
233 | + * @param array $categories Categories for Tax Groups | ||
234 | + * | ||
235 | + * @see GenerateController::actionTax() | ||
236 | + */ | ||
237 | + private function generateTax(int $level, int $count, array $categories = []) | ||
238 | + { | ||
239 | + $count_option = 10; | ||
240 | + $fakers = $this->fakers; | ||
241 | + /** | ||
242 | + * @var TaxGroup[] $models | ||
243 | + */ | ||
244 | + $models = []; | ||
245 | + for ($i = 0; $i < $count; $i++) { | ||
246 | + $models[ $i ] = \Yii::createObject( | ||
247 | + [ | ||
248 | + 'class' => TaxGroup::className(), | ||
249 | + 'is_filter' => true, | ||
250 | + 'display' => true, | ||
251 | + ] | ||
252 | + ); | ||
253 | + $models[ $i ]->level = $level; | ||
254 | + $models[ $i ]->setCategories($categories); | ||
255 | + $models[ $i ]->generateLangs(); | ||
256 | + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { | ||
257 | + $modelLang->title = ucfirst($fakers[ $language_id ]->word); | ||
258 | + } | ||
259 | + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { | ||
260 | + for ($j = 0; $j < $count_option; $j++) { | ||
261 | + /** | ||
262 | + * @var TaxOption $option | ||
263 | + */ | ||
264 | + $option = \Yii::createObject( | ||
265 | + TaxOption::className() | ||
266 | + ); | ||
267 | + $option->tax_group_id = $models[ $i ]->id; | ||
268 | + $option->generateLangs(); | ||
269 | + foreach ($option->modelLangs as $option_language_id => $taxOptionLang) { | ||
270 | + $taxOptionLang->value = ucfirst($fakers[ $option_language_id ]->word); | ||
271 | + } | ||
272 | + $option->save(); | ||
273 | + } | ||
274 | + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); | ||
275 | + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); | ||
276 | + $element = $this->ansiFormat( | ||
277 | + ( $level === TaxGroup::GROUP_PRODUCT ) ? 'Product' : 'Variant', | ||
278 | + Console::FG_RED | ||
279 | + ); | ||
280 | + echo "Category '$title' inserted for $element under $id ID.\n"; | ||
281 | + }; | ||
282 | + } | ||
283 | + } | ||
284 | + } | ||
285 | + | ||
0 | \ No newline at end of file | 286 | \ No newline at end of file |
1 | +++ a/console/ImportController.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace console\controllers; | ||
4 | + | ||
5 | +use common\modules\product\models\Import; | ||
6 | +use Yii; | ||
7 | +use yii\console\Controller; | ||
8 | + | ||
9 | +class ImportController extends Controller { | ||
10 | + public $errors = []; | ||
11 | + | ||
12 | + | ||
13 | + private function getProductsFile($file_type = 'uploadFileProducts') { | ||
14 | + $filename = Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@'. $file_type); | ||
15 | + if (!is_file($filename)) { | ||
16 | + $this->stderr('Task already executed'); | ||
17 | + return Controller::EXIT_CODE_ERROR; | ||
18 | + } | ||
19 | + return fopen ($filename, 'r'); | ||
20 | + } | ||
21 | + | ||
22 | + public function actionProducts() { | ||
23 | +// if (file_exists(Yii::getAlias('@uploadDir/goProducts.lock'))) { | ||
24 | +// $this->errors[] = 'Task already executed'; | ||
25 | +// return Controller::EXIT_CODE_ERROR; | ||
26 | +// } | ||
27 | +// $ff = fopen(Yii::getAlias('@uploadDir/goProducts.lock'), 'w+'); | ||
28 | +// fclose($ff); | ||
29 | + $model = new Import(); | ||
30 | + $model->goProducts(0, null); | ||
31 | +// unlink(Yii::getAlias('@uploadDir/goProducts.lock')); | ||
32 | + return Controller::EXIT_CODE_NORMAL; | ||
33 | + } | ||
34 | + | ||
35 | + public function actionPrices() { | ||
36 | + if (file_exists(Yii::getAlias('@uploadDir/goPrices.lock'))) { | ||
37 | + $this->stderr('Task already executed'); | ||
38 | + return Controller::EXIT_CODE_ERROR; | ||
39 | + } | ||
40 | + $ff = fopen(Yii::getAlias('@uploadDir/goPrices.lock'), 'w+'); | ||
41 | + fclose($ff); | ||
42 | + $model = new Import(); | ||
43 | + $data = $model->goPrices(0, null); | ||
44 | + unlink(Yii::getAlias('@uploadDir/goPrices.lock')); | ||
45 | + return Controller::EXIT_CODE_NORMAL; | ||
46 | + } | ||
47 | + | ||
48 | + private function saveNotFoundRecord (array $line, $filename) | ||
49 | + { | ||
50 | + $str = implode (';', $line)."\n"; | ||
51 | + $str = iconv ("UTF-8//TRANSLIT//IGNORE", "windows-1251", $str); | ||
52 | + | ||
53 | + $fg = fopen (Yii::getAlias('@uploadDir') .'/'. $filename, 'a+'); | ||
54 | + fputs ($fg, $str); | ||
55 | + fclose ($fg); | ||
56 | + } | ||
57 | +} | ||
0 | \ No newline at end of file | 58 | \ No newline at end of file |
1 | +++ a/console/SiteMapController.php | ||
1 | +<?php | ||
2 | + | ||
3 | +namespace console\controllers; | ||
4 | + | ||
5 | +use common\models\Article; | ||
6 | +use common\models\Seo; | ||
7 | +use common\modules\product\models\Category; | ||
8 | +use common\modules\product\models\Product; | ||
9 | +use frontend\models\ProductFrontendSearch; | ||
10 | +use Yii; | ||
11 | +use common\models\Page; | ||
12 | +use yii\helpers\ArrayHelper; | ||
13 | +use yii\helpers\Url; | ||
14 | +use yii\console\Controller; | ||
15 | +/** | ||
16 | + * PageController implements the CRUD actions for Page model. | ||
17 | + */ | ||
18 | +class SiteMapController extends Controller | ||
19 | +{ | ||
20 | + | ||
21 | + private $urlList = ['http://www.rukzachok.com.ua/']; | ||
22 | + private $count = 1; | ||
23 | + | ||
24 | + | ||
25 | + | ||
26 | + public function checkFilter($category, $filter){ | ||
27 | + $productModel = new ProductFrontendSearch(); | ||
28 | + $productProvider = $productModel->search($category, $filter); | ||
29 | + if(!empty($productProvider->models)){ | ||
30 | + return true; | ||
31 | + } else { | ||
32 | + return false; | ||
33 | + } | ||
34 | + } | ||
35 | + | ||
36 | + | ||
37 | + | ||
38 | + public function getAddStatic(){ | ||
39 | + return [ | ||
40 | + 'http://www.rukzachok.com.ua', | ||
41 | + 'http://www.rukzachok.com.ua/catalog' | ||
42 | + ]; | ||
43 | + } | ||
44 | + | ||
45 | + | ||
46 | + public function getProducts() { | ||
47 | + return Product::find()->all(); | ||
48 | + | ||
49 | + } | ||
50 | + | ||
51 | + | ||
52 | + public function getSeoLinks() { | ||
53 | + return Seo::find()->where(['meta' => ''])->all(); | ||
54 | + | ||
55 | + } | ||
56 | + | ||
57 | + public function getStaticPages(){ | ||
58 | + return Page::find()->all(); | ||
59 | + } | ||
60 | + | ||
61 | + | ||
62 | + public function getCategories(){ | ||
63 | + return Category::find()->all(); | ||
64 | + } | ||
65 | + | ||
66 | + | ||
67 | + public function getArticles(){ | ||
68 | + return Article::find()->all(); | ||
69 | + } | ||
70 | + | ||
71 | + public function getBrands($category){ | ||
72 | + | ||
73 | + return $category->brands; | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * @param $category Category; | ||
78 | + * @return mixed | ||
79 | + */ | ||
80 | + | ||
81 | + public function getFilters($category){ | ||
82 | + | ||
83 | + return $category->getActiveFilters()->all(); | ||
84 | + | ||
85 | + } | ||
86 | + | ||
87 | + | ||
88 | + public function checkUrl($url){ | ||
89 | + if(!in_array($url, $this->urlList)){ | ||
90 | + $this->urlList[] = $url; | ||
91 | + return true; | ||
92 | + } else { | ||
93 | + return false; | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + | ||
98 | + public function createRow( $url, $priority, &$content ){ | ||
99 | + if($this->checkUrl($url)){ | ||
100 | + print $this->count++ . "\n"; | ||
101 | + $content .= '<url>' . | ||
102 | + '<loc>' . $url . '</loc>' . | ||
103 | + '<lastmod>' . date('Y-m-d') . '</lastmod>' . | ||
104 | + '<changefreq>Daily</changefreq>' . | ||
105 | + '<priority>' . $priority .'</priority>' . | ||
106 | + '</url>'; | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + | ||
111 | + public function actionProcess() { | ||
112 | + | ||
113 | + $config = ArrayHelper::merge( | ||
114 | + require(__DIR__ . '/../../frontend/config/main.php'), | ||
115 | + require(__DIR__ . '/../../common/config/main.php') | ||
116 | + | ||
117 | + ); | ||
118 | + | ||
119 | + Yii::$app->urlManager->addRules($config['components']['urlManager']['rules']); | ||
120 | + | ||
121 | + | ||
122 | + | ||
123 | + $dirName = Yii::getAlias('@frontend').'/web'; | ||
124 | + | ||
125 | + $filename = 'sitemap.xml'; | ||
126 | + | ||
127 | + setlocale(LC_ALL, 'ru_RU.CP1251'); | ||
128 | + $handle = fopen($dirName .'/'. $filename, "w"); | ||
129 | + | ||
130 | + $content = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'; | ||
131 | + | ||
132 | + foreach ($this->getAddStatic() as $page) { | ||
133 | + $this->createRow($page , 1,$content); | ||
134 | + } | ||
135 | + | ||
136 | + foreach ($this->getStaticPages() as $page) { | ||
137 | + $url = Url::to(['text/index','translit' => $page->translit]); | ||
138 | + $this->createRow($url , 1,$content); | ||
139 | + } | ||
140 | + | ||
141 | + foreach ($this->getCategories() as $category) { | ||
142 | + $url = Url::to(['catalog/category', 'category' => $category]); | ||
143 | + $this->createRow($url , 1,$content); | ||
144 | + } | ||
145 | + | ||
146 | + | ||
147 | + foreach ($this->getProducts() as $product) { | ||
148 | + | ||
149 | + $url = Url::to(['catalog/product', 'product' => $product]); | ||
150 | + $this->createRow($url , 0.9, $content); | ||
151 | + } | ||
152 | + | ||
153 | + | ||
154 | + foreach ($this->getArticles() as $article) { | ||
155 | + | ||
156 | + $url = Url::to(['articles/show', 'translit' => $article->translit, 'id' => $article->id,]); | ||
157 | + $this->createRow($url , 0.8,$content); | ||
158 | + | ||
159 | + } | ||
160 | + | ||
161 | + | ||
162 | + foreach($this->getCategories() as $category){ | ||
163 | + foreach ($this->getBrands($category) as $brand) { | ||
164 | + if($this->checkFilter($category, ['brands' => [$brand->id]])){ | ||
165 | + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias]]]) ; | ||
166 | + $this->createRow($url , 0.8, $content); | ||
167 | + } | ||
168 | + } | ||
169 | + } | ||
170 | + | ||
171 | + | ||
172 | + foreach($this->getCategories() as $category){ | ||
173 | + foreach ($this->getFilters($category) as $filter) { | ||
174 | + if($this->checkFilter($category, [$filter['group_alias'] => [$filter['option_alias']]])){ | ||
175 | + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter['group_alias'] => [$filter['option_alias']]] ]); | ||
176 | + $this->createRow($url , 0.8, $content); | ||
177 | + } | ||
178 | + | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + foreach($this->getSeoLinks() as $link){ | ||
183 | + $url = Yii::$app->urlManager->baseUrl.$link->url; | ||
184 | + $this->createRow($url , 0.7, $content); | ||
185 | + | ||
186 | + } | ||
187 | + | ||
188 | + | ||
189 | + | ||
190 | +// foreach($this->getCategories() as $category){ | ||
191 | +// foreach ($this->getFilters($category) as $filter1) { | ||
192 | +// foreach ($this->getFilters($category) as $filter2) { | ||
193 | +// if($this->checkFilter($category, [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] )){ | ||
194 | +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] ]); | ||
195 | +// $this->createRow($url , 0.7, $content); | ||
196 | +// } | ||
197 | +// | ||
198 | +// } | ||
199 | +// | ||
200 | +// foreach ($this->getBrands($category) as $brand) { | ||
201 | +// if($this->checkFilter($category, ['brands' => [$brand->id], $filter1['group_alias'] => [$filter1['option_alias']]] )){ | ||
202 | +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias],$filter1['group_alias'] => [$filter1['option_alias']]]]); | ||
203 | +// $this->createRow($url , 0.7,$content); | ||
204 | +// } | ||
205 | +// | ||
206 | +// } | ||
207 | +// } | ||
208 | +// } | ||
209 | + | ||
210 | + | ||
211 | + | ||
212 | + $content .= '</urlset>'; | ||
213 | + | ||
214 | + fwrite($handle, $content); | ||
215 | + fclose($handle); | ||
216 | + | ||
217 | + print $dirName .'/'. $filename; | ||
218 | + } | ||
219 | + | ||
220 | +} |
1 | +++ a/models/Customer.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use Yii; | ||
6 | + use yii\web\IdentityInterface; | ||
7 | + | ||
8 | + /** | ||
9 | + * This is the model class for table "customer". | ||
10 | + * | ||
11 | + * @property integer $id | ||
12 | + * @property string $username | ||
13 | + * @property string $password_hash | ||
14 | + * @property string $name | ||
15 | + * @property string $surname | ||
16 | + * @property string $phone | ||
17 | + * @property string $gender | ||
18 | + * @property integer $birth_day | ||
19 | + * @property integer $birth_month | ||
20 | + * @property integer $birth_year | ||
21 | + * @property string $body | ||
22 | + * @property integer $group_id | ||
23 | + * @property string $email | ||
24 | + * @property string $auth_key | ||
25 | + * @property string $password_reset_token | ||
26 | + * @property integer $status | ||
27 | + * @property integer $created_at | ||
28 | + * @property integer $updated_at | ||
29 | + */ | ||
30 | + class Customer extends User implements IdentityInterface | ||
31 | + { | ||
32 | + | ||
33 | + /** | ||
34 | + * @inheritdoc | ||
35 | + */ | ||
36 | + public static function tableName() | ||
37 | + { | ||
38 | + return 'customer'; | ||
39 | + } | ||
40 | + | ||
41 | + /** | ||
42 | + * @inheritdoc | ||
43 | + */ | ||
44 | + public function rules() | ||
45 | + { | ||
46 | + return [ | ||
47 | + [ | ||
48 | + [ | ||
49 | + 'username', | ||
50 | + 'password_hash', | ||
51 | + ], | ||
52 | + 'required', | ||
53 | + ], | ||
54 | + [ | ||
55 | + [ 'password' ], | ||
56 | + 'safe', | ||
57 | + ], | ||
58 | + [ | ||
59 | + [ | ||
60 | + 'birth_day', | ||
61 | + 'birth_month', | ||
62 | + 'birth_year', | ||
63 | + 'group_id', | ||
64 | + 'status', | ||
65 | + 'created_at', | ||
66 | + 'updated_at', | ||
67 | + ], | ||
68 | + 'integer', | ||
69 | + ], | ||
70 | + [ | ||
71 | + [ 'body' ], | ||
72 | + 'string', | ||
73 | + ], | ||
74 | + [ | ||
75 | + [ 'status' ], | ||
76 | + 'default', | ||
77 | + 'value' => '10', | ||
78 | + ], | ||
79 | + [ | ||
80 | + [ | ||
81 | + 'username', | ||
82 | + 'name', | ||
83 | + 'surname', | ||
84 | + 'phone', | ||
85 | + 'email', | ||
86 | + 'password_reset_token', | ||
87 | + ], | ||
88 | + 'string', | ||
89 | + 'max' => 255, | ||
90 | + ], | ||
91 | + [ | ||
92 | + [ | ||
93 | + 'gender', | ||
94 | + 'auth_key', | ||
95 | + ], | ||
96 | + 'string', | ||
97 | + 'max' => 32, | ||
98 | + ], | ||
99 | + ]; | ||
100 | + } | ||
101 | + | ||
102 | + /** | ||
103 | + * @inheritdoc | ||
104 | + */ | ||
105 | + public function attributeLabels() | ||
106 | + { | ||
107 | + return [ | ||
108 | + 'id' => Yii::t('app', 'id'), | ||
109 | + 'username' => Yii::t('app', 'username'), | ||
110 | + 'name' => Yii::t('app', 'cname'), | ||
111 | + 'surname' => Yii::t('app', 'surname'), | ||
112 | + 'phone' => Yii::t('app', 'phone'), | ||
113 | + 'gender' => Yii::t('app', 'gender'), | ||
114 | + 'birth_day' => Yii::t('app', 'birth_day'), | ||
115 | + 'birth_month' => Yii::t('app', 'birth_month'), | ||
116 | + 'birth_year' => Yii::t('app', 'birth_year'), | ||
117 | + 'body' => Yii::t('app', 'body'), | ||
118 | + 'group_id' => Yii::t('app', 'group_id'), | ||
119 | + 'email' => Yii::t('app', 'email'), | ||
120 | + 'auth_key' => Yii::t('app', 'auth_key'), | ||
121 | + 'password_reset_token' => Yii::t('app', 'password_reset_token'), | ||
122 | + 'status' => Yii::t('app', 'status'), | ||
123 | + 'created_at' => Yii::t('app', 'created_at'), | ||
124 | + 'updated_at' => Yii::t('app', 'updated_at'), | ||
125 | + ]; | ||
126 | + } | ||
127 | + | ||
128 | + /** | ||
129 | + * Finds user by email | ||
130 | + * | ||
131 | + * @param string $email | ||
132 | + * | ||
133 | + * @return static|null | ||
134 | + */ | ||
135 | + public static function findByEmail($email) | ||
136 | + { | ||
137 | + return static::findOne( | ||
138 | + [ | ||
139 | + 'email' => $email, | ||
140 | + 'status' => self::STATUS_ACTIVE, | ||
141 | + ] | ||
142 | + ); | ||
143 | + } | ||
144 | + | ||
145 | + /** | ||
146 | + * Get full name | ||
147 | + * | ||
148 | + * @return string | ||
149 | + */ | ||
150 | + public function getName() | ||
151 | + { | ||
152 | + return $this->username . ' ' . $this->surname; | ||
153 | + } | ||
154 | + | ||
155 | + public function getPassword() | ||
156 | + { | ||
157 | + return false; | ||
158 | + } | ||
159 | + | ||
160 | + } |
1 | +++ a/models/CustomerSearch.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use yii\base\Model; | ||
6 | + use yii\data\ActiveDataProvider; | ||
7 | + | ||
8 | + /** | ||
9 | + * CustomerSearch represents the model behind the search form about `common\models\Customer`. | ||
10 | + */ | ||
11 | + class CustomerSearch extends Customer | ||
12 | + { | ||
13 | + | ||
14 | + /** | ||
15 | + * @inheritdoc | ||
16 | + */ | ||
17 | + public function rules() | ||
18 | + { | ||
19 | + return [ | ||
20 | + [ | ||
21 | + [ | ||
22 | + 'id', | ||
23 | + 'birth_day', | ||
24 | + 'birth_month', | ||
25 | + 'birth_year', | ||
26 | + 'group_id', | ||
27 | + ], | ||
28 | + 'integer', | ||
29 | + ], | ||
30 | + [ | ||
31 | + [ | ||
32 | + 'username', | ||
33 | + 'name', | ||
34 | + 'surname', | ||
35 | + 'phone', | ||
36 | + 'body', | ||
37 | + ], | ||
38 | + 'safe', | ||
39 | + ], | ||
40 | + ]; | ||
41 | + } | ||
42 | + | ||
43 | + /** | ||
44 | + * @inheritdoc | ||
45 | + */ | ||
46 | + public function scenarios() | ||
47 | + { | ||
48 | + // bypass scenarios() implementation in the parent class | ||
49 | + return Model::scenarios(); | ||
50 | + } | ||
51 | + | ||
52 | + /** | ||
53 | + * Creates data provider instance with search query applied | ||
54 | + * | ||
55 | + * @param array $params | ||
56 | + * | ||
57 | + * @return ActiveDataProvider | ||
58 | + */ | ||
59 | + public function search($params) | ||
60 | + { | ||
61 | + $query = Customer::find(); | ||
62 | + | ||
63 | + // add conditions that should always apply here | ||
64 | + | ||
65 | + $dataProvider = new ActiveDataProvider( | ||
66 | + [ | ||
67 | + 'query' => $query, | ||
68 | + ] | ||
69 | + ); | ||
70 | + | ||
71 | + $this->load($params); | ||
72 | + | ||
73 | + if (!$this->validate()) { | ||
74 | + // uncomment the following line if you do not want to return any records when validation fails | ||
75 | + // $query->where('0=1'); | ||
76 | + return $dataProvider; | ||
77 | + } | ||
78 | + | ||
79 | + // grid filtering conditions | ||
80 | + $query->andFilterWhere( | ||
81 | + [ | ||
82 | + 'id' => $this->id, | ||
83 | + 'birth_day' => $this->birth_day, | ||
84 | + 'birth_month' => $this->birth_month, | ||
85 | + 'birth_year' => $this->birth_year, | ||
86 | + 'group_id' => $this->group_id, | ||
87 | + ] | ||
88 | + ); | ||
89 | + | ||
90 | + $query->andFilterWhere( | ||
91 | + [ | ||
92 | + 'like', | ||
93 | + 'username', | ||
94 | + $this->username, | ||
95 | + ] | ||
96 | + ) | ||
97 | + ->andFilterWhere( | ||
98 | + [ | ||
99 | + 'like', | ||
100 | + 'name', | ||
101 | + $this->name, | ||
102 | + ] | ||
103 | + ) | ||
104 | + ->andFilterWhere( | ||
105 | + [ | ||
106 | + 'like', | ||
107 | + 'surname', | ||
108 | + $this->surname, | ||
109 | + ] | ||
110 | + ) | ||
111 | + ->andFilterWhere( | ||
112 | + [ | ||
113 | + 'like', | ||
114 | + 'phone', | ||
115 | + $this->phone, | ||
116 | + ] | ||
117 | + ) | ||
118 | + ->andFilterWhere( | ||
119 | + [ | ||
120 | + 'like', | ||
121 | + 'body', | ||
122 | + $this->body, | ||
123 | + ] | ||
124 | + ); | ||
125 | + | ||
126 | + return $dataProvider; | ||
127 | + } | ||
128 | + } |
1 | +++ a/models/Feedback.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use Yii; | ||
6 | + use yii\behaviors\AttributeBehavior; | ||
7 | + use yii\behaviors\TimestampBehavior; | ||
8 | + use yii\db\ActiveRecord; | ||
9 | + | ||
10 | + /** | ||
11 | + * This is the model class for table "feedback". | ||
12 | + * | ||
13 | + * @property integer $id | ||
14 | + * @property string $name | ||
15 | + * @property string $phone | ||
16 | + * @property integer $created_at | ||
17 | + * @property string $ip | ||
18 | + */ | ||
19 | + class Feedback extends ActiveRecord | ||
20 | + { | ||
21 | + | ||
22 | + const SCENARIO_FEEDBACK = 'feedback'; | ||
23 | + const SCENARIO_CALLBACK = 'callback'; | ||
24 | + | ||
25 | + /** | ||
26 | + * @inheritdoc | ||
27 | + */ | ||
28 | + public static function tableName() | ||
29 | + { | ||
30 | + return 'feedback'; | ||
31 | + } | ||
32 | + | ||
33 | + /** | ||
34 | + * @inheritdoc | ||
35 | + */ | ||
36 | + public function scenarios() | ||
37 | + { | ||
38 | + $scenarios = parent::scenarios(); | ||
39 | + $scenarios = array_merge( | ||
40 | + $scenarios, | ||
41 | + [ | ||
42 | + self::SCENARIO_FEEDBACK => [ | ||
43 | + 'name', | ||
44 | + 'phone', | ||
45 | + ], | ||
46 | + self::SCENARIO_CALLBACK => [ 'phone' ], | ||
47 | + ] | ||
48 | + ); | ||
49 | + return $scenarios; | ||
50 | + } | ||
51 | + | ||
52 | + /** | ||
53 | + * @inheritdoc | ||
54 | + */ | ||
55 | + public function behaviors() | ||
56 | + { | ||
57 | + return [ | ||
58 | + [ | ||
59 | + 'class' => TimestampBehavior::className(), | ||
60 | + 'updatedAtAttribute' => false, | ||
61 | + ], | ||
62 | + [ | ||
63 | + 'class' => AttributeBehavior::className(), | ||
64 | + 'attributes' => [ | ||
65 | + ActiveRecord::EVENT_BEFORE_INSERT => 'ip', | ||
66 | + ], | ||
67 | + 'value' => function ($event) { | ||
68 | + return \Yii::$app->request->userIP; | ||
69 | + }, | ||
70 | + ], | ||
71 | + ]; | ||
72 | + } | ||
73 | + | ||
74 | + /** | ||
75 | + * @inheritdoc | ||
76 | + */ | ||
77 | + public function rules() | ||
78 | + { | ||
79 | + return [ | ||
80 | + [ | ||
81 | + [ | ||
82 | + 'phone', | ||
83 | + 'name', | ||
84 | + ], | ||
85 | + 'required', | ||
86 | + ], | ||
87 | + [ | ||
88 | + [ 'phone' ], | ||
89 | + 'match', | ||
90 | + 'pattern' => '/^\+38\(\d{3}\)\d{3}-\d{2}-\d{2}$/', | ||
91 | + ], | ||
92 | + [ | ||
93 | + [ | ||
94 | + 'name', | ||
95 | + 'phone', | ||
96 | + ], | ||
97 | + 'string', | ||
98 | + 'max' => 255, | ||
99 | + ], | ||
100 | + ]; | ||
101 | + } | ||
102 | + | ||
103 | + /** | ||
104 | + * @inheritdoc | ||
105 | + */ | ||
106 | + public function attributeLabels() | ||
107 | + { | ||
108 | + return [ | ||
109 | + 'id' => Yii::t('app', 'id'), | ||
110 | + 'name' => Yii::t('app', 'name'), | ||
111 | + 'phone' => Yii::t('app', 'phone'), | ||
112 | + 'created_at' => Yii::t('app', 'created_at'), | ||
113 | + 'ip' => Yii::t('app', 'ip'), | ||
114 | + ]; | ||
115 | + } | ||
116 | + } |
1 | +++ a/models/FeedbackSearch.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use yii\data\ActiveDataProvider; | ||
6 | + | ||
7 | + class FeedbackSearch extends Feedback | ||
8 | + { | ||
9 | + | ||
10 | + /** | ||
11 | + * @inheritdoc | ||
12 | + */ | ||
13 | + public function rules() | ||
14 | + { | ||
15 | + return [ | ||
16 | + [ | ||
17 | + [ 'id' ], | ||
18 | + 'integer', | ||
19 | + ], | ||
20 | + [ | ||
21 | + [ | ||
22 | + 'name', | ||
23 | + 'phone', | ||
24 | + 'ip', | ||
25 | + 'created_at', | ||
26 | + ], | ||
27 | + 'safe', | ||
28 | + ], | ||
29 | + ]; | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * @inheritdoc | ||
34 | + */ | ||
35 | + public function scenarios() | ||
36 | + { | ||
37 | + return Feedback::scenarios(); | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * Creates data provider instance with search query applied | ||
42 | + * | ||
43 | + * @param array $params | ||
44 | + * | ||
45 | + * @return ActiveDataProvider | ||
46 | + */ | ||
47 | + public function search($params) | ||
48 | + { | ||
49 | + $query = Feedback::find(); | ||
50 | + | ||
51 | + // add conditions that should always apply here | ||
52 | + | ||
53 | + $dataProvider = new ActiveDataProvider( | ||
54 | + [ | ||
55 | + 'query' => $query, | ||
56 | + ] | ||
57 | + ); | ||
58 | + | ||
59 | + $this->load($params); | ||
60 | + | ||
61 | + if (!$this->validate()) { | ||
62 | + // uncomment the following line if you do not want to return any records when validation fails | ||
63 | + // $query->where('0=1'); | ||
64 | + return $dataProvider; | ||
65 | + } | ||
66 | + | ||
67 | + // grid filtering conditions | ||
68 | + $query->andFilterWhere( | ||
69 | + [ | ||
70 | + 'id' => $this->id, | ||
71 | + ] | ||
72 | + ); | ||
73 | + | ||
74 | + $query->andFilterWhere( | ||
75 | + [ | ||
76 | + 'like', | ||
77 | + 'name', | ||
78 | + $this->name, | ||
79 | + ] | ||
80 | + ) | ||
81 | + ->andFilterWhere( | ||
82 | + [ | ||
83 | + 'like', | ||
84 | + 'phone', | ||
85 | + $this->phone, | ||
86 | + ] | ||
87 | + ) | ||
88 | + ->andFilterWhere( | ||
89 | + [ | ||
90 | + 'like', | ||
91 | + 'ip', | ||
92 | + $this->ip, | ||
93 | + ] | ||
94 | + ); | ||
95 | + | ||
96 | + return $dataProvider; | ||
97 | + } | ||
98 | + } |
1 | +++ a/models/Page.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use common\modules\language\behaviors\LanguageBehavior; | ||
6 | + use yii\db\ActiveQuery; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + use yii\web\Request; | ||
9 | + use Yii; | ||
10 | + | ||
11 | + /** | ||
12 | + * This is the model class for table "page". | ||
13 | + * | ||
14 | + * @property integer $id | ||
15 | + * @property bool $in_menu | ||
16 | + * * From language behavior * | ||
17 | + * @property PageLang $lang | ||
18 | + * @property PageLang[] $langs | ||
19 | + * @property PageLang $objectLang | ||
20 | + * @property string $ownerKey | ||
21 | + * @property string $langKey | ||
22 | + * @property PageLang[] $modelLangs | ||
23 | + * @property bool $transactionStatus | ||
24 | + * @method string getOwnerKey() | ||
25 | + * @method void setOwnerKey( string $value ) | ||
26 | + * @method string getLangKey() | ||
27 | + * @method void setLangKey( string $value ) | ||
28 | + * @method ActiveQuery getLangs() | ||
29 | + * @method ActiveQuery getLang( integer $language_id ) | ||
30 | + * @method PageLang[] generateLangs() | ||
31 | + * @method void loadLangs( Request $request ) | ||
32 | + * @method bool linkLangs() | ||
33 | + * @method bool saveLangs() | ||
34 | + * @method bool getTransactionStatus() | ||
35 | + * * End language behavior * | ||
36 | + */ | ||
37 | + class Page extends ActiveRecord | ||
38 | + { | ||
39 | + | ||
40 | + public $title; | ||
41 | + | ||
42 | + /** | ||
43 | + * @inheritdoc | ||
44 | + */ | ||
45 | + public static function tableName() | ||
46 | + { | ||
47 | + return 'page'; | ||
48 | + } | ||
49 | + | ||
50 | + /** | ||
51 | + * @inheritdoc | ||
52 | + */ | ||
53 | + public function behaviors() | ||
54 | + { | ||
55 | + return [ | ||
56 | + 'language' => [ | ||
57 | + 'class' => LanguageBehavior::className(), | ||
58 | + ], | ||
59 | + ]; | ||
60 | + } | ||
61 | + | ||
62 | + /** | ||
63 | + * @inheritdoc | ||
64 | + */ | ||
65 | + public function rules() | ||
66 | + { | ||
67 | + return [ | ||
68 | + [ | ||
69 | + [ 'title' ], | ||
70 | + 'safe', | ||
71 | + ], | ||
72 | + [ | ||
73 | + [ | ||
74 | + 'in_menu', | ||
75 | + ], | ||
76 | + 'boolean', | ||
77 | + ], | ||
78 | + ]; | ||
79 | + } | ||
80 | + | ||
81 | + /** | ||
82 | + * @inheritdoc | ||
83 | + */ | ||
84 | + public function attributeLabels() | ||
85 | + { | ||
86 | + return [ | ||
87 | + 'id' => Yii::t('app', 'id'), | ||
88 | + 'in_menu' => Yii::t('app', 'in_menu'), | ||
89 | + ]; | ||
90 | + } | ||
91 | + } |
1 | +++ a/models/PageLang.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use common\modules\language\models\Language; | ||
6 | + use Yii; | ||
7 | + use yii\db\ActiveRecord; | ||
8 | + | ||
9 | + /** | ||
10 | + * This is the model class for table "page_lang". | ||
11 | + * | ||
12 | + * @property integer $page_id | ||
13 | + * @property integer $language_id | ||
14 | + * @property string $title | ||
15 | + * @property string $body | ||
16 | + * @property string $meta_title | ||
17 | + * @property string $meta_keywords | ||
18 | + * @property string $meta_description | ||
19 | + * @property string $seo_text | ||
20 | + * @property string $h1 | ||
21 | + * @property string $alias | ||
22 | + * @property Language $language | ||
23 | + * @property Page $page | ||
24 | + */ | ||
25 | + class PageLang extends ActiveRecord | ||
26 | + { | ||
27 | + | ||
28 | + public static function primaryKey() | ||
29 | + { | ||
30 | + return [ | ||
31 | + 'page_id', | ||
32 | + 'language_id', | ||
33 | + ]; | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * @inheritdoc | ||
38 | + */ | ||
39 | + public static function tableName() | ||
40 | + { | ||
41 | + return 'page_lang'; | ||
42 | + } | ||
43 | + | ||
44 | + public function behaviors() | ||
45 | + { | ||
46 | + return [ | ||
47 | + 'slug' => [ | ||
48 | + 'class' => 'common\behaviors\Slug', | ||
49 | + ], | ||
50 | + ]; | ||
51 | + } | ||
52 | + | ||
53 | + /** | ||
54 | + * @inheritdoc | ||
55 | + */ | ||
56 | + public function rules() | ||
57 | + { | ||
58 | + return [ | ||
59 | + [ | ||
60 | + [ | ||
61 | + 'title', | ||
62 | + 'body', | ||
63 | + ], | ||
64 | + 'required', | ||
65 | + ], | ||
66 | + [ | ||
67 | + [ | ||
68 | + 'body', | ||
69 | + 'seo_text', | ||
70 | + ], | ||
71 | + 'string', | ||
72 | + ], | ||
73 | + [ | ||
74 | + [ | ||
75 | + 'title', | ||
76 | + 'meta_title', | ||
77 | + 'meta_keywords', | ||
78 | + 'meta_description', | ||
79 | + 'h1', | ||
80 | + 'alias', | ||
81 | + ], | ||
82 | + 'string', | ||
83 | + 'max' => 255, | ||
84 | + ], | ||
85 | + [ | ||
86 | + [ | ||
87 | + 'page_id', | ||
88 | + 'language_id', | ||
89 | + ], | ||
90 | + 'unique', | ||
91 | + 'targetAttribute' => [ | ||
92 | + 'page_id', | ||
93 | + 'language_id', | ||
94 | + ], | ||
95 | + 'message' => 'The combination of Page ID and Language ID has already been taken.', | ||
96 | + ], | ||
97 | + [ | ||
98 | + [ 'language_id' ], | ||
99 | + 'exist', | ||
100 | + 'skipOnError' => true, | ||
101 | + 'targetClass' => Language::className(), | ||
102 | + 'targetAttribute' => [ 'language_id' => 'id' ], | ||
103 | + ], | ||
104 | + [ | ||
105 | + [ 'page_id' ], | ||
106 | + 'exist', | ||
107 | + 'skipOnError' => true, | ||
108 | + 'targetClass' => Page::className(), | ||
109 | + 'targetAttribute' => [ 'page_id' => 'id' ], | ||
110 | + ], | ||
111 | + ]; | ||
112 | + } | ||
113 | + | ||
114 | + /** | ||
115 | + * @inheritdoc | ||
116 | + */ | ||
117 | + public function attributeLabels() | ||
118 | + { | ||
119 | + return [ | ||
120 | + 'page_id' => Yii::t('app', 'page_id'), | ||
121 | + 'language_id' => Yii::t('app', 'language_id'), | ||
122 | + 'title' => Yii::t('app', 'title'), | ||
123 | + 'body' => Yii::t('app', 'body'), | ||
124 | + 'meta_title' => Yii::t('app', 'meta_title'), | ||
125 | + 'meta_keywords' => Yii::t('app', 'meta_keywords'), | ||
126 | + 'meta_description' => Yii::t('app', 'meta_description'), | ||
127 | + 'seo_text' => Yii::t('app', 'seo_text'), | ||
128 | + 'h1' => Yii::t('app', 'h1'), | ||
129 | + 'alias' => Yii::t('app', 'alias'), | ||
130 | + ]; | ||
131 | + } | ||
132 | + | ||
133 | + /** | ||
134 | + * @return \yii\db\ActiveQuery | ||
135 | + */ | ||
136 | + public function getLanguage() | ||
137 | + { | ||
138 | + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); | ||
139 | + } | ||
140 | + | ||
141 | + /** | ||
142 | + * @return \yii\db\ActiveQuery | ||
143 | + */ | ||
144 | + public function getPage() | ||
145 | + { | ||
146 | + return $this->hasOne(Page::className(), [ 'id' => 'page_id' ]); | ||
147 | + } | ||
148 | + } |
1 | +++ a/models/PageSearch.php | ||
1 | +<?php | ||
2 | + | ||
3 | + namespace common\models; | ||
4 | + | ||
5 | + use yii\base\Model; | ||
6 | + use yii\data\ActiveDataProvider; | ||
7 | + | ||
8 | + /** | ||
9 | + * PageSearch represents the model behind the search form about `common\models\Page`. | ||
10 | + */ | ||
11 | + class PageSearch extends Page | ||
12 | + { | ||
13 | + | ||
14 | + public $title; | ||
15 | + | ||
16 | + /** | ||
17 | + * @inheritdoc | ||
18 | + */ | ||
19 | + public function rules() | ||
20 | + { | ||
21 | + return [ | ||
22 | + [ | ||
23 | + [ 'id' ], | ||
24 | + 'integer', | ||
25 | + ], | ||
26 | + [ | ||
27 | + [ 'title' ], | ||
28 | + 'safe', | ||
29 | + ], | ||
30 | + [ | ||
31 | + [ 'in_menu' ], | ||
32 | + 'boolean', | ||
33 | + ], | ||
34 | + ]; | ||
35 | + } | ||
36 | + | ||
37 | + /** | ||
38 | + * @inheritdoc | ||
39 | + */ | ||
40 | + public function scenarios() | ||
41 | + { | ||
42 | + // bypass scenarios() implementation in the parent class | ||
43 | + return Model::scenarios(); | ||
44 | + } | ||
45 | + | ||
46 | + public function behaviors() | ||
47 | + { | ||
48 | + return []; | ||
49 | + } | ||
50 | + | ||
51 | + /** | ||
52 | + * Creates data provider instance with search query applied | ||
53 | + * | ||
54 | + * @param array $params | ||
55 | + * | ||
56 | + * @return ActiveDataProvider | ||
57 | + */ | ||
58 | + public function search($params) | ||
59 | + { | ||
60 | + $query = Page::find() | ||
61 | + ->joinWith('lang'); | ||
62 | + | ||
63 | + // add conditions that should always apply here | ||
64 | + | ||
65 | + $dataProvider = new ActiveDataProvider( | ||
66 | + [ | ||
67 | + 'query' => $query, | ||
68 | + 'sort' => [ | ||
69 | + 'attributes' => [ | ||
70 | + 'title' => [ | ||
71 | + 'asc' => [ 'page_lang.title' => SORT_ASC ], | ||
72 | + 'desc' => [ 'page_lang.title' => SORT_DESC ], | ||
73 | + ], | ||
74 | + 'id', | ||
75 | + 'in_menu', | ||
76 | + ], | ||
77 | + ], | ||
78 | + ] | ||
79 | + ); | ||
80 | + | ||
81 | + $this->load($params); | ||
82 | + | ||
83 | + if (!$this->validate()) { | ||
84 | + // uncomment the following line if you do not want to return any records when validation fails | ||
85 | + // $query->where('0=1'); | ||
86 | + return $dataProvider; | ||
87 | + } | ||
88 | + | ||
89 | + // grid filtering conditions | ||
90 | + $query->andFilterWhere( | ||
91 | + [ | ||
92 | + 'id' => $this->id, | ||
93 | + 'in_menu' => $this->in_menu, | ||
94 | + ] | ||
95 | + ) | ||
96 | + ->andFilterWhere( | ||
97 | + [ | ||
98 | + 'like', | ||
99 | + 'page_lang.title', | ||
100 | + $this->title, | ||
101 | + ] | ||
102 | + ); | ||
103 | + | ||
104 | + return $dataProvider; | ||
105 | + } | ||
106 | + } |
1 | +++ a/translation/ru/app.php | ||
1 | +<?php | ||
2 | + return [ | ||
3 | + 'id' => 'ID', | ||
4 | + 'username' => 'Логин', | ||
5 | + 'cname' => 'Имя', | ||
6 | + 'surname' => 'Фамилия', | ||
7 | + 'auth_key' => 'Ключ аутентификации', | ||
8 | + 'password_hash' => 'Хэш пароля', | ||
9 | + 'password_reset_token' => 'Password Reset Token', | ||
10 | + 'email' => 'Логин (e-mail)', | ||
11 | + 'phone' => 'Телефон', | ||
12 | + 'status' => 'Статус', | ||
13 | + 'gender' => 'Пол', | ||
14 | + 'birth_day' => 'Birth Day', | ||
15 | + 'birth_month' => 'Birth Month', | ||
16 | + 'birth_year' => 'Birth Year', | ||
17 | + 'group_id' => 'Group ID', | ||
18 | + 'created_at' => 'Created At', | ||
19 | + 'updated_at' => 'Updated At', | ||
20 | + 'verifyCode' => 'Код проверки', | ||
21 | + 'password' => 'Пароль', | ||
22 | + 'password_repeat' => 'Повторить пароль', | ||
23 | + 'registration' => 'Регистрация', | ||
24 | + 'message' => 'Этот {field} уже занят', | ||
25 | + 'message_match_password' => 'Пароли не совпадают', | ||
26 | + 'exit' => 'Выход', | ||
27 | + 'enter' => 'Войти', | ||
28 | + 'your_personal_area' => 'Вход в личный кабинет', | ||
29 | + 'forgot_password' => 'Забыли пароль?', | ||
30 | + 'rememberMe' => 'Запомнить меня', | ||
31 | + 'articles' => 'Всего товаров', | ||
32 | + 'code' => 'Код: {0}', | ||
33 | + 'checkout' => 'оформить заказ', | ||
34 | + 'sum' => 'Сумма', | ||
35 | + 'continue_shopping' => 'продолжить покупки', | ||
36 | + 'edit_personal_data' => 'Редактировать личные данные', | ||
37 | + 'personal_data' => 'Личные данные', | ||
38 | + 'my_orders' => 'Мои заказы', | ||
39 | + 'bookmarks' => 'Закладки', | ||
40 | + 'basket' => 'Корзина', | ||
41 | + 'banner_id' => 'Banner ID', | ||
42 | + 'image' => 'Изображение', | ||
43 | + 'alt' => 'Описание', | ||
44 | + 'title' => 'Заголовок', | ||
45 | + 'url' => 'Ссылка', | ||
46 | + 'width' => 'Ширина', | ||
47 | + 'height' => 'Высота', | ||
48 | + 'blog_id' => 'Blog ID', | ||
49 | + 'user_id' => 'User ID', | ||
50 | + 'name' => 'Название', | ||
51 | + 'link' => 'Ссылка', | ||
52 | + 'user_add_id' => 'User Add ID', | ||
53 | + 'view_count' => 'Количество просмотров', | ||
54 | + 'description' => 'Описание', | ||
55 | + 'cover' => 'Фото главное', | ||
56 | + 'event_id' => 'Event ID', | ||
57 | + 'alias' => 'Ссылка', | ||
58 | + 'body' => 'Тело', | ||
59 | + 'meta_title' => 'Мета заголовок', | ||
60 | + 'h1' => 'H1', | ||
61 | + 'seo_text' => 'Сео Текст', | ||
62 | + 'end_at' => 'Срок действия по', | ||
63 | + 'order_items_id' => 'Order Items ID', | ||
64 | + 'order_id' => 'Order ID', | ||
65 | + 'item_id' => 'Item ID', | ||
66 | + 'item_count' => 'Количество', | ||
67 | + 'price' => 'Цена', | ||
68 | + 'customer_id' => 'Customer ID', | ||
69 | + 'delivery' => 'Доставка', | ||
70 | + 'payment' => 'Оплата', | ||
71 | + 'seo_id' => 'Seo ID', | ||
72 | + 'controller' => 'Controller', | ||
73 | + 'seo_category_id' => 'Seo Category ID', | ||
74 | + 'seo_dynamic_id' => 'Seo Dynamic ID', | ||
75 | + 'action' => 'Action', | ||
76 | + 'fields' => 'Поля', | ||
77 | + 'param' => 'Параметры', | ||
78 | + 'key' => 'Ключ', | ||
79 | + 'service_id' => 'Service ID', | ||
80 | + 'slider_id' => 'Slider ID', | ||
81 | + 'speed' => 'Скорость', | ||
82 | + 'duration' => 'Продолжительность', | ||
83 | + 'slider_image_id' => 'Slider Image ID', | ||
84 | + 'sort' => 'Сортировка', | ||
85 | + 'order_name' => 'Ф.И.О', | ||
86 | + 'order_phone' => 'Контактный телефон', | ||
87 | + 'order_email' => 'email', | ||
88 | + 'order_comment' => 'Комментарии', | ||
89 | + 'articlesID' => 'ID', | ||
90 | + 'articlesDate' => 'Дата', | ||
91 | + 'articlesImage' => 'Изображение', | ||
92 | + 'lang-Articles ID' => '', | ||
93 | + 'lang-Language ID' => '', | ||
94 | + 'lang-Title' => '', | ||
95 | + 'lang-Body' => '', | ||
96 | + 'lang-Meta Title' => '', | ||
97 | + 'lang-Meta Keywords' => '', | ||
98 | + 'lang-Meta Description' => '', | ||
99 | + 'lang-Seo Text' => '', | ||
100 | + 'lang-H1' => '', | ||
101 | + 'lang-Body Preview' => '', | ||
102 | + 'language_id' => '', | ||
103 | + 'bg_id' => '', | ||
104 | + 'feedback_id' => 'Feedback ID', | ||
105 | + 'ip' => 'IP', | ||
106 | + 'productName' => 'Продукт', | ||
107 | + 'op_name' => 'Вид', | ||
108 | + 'art' => 'Артикул', | ||
109 | + 'cost' => 'Цена за один', | ||
110 | + 'count' => 'Кол.', | ||
111 | + 'sumCost' => 'Сумма', | ||
112 | + 'in_menu' => 'Show in menu', | ||
113 | + 'page_id' => 'Page ID', | ||
114 | + 'meta_keywords' => 'Meta Keywords', | ||
115 | + 'meta_description' => 'Meta Description', | ||
116 | + 'product_spec_id' => 'Product Spec ID', | ||
117 | + 'product_id' => 'Product ID', | ||
118 | + 'tech_spec_link' => 'Tech Spec Link', | ||
119 | + 'tech_char_link' => 'Tech Char Link', | ||
120 | + 'techSpecFile' => 'techSpecFile', | ||
121 | + 'techCharFile' => 'techCharFile', | ||
122 | + 'tech_spec_text' => 'Tech Spec Text', | ||
123 | + 'instruction' => 'Instruction', | ||
124 | + 'product_to_project_id' => 'Product To Project ID', | ||
125 | + 'product_variant_id' => 'Product Variant ID', | ||
126 | + 'project_id' => 'Project ID', | ||
127 | + 'product_to_rating_id' => 'Product To Rating ID', | ||
128 | + 'value' => 'Value', | ||
129 | + 'images' => 'Images', | ||
130 | + 'project_image_id' => 'Project Image ID', | ||
131 | + 'meta' => 'Meta', | ||
132 | + 'date_time' => 'Дата', | ||
133 | + 'template_location_id' => 'Template Location ID', | ||
134 | + 'template_location_name' => 'Template Location Name', | ||
135 | + 'template_location_title' => 'Template Location Title', | ||
136 | + 'is_slider' => 'Is Slider', | ||
137 | + 'is_banner' => 'Is Banner', | ||
138 | + 'order_delivery_id' => 'order Delivery ID', | ||
139 | + 'text' => 'Text', | ||
140 | + 'emailis' => 'Такой email уже есть.', | ||
141 | + 'меню' => 'меню', | ||
142 | + 'контрактный отдел' => 'контрактный отдел', | ||
143 | + 'отдел по работе с дизайнерами и архитекторами' => 'отдел по работе с дизайнерами и архитекторами', | ||
144 | + 'диз_арх_2' => 'отдел по работе<br/>с дизайнерами<br/>и архитекторами', | ||
145 | + 'search' => 'поиск', | ||
146 | + 'copy1' => '2003 - 2016 Все права защищены и охраняются действующим законодательством Украины.', | ||
147 | + 'copy2' => 'Использование материалов с данного сайта возможно только с письменного разрешения компании ООО «Витекс Украина».', | ||
148 | + 'form1' => 'Запрос на просчет', | ||
149 | + 'comment' => 'комментарий', | ||
150 | + 'submit' => 'Отправить', | ||
151 | + 'Сертификаты' => 'Сертификаты', | ||
152 | + 'Монтаж, уборка, уход' => 'Монтаж, уборка, уход', | ||
153 | + 'Галерея объектов' => 'Галерея объектов', | ||
154 | + 'скачать' => 'скачать', | ||
155 | + 'Документ технической документации' => 'Документ технической документации', | ||
156 | + 'Вы также можете скачать таблицу с ' => 'Вы также можете скачать таблицу с ', | ||
157 | + 'техническими характеристиками' => 'техническими характеристиками', | ||
158 | + 'Номер по каталогу' => 'Номер по каталогу', | ||
159 | + 'Заказать образец' => 'Заказать образец', | ||
160 | + 'Технические характеристики' => 'Технические характеристики', | ||
161 | + 'Контрактные продукты' => 'Контрактные продукты', | ||
162 | + 'Статьи' => 'Статьи', | ||
163 | + 'Контакты' => 'Контакты', | ||
164 | + 'Отправить запрос' => 'Отправить запрос', | ||
165 | + 'Создание сайтов' => 'Создание сайтов', | ||
166 | + 'Подробнее' => 'Подробнее', | ||
167 | + 'Запрос на просчет' => 'Запрос на просчет', | ||
168 | + 'comment2' => 'Комментарий', | ||
169 | + 'продолжить выбор' => 'продолжить выбор', | ||
170 | + 'Отправить' => 'Отправить', | ||
171 | + 'success1' => 'Ваша заявка принята.', | ||
172 | + 'success2' => 'Мы свяжемся в Вами в ближайшее время', | ||
173 | + 'Поиск' => 'Поиск', | ||
174 | + 'Результаты поиска для' => 'Результаты поиска для', | ||
175 | + 'certs' => 'Сертификаты', | ||
176 | + ]; | ||
0 | \ No newline at end of file | 177 | \ No newline at end of file |
1 | +++ a/translation/ru/blog.php | ||
1 | +<?php | ||
2 | + return [ | ||
3 | + 'Select category' => 'Выберите категорию ...', | ||
4 | + 'Select tag' => 'Выберите тэг ...', | ||
5 | + 'Has no parent rubric' => 'Без категории', | ||
6 | + 'Waiting for results' => 'Загрузка ...', | ||
7 | + 'Select related products' => 'Выберите сопутствующие товары', | ||
8 | + 'Select related articles' => 'Выберите статьи', | ||
9 | + 'Blog Articles' => 'Статьи блога', | ||
10 | + 'Create Blog Article' => 'Создать статью', | ||
11 | + 'Update Blog Article: ' => 'Обновить статью: ', | ||
12 | + 'Not active' => 'Не активна', | ||
13 | + 'Active' => 'Активна', | ||
14 | + 'Are you sure you want to delete this item?' => 'Вы точно хотите это удалить ?', | ||
15 | + 'Update' => 'Обновить', | ||
16 | + 'Blog Categories' => 'Рубрики', | ||
17 | + 'Create Blog Category' => 'Создать рубрику', | ||
18 | + 'Update Blog Category: ' => 'Обновить рубрику: ', | ||
19 | + 'Blog Tags' => 'Тэги', | ||
20 | + 'Create Blog Tag' => 'Создать тэг', | ||
21 | + 'Update Blog Tag: ' => 'Обновить тэг: ', | ||
22 | + ]; | ||
0 | \ No newline at end of file | 23 | \ No newline at end of file |
1 | +++ a/translation/ru/product.php | ||
1 | +<?php | ||
2 | + return [ | ||
3 | + 'Categories' => 'Категории', | ||
4 | + 'Create Category' => 'Создать Категорию', | ||
5 | + 'Name' => 'Наименование', | ||
6 | + 'Remote ID' => 'ID в 1С', | ||
7 | + 'Search for "{keywords}"' => 'Поиск по "{keywords}"', | ||
8 | + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"', | ||
9 | + 'Promo products' => 'Акционные товары', | ||
10 | + 'New products' => 'Новинки', | ||
11 | + 'Top products' => 'Популярные', | ||
12 | + 'Similar products' => 'Похожие товары', | ||
13 | + 'Brands' => 'Бренды', | ||
14 | + 'Brand' => 'Бренд', | ||
15 | + 'Category' => 'Категория', | ||
16 | + 'Select brand' => 'Выберите бренд', | ||
17 | + 'Select category' => 'Выберите категорию', | ||
18 | + 'SKU' => 'Артикул', | ||
19 | + 'Stock' => 'Остаток', | ||
20 | + 'Price' => 'Цена', | ||
21 | + 'Price Old' => 'Старая Цена', | ||
22 | + 'Products' => 'Товары', | ||
23 | + 'Product' => 'Товар', | ||
24 | + 'Variants' => 'Модификации', | ||
25 | + 'Variant' => 'Модификация', | ||
26 | + 'Create Product' => 'Создать Товар', | ||
27 | + 'Enable' => 'Доступно', | ||
28 | + 'Disable' => 'Отсутсвует', | ||
29 | + ]; | ||
0 | \ No newline at end of file | 30 | \ No newline at end of file |
1 | +++ a/translation/ua/app.php | ||
1 | +<?php | ||
2 | + return [ | ||
3 | + 'id' => 'ID', | ||
4 | + 'username' => "Логін", | ||
5 | + 'cname' => 'Ім\'я', | ||
6 | + 'surname' => 'Фамилия', | ||
7 | + 'auth_key' => 'Ключ аутентифікації', | ||
8 | + 'password_hash' => 'Хеш паролю', | ||
9 | + 'password_reset_token' => 'Password Reset Token', | ||
10 | + 'email' => 'Логін (e-mail)', | ||
11 | + 'phone' => 'Телефон', | ||
12 | + 'status' => 'Статус', | ||
13 | + 'gender' => 'Пол', | ||
14 | + 'birth_day' => 'Birth Day', | ||
15 | + 'birth_month' => 'Birth Month', | ||
16 | + 'birth_year' => 'Birth Year', | ||
17 | + 'group_id' => 'Group ID', | ||
18 | + 'created_at' => 'Створено', | ||
19 | + 'updated_at' => 'Оновлено', | ||
20 | + 'verifyCode' => 'Код перевірки', | ||
21 | + 'password' => 'Пароль', | ||
22 | + 'password_repeat' => 'Повторити пароль', | ||
23 | + 'registration' => 'Реєстрація', | ||
24 | + 'message' => 'Цей {field} вже зайнято', | ||
25 | + 'message_match_password' => 'Пароли не совпадают', | ||
26 | + 'exit' => 'Вихід', | ||
27 | + 'enter' => 'Війти', | ||
28 | + 'your_personal_area' => 'Вхід в особистий кабінет', | ||
29 | + 'forgot_password' => 'Забули пароль?', | ||
30 | + 'rememberMe' => 'Запам\'ятати мене', | ||
31 | + 'articles' => 'Всього товарів', | ||
32 | + 'code' => 'Код: {0}', | ||
33 | + 'checkout' => 'оформити замовлення', | ||
34 | + 'sum' => 'Сума', | ||
35 | + 'continue_shopping' => 'продовжити покупки', | ||
36 | + 'edit_personal_data' => 'Редагувати особисті дані', | ||
37 | + 'personal_data' => 'Особисті дані', | ||
38 | + 'my_orders' => 'Мої замовлення', | ||
39 | + 'bookmarks' => 'Закладки', | ||
40 | + 'basket' => 'Кошик', | ||
41 | + 'banner_id' => 'Banner ID', | ||
42 | + 'image' => 'Зображення', | ||
43 | + 'alt' => 'Опис', | ||
44 | + 'title' => 'Заголовок', | ||
45 | + 'url' => 'Посилання', | ||
46 | + 'width' => 'Ширина', | ||
47 | + 'height' => 'Висота', | ||
48 | + 'blog_id' => 'Blog ID', | ||
49 | + 'user_id' => 'User ID', | ||
50 | + 'name' => 'Назва', | ||
51 | + 'link' => 'Посилання', | ||
52 | + 'user_add_id' => 'User Add ID', | ||
53 | + 'view_count' => 'Кількість переглядів', | ||
54 | + 'description' => 'Опис', | ||
55 | + 'cover' => 'Фото головне', | ||
56 | + 'event_id' => 'Event ID', | ||
57 | + 'alias' => 'Посилання', | ||
58 | + 'body' => 'Тіло', | ||
59 | + 'meta_title' => 'Мета заголовок', | ||
60 | + 'h1' => 'H1', | ||
61 | + 'seo_text' => 'Сео Текст', | ||
62 | + 'end_at' => 'Термін дії до', | ||
63 | + 'order_items_id' => 'Order Items ID', | ||
64 | + 'order_id' => 'Order ID', | ||
65 | + 'item_id' => 'Item ID', | ||
66 | + 'item_count' => 'Кількість', | ||
67 | + 'price' => 'Ціна', | ||
68 | + 'customer_id' => 'Customer ID', | ||
69 | + 'delivery' => 'Доставка', | ||
70 | + 'payment' => 'Оплата', | ||
71 | + 'seo_id' => 'Seo ID', | ||
72 | + 'controller' => 'Controller', | ||
73 | + 'seo_category_id' => 'Seo Category ID', | ||
74 | + 'seo_dynamic_id' => 'Seo Dynamic ID', | ||
75 | + 'action' => 'Action', | ||
76 | + 'fields' => 'Поля', | ||
77 | + 'param' => 'Параметри', | ||
78 | + 'key' => 'Ключ', | ||
79 | + 'service_id' => 'Service ID', | ||
80 | + 'slider_id' => 'Slider ID', | ||
81 | + 'speed' => 'Швидкість', | ||
82 | + 'duration' => 'Тривалість', | ||
83 | + 'slider_image_id' => 'Slider Image ID', | ||
84 | + 'sort' => 'Сортування', | ||
85 | + 'order_name' => 'П.І.Б.', | ||
86 | + 'order_phone' => 'Контактний телефон', | ||
87 | + 'order_email' => 'email', | ||
88 | + 'order_comment' => 'Коментар', | ||
89 | + 'articlesID' => 'ID', | ||
90 | + 'articlesDate' => 'Дата', | ||
91 | + 'articlesImage' => 'Зображення', | ||
92 | + 'lang-Articles ID' => '', | ||
93 | + 'lang-Language ID' => '', | ||
94 | + 'lang-Title' => '', | ||
95 | + 'lang-Body' => '', | ||
96 | + 'lang-Meta Title' => '', | ||
97 | + 'lang-Meta Keywords' => '', | ||
98 | + 'lang-Meta Description' => '', | ||
99 | + 'lang-Seo Text' => '', | ||
100 | + 'lang-H1' => '', | ||
101 | + 'lang-Body Preview' => '', | ||
102 | + 'language_id' => '', | ||
103 | + 'bg_id' => '', | ||
104 | + 'feedback_id' => 'Feedback ID', | ||
105 | + 'ip' => 'IP', | ||
106 | + 'productName' => 'Продукт', | ||
107 | + 'op_name' => 'Вид', | ||
108 | + 'art' => 'Артикул', | ||
109 | + 'cost' => 'Цена за один', | ||
110 | + 'count' => 'Кол.', | ||
111 | + 'sumCost' => 'Сумма', | ||
112 | + 'in_menu' => 'Show in menu', | ||
113 | + 'page_id' => 'Page ID', | ||
114 | + 'meta_keywords' => 'Meta Keywords', | ||
115 | + 'meta_description' => 'Meta Description', | ||
116 | + 'product_spec_id' => 'Product Spec ID', | ||
117 | + 'product_id' => 'Product ID', | ||
118 | + 'tech_spec_link' => 'Tech Spec Link', | ||
119 | + 'tech_char_link' => 'Tech Char Link', | ||
120 | + 'techSpecFile' => 'techSpecFile', | ||
121 | + 'techCharFile' => 'techCharFile', | ||
122 | + 'tech_spec_text' => 'Tech Spec Text', | ||
123 | + 'instruction' => 'Instruction', | ||
124 | + 'product_to_project_id' => 'Product To Project ID', | ||
125 | + 'product_variant_id' => 'Product Variant ID', | ||
126 | + 'project_id' => 'Project ID', | ||
127 | + 'product_to_rating_id' => 'Product To Rating ID', | ||
128 | + 'value' => 'Value', | ||
129 | + 'images' => 'Images', | ||
130 | + 'project_image_id' => 'Project Image ID', | ||
131 | + 'meta' => 'Meta', | ||
132 | + 'date_time' => 'Дата', | ||
133 | + 'template_location_id' => 'Template Location ID', | ||
134 | + 'template_location_name' => 'Template Location Name', | ||
135 | + 'template_location_title' => 'Template Location Title', | ||
136 | + 'is_slider' => 'Is Slider', | ||
137 | + 'is_banner' => 'Is Banner', | ||
138 | + 'order_delivery_id' => 'order Delivery ID', | ||
139 | + 'text' => 'Text', | ||
140 | + 'emailis' => 'Такой email уже есть.', | ||
141 | + 'меню' => 'меню', | ||
142 | + 'контрактный отдел' => 'контрактне відділення', | ||
143 | + 'отдел по работе с дизайнерами и архитекторами' => 'відділення по роботі з дизайнерами та архітекторами', | ||
144 | + 'диз_арх_2' => 'відділення по роботі<br/>з дизайнерами<br/>та архітекторами', | ||
145 | + 'search' => 'пошук', | ||
146 | + 'copy1' => '2003 - 2016 Всі права захищені та охорняються чинним законодавством України.', | ||
147 | + 'copy2' => 'Використання матеріалів з даного сайту можливе лише з письмого дозволу компанії ТОВ «Вітекс Україна».', | ||
148 | + 'form1' => 'Запит на прорахування', | ||
149 | + 'comment' => 'коментар', | ||
150 | + 'submit' => 'Відправити', | ||
151 | + 'Сертификаты' => 'Сертифікати', | ||
152 | + 'Монтаж, уборка, уход' => 'Монтаж, прибирання, догляд', | ||
153 | + 'Галерея объектов' => "Галерея об'єктів", | ||
154 | + 'скачать' => 'завантажити', | ||
155 | + 'Документ технической документации' => 'Документ технічної документації', | ||
156 | + 'Вы также можете скачать таблицу с ' => 'Ви також можете завантажити таблицю з ', | ||
157 | + 'техническими характеристиками' => 'технічними характеристиками', | ||
158 | + 'Номер по каталогу' => 'Номер за каталогом', | ||
159 | + 'Заказать образец' => 'Замовити зразок', | ||
160 | + 'Технические характеристики' => 'Технічні характеристики', | ||
161 | + 'Контрактные продукты' => 'Контрактні продукти', | ||
162 | + 'Статьи' => 'Статті', | ||
163 | + 'Контакты' => 'Контакти', | ||
164 | + 'Отправить запрос' => 'Надіслати запит', | ||
165 | + 'Создание сайтов' => 'Створення сайтів', | ||
166 | + 'Подробнее' => 'Подробнее', | ||
167 | + 'Запрос на просчет' => 'Запит на прорахування', | ||
168 | + 'comment2' => 'Коментар', | ||
169 | + 'продолжить выбор' => 'продовжити вибір', | ||
170 | + 'Отправить' => 'Відправити', | ||
171 | + 'success1' => 'Ваша заявка прийнята.', | ||
172 | + 'success2' => "Ми зв'яжемось з Вами найближчим часом", | ||
173 | + 'Поиск' => 'Пошук', | ||
174 | + 'Результаты поиска для' => 'Результати пошуку для', | ||
175 | + 'certs' => 'Сертифікати', | ||
176 | + ]; | ||
0 | \ No newline at end of file | 177 | \ No newline at end of file |
1 | +++ a/translation/ua/product.php | ||
1 | +<?php | ||
2 | + return [ | ||
3 | + 'Categories' => 'Категории', | ||
4 | + 'Create Category' => 'Создать Категорию', | ||
5 | + 'Name' => 'Наименование', | ||
6 | + 'Remote ID' => 'ID в 1С', | ||
7 | + 'Search for "{keywords}"' => 'Поиск по "{keywords}"', | ||
8 | + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"', | ||
9 | + 'Promo products' => 'Акционные товары', | ||
10 | + 'New products' => 'Новинки', | ||
11 | + 'Top products' => 'Популярные', | ||
12 | + 'Similar products' => 'Похожие товары', | ||
13 | + 'Brands' => 'Бренды', | ||
14 | + 'Brand' => 'Бренд', | ||
15 | + 'Category' => 'Категория', | ||
16 | + 'Select brand' => 'Выберите бренд', | ||
17 | + 'Select category' => 'Выберите категорию', | ||
18 | + 'SKU' => 'Артикул', | ||
19 | + 'Stock' => 'Остаток', | ||
20 | + 'Price' => 'Цена', | ||
21 | + 'Price Old' => 'Старая Цена', | ||
22 | + 'Products' => 'Товары', | ||
23 | + 'Product' => 'Товар', | ||
24 | + 'Variants' => 'Модификации', | ||
25 | + 'Variant' => 'Модификация', | ||
26 | + 'Create Product' => 'Создать Товар', | ||
27 | + 'Enable' => 'Доступно', | ||
28 | + 'Disable' => 'Отсутсвует', | ||
29 | + ]; | ||
0 | \ No newline at end of file | 30 | \ No newline at end of file |