Commit 622f41fdfed151bacb63f30e90673fbd7f84a08f
Merge remote-tracking branch 'origin/master'
# Conflicts: # frontend/controllers/ServiceController.php
Showing
10 changed files
with
614 additions
and
85 deletions
Show diff stats
1 | +<?php | |
2 | + | |
3 | +/** | |
4 | + * ====================================================================================================================| | |
5 | + * Компонент, который служит для обрезания строк по заданным параметрам | |
6 | + * ====================================================================================================================| | |
7 | + */ | |
8 | + | |
9 | +namespace common\components; | |
10 | + | |
11 | +use yii\base\Object; | |
12 | + | |
13 | +/** | |
14 | + * Class Substringer | |
15 | + * @package common\components | |
16 | + * ====================================================================================================================| | |
17 | + * @method @static simpleStringSubstring | |
18 | + * @method @static changeStringByRegex | |
19 | + * ====================================================================================================================| | |
20 | + */ | |
21 | +class Substringer extends Object | |
22 | +{ | |
23 | + | |
24 | + | |
25 | + /** | |
26 | + * @param string $haystack | |
27 | + * @param string $needle | |
28 | + * @param bool $reverse | |
29 | + * @return string | |
30 | + *=================================================================================================================| | |
31 | + * @static | |
32 | + * Метод, который берет строку, и обрезает её до заданного момента | |
33 | + *=================================================================================================================| | |
34 | + * @todo | |
35 | + * Пока что метод адекватно работает только при том условии, что нужный нам символ/комбинация символов является | |
36 | + * первой при поисковом запросе, нужно допилить логику, если например: | |
37 | + * $haystack='www.site.com?sort=title_act&sort=category-1&sort=test_val_desc | |
38 | + * то, обрезать всё до нужной комбинации | |
39 | + *=================================================================================================================| | |
40 | + * @example 1 | |
41 | + * Start Data: | |
42 | + * $haystack='https://www.youtube.com?v=OBwS66EBUcY | |
43 | + * $needle='?'; | |
44 | + * Return result: | |
45 | + * $res='https://www.youtube.com/watch'; | |
46 | + * @example 2 | |
47 | + * Start data | |
48 | + * $haystack='https://www.youtube.com/watch?v=OBwS66EBUcY'; | |
49 | + * $needle ='?'; | |
50 | + * $res = '?v=OBwS66EBUcY' | |
51 | + * | |
52 | + */ | |
53 | + public static function simpleStringSubstring(string $haystack, string $needle): string | |
54 | + { | |
55 | + $deletePosition = strpos($haystack, $needle); | |
56 | + if ($deletePosition == 0) | |
57 | + return $haystack; | |
58 | + $result = substr($haystack, 0, $deletePosition); | |
59 | + return $result; | |
60 | + } | |
61 | + | |
62 | + | |
63 | + | |
64 | + | |
65 | + | |
66 | + | |
67 | + | |
68 | + /** | |
69 | + * @param string $haystack | |
70 | + * @param string $regex1 | |
71 | + * @param string $regex2 | |
72 | + * @param string $requiredDelimiter | |
73 | + * @param string $firstConcatenateSymbol | |
74 | + * @param string $secondConcatenateSymbol | |
75 | + * @return string | |
76 | + * ================================================================================================================| | |
77 | + * Метод, который берет $haystack, а так же 2 Regex паттерна | |
78 | + * 1) пытается найти regex совпадения | |
79 | + * если их нет @return original $haystack | |
80 | + * 2) вырезает всё лишнее со строки с помощью self::Substringer | |
81 | + * 3) конкатенирует между собой то,что получилось за принципом | |
82 | + * mainString+delimiter1+regex1Result+delimiter2+regex2Result | |
83 | + * ================================================================================================================| | |
84 | + * @example | |
85 | + * $haystack='https://www.linija-svitla.ua/catalog/ulichnoe-osveshchenie?page_num=3&fcatlist=3268%2C&sort=test_test&3269%2C3270%2C3272%2C3273%2C3274%2C3275&page=50&per-page=18'; | |
86 | + * $regex1='/(\??|&?)page=\d{1,5}&per-page=\d{1,5}/'; | |
87 | + * $regex2='/(\?|&)sort=[a-zA-z_]+/'; | |
88 | + * $requiredDelimiter='?'; | |
89 | + * $firstConcatenateSymbol='?'; | |
90 | + * $secondConcatenateSymbol='&'; | |
91 | + * $res=$this->changeStringByRegex($haystack,$regex1,$regex2,$firstConcatenateSymbol,$secondConcatenateSymbol,$requiredDelimiter); | |
92 | + * @example-return | |
93 | + * https://www.linija-svitla.ua/catalog/ulichnoe-osveshchenie?page=50&per-page=18&sort=test_test | |
94 | + * ================================================================================================================| | |
95 | + * @todo | |
96 | + * 1) Пока что метод работает только с 2 regex,надо будет поменять строго regex string => regex array | |
97 | + * 2) Метод полюбому обрезает первый символ результирующей строки regex | |
98 | + * 3) нужно сделать механизм замены строки только по 1 regex | |
99 | + * ================================================================================================================| | |
100 | + * | |
101 | + */ | |
102 | + /** | |
103 | + * @param string $haystack | |
104 | + * @param string $regex1 | |
105 | + * @param string $regex2 | |
106 | + * @param string $requiredDelimiter | |
107 | + * @param string $firstConcatenateSymbol | |
108 | + * @param string $secondConcatenateSymbol | |
109 | + * | |
110 | + * @return string | |
111 | + */ | |
112 | + public static function changeStringByRegex(string $haystack, string $regex1, string $regex2, string $requiredDelimiter = '', | |
113 | + string $firstConcatenateSymbol = '', | |
114 | + string $secondConcatenateSymbol = '' | |
115 | + ): string | |
116 | + { | |
117 | + | |
118 | + # 1 give rexe1/regex2 parts | |
119 | + # IF we have no consilience with both Regex == > return $haystack | |
120 | + if (preg_match($regex1, $haystack) !== 0 || preg_match($regex2, $haystack) !== 0) { | |
121 | + preg_match($regex1, $haystack, $matches[0]); | |
122 | + preg_match($regex2, $haystack, $matches[1]); | |
123 | + } else return $haystack; | |
124 | + | |
125 | + # 2 give must part of string | |
126 | + $mustPartOfstring = self::SimpleStringSubstring($haystack, $requiredDelimiter); | |
127 | + | |
128 | + # 3 if regex1/regex2 !empty concatenate they with $mustPartOfString | |
129 | + if (isset($matches[0][0]) && isset($matches[1][0])) { | |
130 | + # удаляем первый символ ( прим; $matches[0][0]='&sort=test_desc') | |
131 | + # нам надо только текст без первого спецсимвола | |
132 | + $matches[0][0] = substr($matches[0][0], 1); | |
133 | + $mustPartOfstring = (isset($matches[0][0])) ? $mustPartOfstring . $firstConcatenateSymbol . $matches[0][0] : $mustPartOfstring; | |
134 | + $matches[1][0] = substr($matches[1][0], 1); | |
135 | + $mustPartOfstring = (isset($matches[1][0])) ? $mustPartOfstring . $secondConcatenateSymbol . $matches[1][0] : $mustPartOfstring; | |
136 | + } # если найден только 1й regex | |
137 | + elseif (isset($matches[0][0]) && !isset($matches[1][0])) { | |
138 | + $matches[0][0] = substr($matches[0][0], 1); | |
139 | + $mustPartOfstring = (isset($matches[0][0])) ? $mustPartOfstring . $firstConcatenateSymbol . $matches[0][0] : $mustPartOfstring; | |
140 | + } # если найден 2й regex | |
141 | + elseif (!isset($matches[0][0]) && isset($matches[1][0])) { | |
142 | + $matches[1][0] = substr($matches[1][0], 1); | |
143 | + $mustPartOfstring = (isset($matches[1][0])) ? $mustPartOfstring . $firstConcatenateSymbol . $matches[1][0] : $mustPartOfstring; | |
144 | + } | |
145 | + | |
146 | + return $mustPartOfstring; | |
147 | + | |
148 | + | |
149 | + } | |
150 | + | |
151 | + | |
152 | +} | |
0 | 153 | \ No newline at end of file | ... | ... |
frontend/controllers/BlogController.php
... | ... | @@ -10,7 +10,8 @@ |
10 | 10 | use yii\helpers\ArrayHelper; |
11 | 11 | use yii\web\Controller; |
12 | 12 | use yii\web\NotFoundHttpException; |
13 | - | |
13 | + | |
14 | + | |
14 | 15 | /** |
15 | 16 | * Class BlogController |
16 | 17 | * |
... | ... | @@ -52,15 +53,17 @@ |
52 | 53 | ->distinct(), |
53 | 54 | 'pagination' => [ |
54 | 55 | 'pageSize' => 6, |
56 | + | |
55 | 57 | ], |
56 | 58 | ] |
57 | 59 | ); |
58 | - | |
59 | - return $this->render( | |
60 | + | |
61 | + return $this->render( | |
60 | 62 | 'index', |
61 | 63 | [ |
62 | 64 | 'categories' => $data, |
63 | 65 | 'dataProvider' => $dataProvider, |
66 | + | |
64 | 67 | ] |
65 | 68 | ); |
66 | 69 | } | ... | ... |
frontend/controllers/ServiceController.php
... | ... | @@ -84,44 +84,45 @@ |
84 | 84 | } |
85 | 85 | |
86 | 86 | } |
87 | - | |
88 | - $layoutMicrodata = ( count($model->prices) > 1 ) ? [ | |
89 | - '@context' => 'http://schema.org/', | |
90 | - '@type' => 'Product', | |
91 | - 'name' => "'{$model->language->attributes['title']}'", | |
92 | - 'offers' => [ | |
93 | - '@type' => 'AggregateOffer', | |
94 | - 'lowPrice' => "'" . min($prices) . "'", | |
95 | - 'highPrice' => "'" . max($prices) . "'", | |
96 | - 'priceCurrency' => 'UAH', | |
97 | - ], | |
98 | - ] : [ | |
99 | - 'type' => 'Product', | |
100 | - 'name' => "'{$model->language->attributes['title']}'", | |
101 | - 'offers' => [ | |
102 | - '@type' => 'Offer', | |
103 | - 'priceCurrency' => 'UAH', | |
104 | - 'Price' => "'" . max($prices) . "'", | |
105 | - ], | |
106 | - ]; | |
107 | - | |
108 | - $microdata = new MicrodataFabric(); | |
109 | - $pageMicrodata = $microdata::createJsonFromProduct($layoutMicrodata) | |
110 | - ->toJson(); | |
111 | - | |
112 | - $model->body = str_replace( | |
113 | - '[[prices]]', | |
114 | - $this->renderPartial('_prices', [ 'prices' => $model->prices ]), | |
115 | - $model->body | |
116 | - ); | |
117 | - return $this->render( | |
118 | - 'view', | |
87 | + | |
88 | + $layoutMicrodata=(count($model->prices)>1)? | |
119 | 89 | [ |
120 | - 'model' => $model, | |
121 | - 'others' => $others, | |
122 | - 'microdata' => $pageMicrodata, | |
90 | + 'context' => 'http://schema.org/', | |
91 | + 'type' => 'Product', | |
92 | + 'name'=> "'{$model->language->attributes['title']}'", | |
93 | + 'offers' => | |
94 | + [ | |
95 | + '@type'=> 'AggregateOffer', | |
96 | + 'lowPrice'=> "'".min($prices)."'", | |
97 | + 'highPrice'=> "'".max($prices)."'", | |
98 | + 'priceCurrency'=> 'UAH' | |
99 | + ] | |
123 | 100 | ] |
124 | - ); | |
101 | + :[ | |
102 | + 'type'=>'Product', | |
103 | + 'name'=> "'{$model->language->attributes['title']}'", | |
104 | + 'offers'=> [ | |
105 | + '@type'=> 'Offer', | |
106 | + 'priceCurrency'=> 'UAH', | |
107 | + | |
108 | + ] | |
109 | + ]; | |
110 | + if (count($model->prices) <= 1 && isset($prices)) { | |
111 | + if (!empty($prices)) $layoutMicrodata['offers']['Price'] = "'" . max($prices) . "'"; | |
112 | + } | |
113 | + | |
114 | + | |
115 | + $microdata=new MicrodataFabric(); | |
116 | + $pageMicrodata=$microdata::createJsonFromProduct($layoutMicrodata)->toJson(); | |
117 | + | |
118 | + | |
119 | + | |
120 | + $model->body = str_replace('[[prices]]', $this->renderPartial('_prices', ['prices' => $model->prices]), $model->body); | |
121 | + return $this->render('view', [ | |
122 | + 'model' => $model, | |
123 | + 'others'=> $others, | |
124 | + 'microdata'=>$pageMicrodata | |
125 | + ]); | |
125 | 126 | } |
126 | 127 | |
127 | 128 | public function findModel($id) | ... | ... |
frontend/microdata/ProductMicrodata.php
frontend/views/blog/_article.php
... | ... | @@ -27,7 +27,7 @@ $imageHeight=240; |
27 | 27 | |
28 | 28 | <div itemtype="http://schema.org/Article" itemscope class="blog-list-wr"> |
29 | 29 | |
30 | - <div itemprop="logo" itemscope itemtype="https://schema.org/ImageObject" class="img-blog-list"> | |
30 | + <div itemscope itemtype="https://schema.org/ImageObject" class="img-blog-list"> | |
31 | 31 | |
32 | 32 | <meta itemprop="url" content="<?=($model->image) ? $model->image->getPath() : null;?>"> |
33 | 33 | <meta itemprop="height" content="<?=$imageWidth;?>"/> |
... | ... | @@ -48,8 +48,10 @@ $imageHeight=240; |
48 | 48 | </div> |
49 | 49 | |
50 | 50 | <div class="blog-all-date-views"> |
51 | - <metacontent="<?=date('d.m.Y', $model->created_at);?>"/> | |
52 | - <div itemprop="datePublished" class="blog-date"><?=date('d.m.Y', $model->created_at)?></div> | |
51 | + | |
52 | + <metacontent | |
53 | + ="<?= date('d-m-Y', $model->created_at); ?>"/> | |
54 | + <div itemprop="datePublished" class="blog-date"><?= date('d-m-Y', $model->created_at) ?></div> | |
53 | 55 | <div class="blog-views-comments-ico"> |
54 | 56 | <div class="blog-comments-ico"><?=count($model->comments)?></div> |
55 | 57 | <div class="blog-views-ico"><?=$model->views?></div> |
... | ... | @@ -57,35 +59,32 @@ $imageHeight=240; |
57 | 59 | </div> |
58 | 60 | |
59 | 61 | |
60 | - | |
61 | - | |
62 | - | |
63 | - | |
64 | - | |
65 | - <meta itemprop="headline" content="XXX"/> где ХХХ название статьи в H1 | |
62 | + <meta itemprop="headline" content="XXX"/> <?= $model->title; ?> | |
66 | 63 | |
67 | 64 | <div itemprop="publisher" itemscope itemtype="https://schema.org/Organization"> |
68 | 65 | |
69 | 66 | |
70 | - | |
71 | - <meta itemprop="name" content="НАЗВАНИЕ САЙТА"> | |
67 | + <meta itemprop="name" content="<?= \Yii::t('app', 'ABC short'); ?>"> | |
68 | + <meta itemprop="logo" | |
69 | + content="<?= 'https://as01.epimg.net/epik/imagenes/2018/03/10/portada/1520705351_010030_1520705450_noticia_normal.jpg'; ?>"> | |
72 | 70 | |
73 | 71 | </div> |
74 | 72 | |
75 | 73 | <div itemprop="author" itemscope itemtype="https://schema.org/Person"> |
76 | 74 | |
77 | - <meta itemprop="name" content="НАЗВАНИЕ САЙТА"/> | |
75 | + <meta itemprop="name" content="<?= Url::home(true) ?>"/> | |
78 | 76 | |
79 | 77 | </div> |
80 | 78 | |
81 | - <p itemprop="description">ОПИСАНИЕ</p> | |
79 | + <p itemprop="description"><?= $model->body_preview; ?></p> | |
82 | 80 | |
83 | 81 | <div itemprop="image" itemscope itemtype="https://schema.org/ImageObject"> |
84 | 82 | |
85 | - <meta itemprop="url" content="КАРТИНКА"> | |
83 | + <meta itemprop="url" | |
84 | + content="<?= 'https://as01.epimg.net/epik/imagenes/2018/03/10/portada/1520705351_010030_1520705450_noticia_normal.jpg'; ?>"> | |
86 | 85 | |
87 | - <meta itemprop="height" content="675"/> | |
86 | + <meta itemprop="height" content="<?= $imageHeight; ?>"/> | |
88 | 87 | |
89 | - <meta itemprop="width" content="900"/> | |
88 | + <meta itemprop="width" content="<?= $imageWidth; ?>"/> | |
90 | 89 | </div> |
91 | 90 | </div> |
92 | 91 | \ No newline at end of file | ... | ... |
frontend/views/blog/index.php
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | use yii\web\View; |
7 | 7 | use yii\widgets\ActiveForm; |
8 | 8 | use yii\widgets\ListView; |
9 | - | |
9 | +use frontend\widgets\SeoLinks; | |
10 | 10 | /** |
11 | 11 | * @var View $this |
12 | 12 | * @var ActiveDataProvider $dataProvider |
... | ... | @@ -61,31 +61,43 @@ |
61 | 61 | </div> |
62 | 62 | </div> |
63 | 63 | <div class="row blog-list-row"> |
64 | - <?= ListView::widget( | |
64 | + <?= ListView::widget( | |
65 | 65 | [ |
66 | 66 | 'dataProvider' => $dataProvider, |
67 | 67 | 'itemView' => '_article', |
68 | 68 | 'itemOptions' => [ |
69 | 69 | 'class' => 'col-xs-12 col-sm-4 col-md-4 blog-list-col', |
70 | 70 | ], |
71 | - 'layout' => '{items}{pager}', | |
71 | + #'layout' => '{items}{pager}', | |
72 | + 'layout' => '{items}', | |
72 | 73 | ] |
73 | 74 | ); ?> |
75 | + <?php | |
76 | + #die(var_dump($dataProvider2)); | |
77 | + | |
78 | + ?> | |
79 | + | |
74 | 80 | |
75 | 81 | <div class="col-xs-12 col-sm-12"> |
76 | 82 | <div class="style navi-c-a"> |
77 | - | |
78 | - <?php echo \frontend\widgets\FrontendPager::widget( | |
79 | - [ | |
80 | - 'pagination' => $dataProvider->pagination, | |
81 | - 'prevPageLabel' => 'previous', | |
82 | - 'nextPageLabel' => 'next', | |
83 | - 'maxButtonCount' => 5, | |
84 | - 'lastPageLabel' => 'last_number', | |
85 | - ] | |
86 | - );?> | |
83 | + | |
84 | + <?php echo \frontend\widgets\FrontendPager::widget( | |
85 | + [ | |
86 | + 'pagination' => $dataProvider->pagination, | |
87 | + 'prevPageLabel' => 'previous', | |
88 | + 'nextPageLabel' => 'next', | |
89 | + 'maxButtonCount' => 5, | |
90 | + 'lastPageLabel' => 'last_number', | |
91 | + ] | |
92 | + ); ?> | |
87 | 93 | </div> |
88 | 94 | </div> |
89 | 95 | </div> |
90 | 96 | </div> |
91 | -</section> | |
92 | 97 | \ No newline at end of file |
98 | +</section> | |
99 | +<?php SeoLinks::widget( | |
100 | + [ | |
101 | + 'pagination' => $dataProvider->pagination, | |
102 | + 'seo' => $seo, | |
103 | + ] | |
104 | +) ?> | |
93 | 105 | \ No newline at end of file | ... | ... |
frontend/views/layouts/main.php
... | ... | @@ -141,7 +141,7 @@ $this->registerMetaTag( |
141 | 141 | <!DOCTYPE html > |
142 | 142 | <html xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html" lang="<?= \Yii::$app->language ?>"> |
143 | 143 | <head> |
144 | - | |
144 | + | |
145 | 145 | <script type="text/javascript"> |
146 | 146 | WebFontConfig = { |
147 | 147 | google: {families: [ 'Ubuntu:400,500,700' ]} |
... | ... | @@ -154,7 +154,7 @@ $this->registerMetaTag( |
154 | 154 | var s = document.getElementsByTagName('script')[ 0 ]; |
155 | 155 | s.parentNode.insertBefore(wf, s); |
156 | 156 | })(); </script> |
157 | - | |
157 | + | |
158 | 158 | <script> |
159 | 159 | |
160 | 160 | <!-- Global site tag (gtag.js) - Google Analytics --> |
... | ... | @@ -307,7 +307,7 @@ $this->registerMetaTag( |
307 | 307 | 'url' => Url::to(['package/index']), |
308 | 308 | ]; |
309 | 309 | |
310 | - | |
310 | + | |
311 | 311 | |
312 | 312 | |
313 | 313 | ?> |
... | ... | @@ -646,10 +646,11 @@ $this->registerMetaTag( |
646 | 646 | ], |
647 | 647 | |
648 | 648 | ]; |
649 | - if(isset($phones) && !empty($phones)){foreach ($phones as $key =>$phone) | |
650 | - { | |
651 | - $layoutMicrodata['contactPoint']['telephone'.$key]=$phone; | |
652 | - }} | |
649 | + | |
650 | + if(isset($phones[0])) { | |
651 | + $layoutMicrodata['contactPoint']['telephone'] = "+38" . $phones[0]; | |
652 | + } | |
653 | + | |
653 | 654 | |
654 | 655 | |
655 | 656 | $settings->email; | ... | ... |
frontend/views/site/index.php
... | ... | @@ -98,8 +98,9 @@ JS; |
98 | 98 | <!--263x146px--> |
99 | 99 | <?=ImageHelper::set($service->image->getPath()) |
100 | 100 | ->cropResize(263, 146) |
101 | - ->quality(82) | |
102 | - ->renderImage()?> | |
101 | + ->quality(84) | |
102 | + ->renderImage(['alt'=>ucfirst($service->title).' - Консультация врача и лечение заболеваний', | |
103 | + 'title'=>ucfirst($service->title).' - Консультация врача и лечение заболеваний'])?> | |
103 | 104 | </div> |
104 | 105 | <div class="style categories-home-links-wr"> |
105 | 106 | <div class="style categories-home-links-title"> | ... | ... |
frontend/widgets/FrontendPager.php
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | use function key_exists; |
6 | 6 | use yii\helpers\ArrayHelper; |
7 | 7 | use yii\helpers\Html; |
8 | - | |
8 | + use common\components\Substringer; | |
9 | 9 | /** |
10 | 10 | * Class FrontendPager |
11 | 11 | * |
... | ... | @@ -128,20 +128,33 @@ |
128 | 128 | /** |
129 | 129 | * @var \artbox\catalog\models\Filter $filter |
130 | 130 | */ |
131 | - $filter = \Yii::$app->get('filter')->filterObj; | |
132 | - if (key_exists($link, $filter->getReplacedFilters())) { | |
133 | - $link = $filter->getReplacedFilters()[ $link ]; | |
134 | - } | |
131 | +// $filter = \Yii::$app->get('filter')->filterObj; | |
132 | +// if (key_exists($link, $filter->getReplacedFilters())) { | |
133 | +// $link = $filter->getReplacedFilters()[ $link ]; | |
134 | +// } | |
135 | 135 | return Html::tag( |
136 | 136 | 'li', |
137 | 137 | Html::a($label, $link, $linkOptions), |
138 | 138 | $options |
139 | 139 | ); |
140 | 140 | } |
141 | - if ($active) { | |
141 | + | |
142 | + | |
143 | + if ($active) { | |
142 | 144 | return Html::tag('li', Html::a($label, null, $linkOptions), $options); |
143 | 145 | } else { |
144 | - return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), $linkOptions), $options); | |
146 | + # убираю весь мусор кроме прямой пагинации с ссылок | |
147 | + $haystack = $this->pagination->createUrl($page); | |
148 | + | |
149 | + $regex1 = '/(\??|&?)page=\d{1,5}&per-page=\d{1,5}/'; | |
150 | + $regex2 = '/(\?|&)sort=[a-zA-z_]+/'; | |
151 | + $requiredDelimiter = '?'; | |
152 | + $firstConcatenateSymbol = '?'; | |
153 | + $secondConcatenateSymbol = '?'; | |
154 | + $res = Substringer::changeStringByRegex($haystack, $regex1, $regex2, $firstConcatenateSymbol, $secondConcatenateSymbol, $requiredDelimiter); | |
155 | + | |
156 | + return Html::tag('li', Html::a($label, $res, $linkOptions), $options); | |
157 | + #return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), $linkOptions), $options); | |
145 | 158 | } |
146 | 159 | |
147 | 160 | } | ... | ... |
1 | +<?php | |
2 | +/** | |
3 | + * ====================================================================================================================| | |
4 | + * @author artbox Khonko Alex | |
5 | + * Виджет, полностью копирует \artbox\artbox-core\widgets\SeoWidget | |
6 | + * Примечание: | |
7 | + * Я не смог чисто унаследовать виджет, потому что в основу его ложится родительский вариант | |
8 | + * SeoWidget::run -> parent::run | |
9 | + * При наследовании у меня выдавалось по 2 одинаковых link prev/link next | |
10 | + * Поэтому ядро виджета я вернул в исходное состояние, поменял use во всех местах на свой, мой является | |
11 | + * как минимум на 50% дописанным/переписанным вариантом для линии | |
12 | + * ====================================================================================================================| | |
13 | + */ | |
14 | + | |
15 | +namespace frontend\widgets; | |
16 | + | |
17 | +use Yii; | |
18 | +use common\components\Substringer; | |
19 | +use yii\helpers\Url; | |
20 | +use yii\base\Widget; | |
21 | +use yii\data\Pagination; | |
22 | +use yii\helpers\VarDumper as d; | |
23 | + | |
24 | +class SeoLinks extends Widget | |
25 | +{ | |
26 | + | |
27 | + /** | |
28 | + * @var null | Pagination | |
29 | + * @var null | \common\components\SeoComponent | |
30 | + */ | |
31 | + public $pagination = null; | |
32 | + | |
33 | + public $canonical = true; | |
34 | + | |
35 | + public $firstpageWithoutParameter = false; | |
36 | + | |
37 | + public $seo = null; | |
38 | + /** | |
39 | + * @inheritdoc | |
40 | + */ | |
41 | + | |
42 | + /** | |
43 | + * ============================================================================================================| | |
44 | + * Правка от Саши | |
45 | + * 1) Если есть параметры в урле, Canonical должен вести на страницу без параметров, | |
46 | + * за исключением Пагинации и сортировки. И next/prev чтобы были без этих параметров | |
47 | + * Пример страницы | |
48 | + * https://www.linija-svitla.ua/catalog/ulichnoe-osveshchenie?page_num=3&fcatlist=3268%2C&sort=test_test&3269%2C3270%2C3272%2C3273%2C3274%2C3275&page=50&per-page=18 | |
49 | + * ************************************************************************************************************ | |
50 | + * 2) link prev со 2й страницы должен ссылатся на первую, НО , | |
51 | + * a) там должна отсутствовать пагинация б) должна остатся сортировка | |
52 | + * Прим | |
53 | + * Было https://www.linija-svitla.ua/catalog/ulichnoe-osveshchenie?page=1&per=page=18&sort=title_asc | |
54 | + * Стало https://www.linija-svitla.ua/catalog/ulichnoe-osveshchenie?sort=title_asc | |
55 | + * ============================================================================================================| | |
56 | + * 3) При 301 редирректе от НеЧПУ продуктам/фильтрам к ЧПУ, теги prev/next ссылаются на старые НеЧПУ | |
57 | + * страницы | |
58 | + * ============================================================================================================| | |
59 | + */ | |
60 | + public function run() | |
61 | + { | |
62 | + | |
63 | + # 0 изменяем ссылку, оставляя в ней только пагинацию и сортировку | |
64 | + $firstRegex = '/(\??|&?)page=\d{1,5}&per-page=\d{1,5}/'; | |
65 | + $secondRegex = '/(\?|&)sort=[a-zA-z_]+/'; | |
66 | + $requiredDelimiter = '?'; | |
67 | + $firstConcatenateSymbol = '?'; | |
68 | + $secondConcatenateSymbol = '&'; | |
69 | + | |
70 | + $links = $this->pagination->getLinks(); | |
71 | + | |
72 | + if ($this->canonical and ($this->seo->getRobots() != 'noindex,nofollow' and $this->seo->getRobots() != 'noindex,follow' and $this->seo->getRobots() != 'noindex, nofollow' and $this->seo->getRobots() != 'noindex, follow') | |
73 | + ) { | |
74 | + # 0 | |
75 | + $cannonicalLink = Substringer::changeStringByRegex( | |
76 | + Yii::$app->request->getAbsoluteUrl(), | |
77 | + $firstRegex, | |
78 | + $secondRegex, | |
79 | + $requiredDelimiter, | |
80 | + $firstConcatenateSymbol, | |
81 | + $secondConcatenateSymbol | |
82 | + ); | |
83 | + # 1 В канониклах должна присутствовать только пагинация, удаляю всё, что не есть пагинацией | |
84 | + if ((strpos($cannonicalLink, '?page'))) { | |
85 | + $cannonicalLink = Substringer::simpleStringSubstring($cannonicalLink, '&sort'); | |
86 | + } else { | |
87 | + $cannonicalLink = Substringer::changeStringByRegex( | |
88 | + Yii::$app->request->getAbsoluteUrl(), | |
89 | + $firstRegex, | |
90 | + $secondRegex, | |
91 | + $requiredDelimiter, | |
92 | + $firstConcatenateSymbol, | |
93 | + $secondConcatenateSymbol | |
94 | + ); | |
95 | + $cannonicalLink = Substringer::simpleStringSubstring($cannonicalLink, '&sort'); | |
96 | + /** | |
97 | + * Сейчас ссылка имеет вид: www.siteName.com?page=X&per-page=X | |
98 | + */ | |
99 | + | |
100 | + # 1.1 Если в результате у нас остается URL вида www.siteName.com? | |
101 | + # обрезаем этот ? | |
102 | + if (strpos($cannonicalLink, '?') && !strpos($cannonicalLink, '?page')) { | |
103 | + $cannonicalLink = stristr($cannonicalLink, '?', true); | |
104 | + } | |
105 | + | |
106 | + # 1.2 если ссылка содержит в себе пагинацию, НО она по каким-то причинам не первая, | |
107 | + # удаляем все GET параметры до неё, и заменяем & на ? | |
108 | + if (strpos($cannonicalLink, '?page') !== false) { | |
109 | + $cannonicalLinkRigth = stristr($cannonicalLink, '&page'); | |
110 | + $cannonicalLinkRigth = substr($cannonicalLinkRigth, 1); | |
111 | + $cannonicalLinkRigth = '?' . $cannonicalLinkRigth; | |
112 | + | |
113 | + $cannonicalLink = stristr($cannonicalLink, '?', true); | |
114 | + $cannonicalLink .= $cannonicalLinkRigth; | |
115 | + } | |
116 | + | |
117 | + } | |
118 | + # если нашем очищенном каноникле есть ? в конце(www.siteName.com?page=X&per-page=X?), убираем его | |
119 | + $cannonicalLink = ($cannonicalLink[strlen($cannonicalLink) - 1] !== '?') ? $cannonicalLink : rtrim( | |
120 | + $cannonicalLink, | |
121 | + '?' | |
122 | + ); | |
123 | + | |
124 | + # 1.3 в каноникле должна сохранятся очередность нужных GET параметров | |
125 | + # берём финальную ссылку и если нужно, перерисовываем её | |
126 | + $seoCuttedLink = $this->changeLinksToRequestView($cannonicalLink, Yii::$app->request->getAbsoluteUrl()); | |
127 | + $this->view->registerLinkTag( | |
128 | + [ | |
129 | + 'rel' => 'canonical', | |
130 | + 'href' => $cannonicalLink, | |
131 | + ] | |
132 | + ); | |
133 | + } | |
134 | + | |
135 | + /** | |
136 | + * ========================================================================================================= | |
137 | + * То же самое для тега next | |
138 | + * ========================================================================================================= | |
139 | + */ | |
140 | + | |
141 | + if (key_exists('next', $links)) { | |
142 | + #2.1 Обрезаем ссылку справа по заданным GET regexp-ам | |
143 | + $nextPageGetQueryString = stristr( | |
144 | + Substringer::changeStringByRegex( | |
145 | + $links['next'], | |
146 | + $firstRegex, | |
147 | + $secondRegex, | |
148 | + $requiredDelimiter, | |
149 | + $firstConcatenateSymbol, | |
150 | + $secondConcatenateSymbol | |
151 | + ), | |
152 | + '?' | |
153 | + ); | |
154 | + # 2.2 сливаем левую и правую часть | |
155 | + $nextLinkTitle = Substringer::simpleStringSubstring( | |
156 | + Yii::$app->request->url, | |
157 | + '?' | |
158 | + ) . $nextPageGetQueryString; | |
159 | + | |
160 | + # 2.3 перестраиваем сслыку согласно входящему стилю | |
161 | + $nextLinkTitle = $this->changeLinksToRequestView($nextLinkTitle, Yii::$app->request->url); | |
162 | + | |
163 | + $this->view->registerLinkTag( | |
164 | + [ | |
165 | + 'rel' => 'next', | |
166 | + 'href' => $nextLinkTitle, | |
167 | + ] | |
168 | + ); | |
169 | + } | |
170 | + | |
171 | + if ($this->firstpageWithoutParameter and $this->pagination->page == 1) { | |
172 | + if (key_exists('prev', $links)) { | |
173 | + $link = stristr(\Yii::$app->request->url, '?', true); | |
174 | + /** | |
175 | + * @var \artbox\catalog\models\Filter $filter | |
176 | + */ | |
177 | + $filter = \Yii::$app->get('filter')->filterObj; | |
178 | + if (key_exists($link, $filter->getReplacedFilters())) { | |
179 | + $link = $filter->getReplacedFilters()[$link]; | |
180 | + } | |
181 | + | |
182 | + $seoCuttedLink = $this->changeLinksToRequestView($link, Yii::$app->request->url); | |
183 | + $this->view->registerLinkTag( | |
184 | + [ | |
185 | + 'rel' => 'prev', | |
186 | + 'href' => $seoCuttedLink, | |
187 | + | |
188 | + ] | |
189 | + ); | |
190 | + } | |
191 | + } else { | |
192 | + if (key_exists('prev', $links)) { | |
193 | + | |
194 | + # правка 3 Я не ломаю старых наработок, а просто прокидываю новый(если был редирект) URL prev для | |
195 | + // предыдущих правок | |
196 | + $prevPageGetQueryString = stristr( | |
197 | + Substringer::changeStringByRegex( | |
198 | + $links['prev'], | |
199 | + $firstRegex, | |
200 | + $secondRegex, | |
201 | + $requiredDelimiter, | |
202 | + $firstConcatenateSymbol, | |
203 | + $secondConcatenateSymbol | |
204 | + ), | |
205 | + '?' | |
206 | + ); | |
207 | + $prevLinkTitle = Substringer::simpleStringSubstring( | |
208 | + Yii::$app->request->url, | |
209 | + '?' | |
210 | + ) . $prevPageGetQueryString; | |
211 | + # правка 2 | |
212 | + $seoStringStart = $prevLinkTitle; | |
213 | + $seoCuttedLink = $seoStringStart; | |
214 | + $seoCuttedLink = Substringer::changeStringByRegex( | |
215 | + $seoCuttedLink, | |
216 | + $firstRegex, | |
217 | + $secondRegex, | |
218 | + $requiredDelimiter, | |
219 | + $firstConcatenateSymbol, | |
220 | + $secondConcatenateSymbol | |
221 | + ); | |
222 | + | |
223 | + if (strpos($seoStringStart, '?page=1')) { | |
224 | + | |
225 | + /** | |
226 | + * Правка для категории/блога | |
227 | + * У них разное к-во страниц, поэтому на замену конкретной срезки URL(?page=1&per-page=18) как было раньше, | |
228 | + * я буду автоматически пересматривать currentPageSizeParam для странички | |
229 | + */ | |
230 | + $perPageParam = false; | |
231 | + | |
232 | + # если в $seoCuttredLink уже есть пагинация, то паршу строку и достаю реальное к-во записей для страницы | |
233 | + if (strpos($seoCuttedLink, 'per-page')) { | |
234 | + $perPageParam = parse_str($seoCuttedLink, $params); | |
235 | + $perPageParam = (isset ($params['per-page'])) ? $params['per-page'] : false; | |
236 | + } | |
237 | + # или же ставлю размер как в frontend/views/category, | |
238 | + # потому что большая часть кода здесь заточена именно под currentPerPageSizeParam=18 | |
239 | + $perPageParam = ($perPageParam === false) ? 18 : $perPageParam; | |
240 | + $seoCuttedLink = str_replace('?page=1&per-page=' . $perPageParam, '?', $seoCuttedLink); | |
241 | + $seoCuttedLink = str_replace('&', '', $seoCuttedLink); | |
242 | + $strlen = mb_strlen($seoCuttedLink); | |
243 | + if ($seoCuttedLink[$strlen - 1] == '?') { | |
244 | + $seoCuttedLink = str_replace('?', '', $seoCuttedLink); | |
245 | + } | |
246 | + | |
247 | + } else { | |
248 | + $seoCuttedLink = $seoStringStart; | |
249 | + } | |
250 | + | |
251 | + # правка 2 | |
252 | + | |
253 | + if (strpos($seoCuttedLink, '?sort')) { | |
254 | + if (strpos($seoCuttedLink, '&page=1')) { | |
255 | + $seoCuttedLink = Substringer::simpleStringSubstring($seoCuttedLink, '&page=1&per-page'); | |
256 | + } | |
257 | + | |
258 | + } | |
259 | + | |
260 | + $seoCuttedLink = Substringer::changeStringByRegex( | |
261 | + $seoCuttedLink, | |
262 | + $firstRegex, | |
263 | + $secondRegex, | |
264 | + $requiredDelimiter, | |
265 | + $firstConcatenateSymbol, | |
266 | + $secondConcatenateSymbol | |
267 | + ); | |
268 | + # поправляем URL выдачи, если он после всего имеет такой вид: <link href="/catName/subCatName?page=Xper-page=Xsort=param1_val1" rel="prev"> | |
269 | + if (preg_match('/\d+sort=/', $seoCuttedLink) != false) { | |
270 | + $seoCuttedLinkRigth = stristr($seoCuttedLink, 'sort='); | |
271 | + $seoCuttedLink = stristr($seoCuttedLink, 'sort=', true) . '&' . $seoCuttedLinkRigth; | |
272 | + } | |
273 | + # то же самое, если в пагинации URL пропущен & ==> www.site.com?page=Xper-page=X | |
274 | + if (preg_match('/\d+per-page=/', $seoCuttedLink) != false) { | |
275 | + $seoCuttedLinkRigth = stristr($seoCuttedLink, 'per-page='); | |
276 | + $seoCuttedLink = stristr($seoCuttedLink, 'per-page=', true) . '&' . $seoCuttedLinkRigth; | |
277 | + } | |
278 | + | |
279 | + $seoCuttedLink = $this->changeLinksToRequestView($seoCuttedLink, Yii::$app->request->url); | |
280 | + | |
281 | + $this->view->registerLinkTag( | |
282 | + [ | |
283 | + 'rel' => 'prev', | |
284 | + 'href' => $seoCuttedLink, | |
285 | + ] | |
286 | + ); | |
287 | + } | |
288 | + } | |
289 | + | |
290 | + parent::run(); | |
291 | + } | |
292 | + | |
293 | + /** | |
294 | + * @param string $link | |
295 | + * @param string $requestUrl | |
296 | + * ========================================================================================================= | |
297 | + * Метод, который берет строку(URL), и преобразовывает его в вид, в котором он поступал. Написан для тегов | |
298 | + * next/prev/cannonical | |
299 | + * ========================================================================================================= | |
300 | + * К нам приходит сюда(в данный виджет) URL, из которого нужно сформировать данные 3 линка. По всем Саниным | |
301 | + * правкам, они должны быть в одном из форматов: | |
302 | + * + новенькийЧпу?page=X&per-page=X&sort=param_val | |
303 | + * + новенькийЧпу?sort=param_val&page=X&per-page=X | |
304 | + * Метод берет текущий URL, смотрит какая из 2 вариаций пришла, и не меняя уже написанной логики | |
305 | + * возвращает обработанную конечтную строку,идентичную входящей | |
306 | + *Если в текущем URL отсутствует погинация, НО присутствует сортировка, | |
307 | + * делает ссылку типа www.siteName.com?sort=var_param&page=X&per-page=X | |
308 | + * ========================================================================================================= | |
309 | + * | |
310 | + * @return string | |
311 | + */ | |
312 | + private function changeLinksToRequestView(string $link, string $requestUrl) | |
313 | + { | |
314 | + $positionSort = (strpos($requestUrl, 'sort=') !== false) ? strpos($requestUrl, 'sort=') : 0; | |
315 | + $positionPagination = (strpos($requestUrl, 'page=') !== false) ? strpos($requestUrl, 'page=') : 0; | |
316 | + | |
317 | + $regex1 = '/(\??|&?)page=\d{1,5}&per-page=\d{1,5}/'; | |
318 | + $regex2 = '/(\??|&)sort=[a-zA-z_]+&?/'; | |
319 | + | |
320 | + /* | |
321 | + * У нас 5 вариантов: | |
322 | + * sort>page ===> пагинация идёт первой | |
323 | + * sort<page ===> перввая сортировка | |
324 | + * !sort | |
325 | + * !page | |
326 | + * !sort !page | |
327 | + */ | |
328 | + $option = $positionSort - $positionPagination; | |
329 | + if ($option > 0 && $positionPagination != 0)// первая пагинация | |
330 | + { | |
331 | + $link = Substringer::changeStringByRegex($link, $regex1, $regex2, '?', '?', '&'); | |
332 | + } elseif ($option > 0 && $positionPagination == 0)//есть сортировка, но нету пагинации | |
333 | + { | |
334 | + $link = Substringer::changeStringByRegex($link, $regex2, $regex1, '?', '?', '&'); | |
335 | + } elseif ($option < 0)// page>sort | |
336 | + { | |
337 | + $link = Substringer::changeStringByRegex($link, $regex2, $regex1, '?', '?', '&'); | |
338 | + } else // нету ни сортировки, ни пагинации | |
339 | + { | |
340 | + $link = Substringer::changeStringByRegex($link, $regex2, $regex1, '?', '?', '&'); | |
341 | + } | |
342 | + $result = $link; | |
343 | + return $result; | |
344 | + } | |
345 | +} | |
346 | + | ... | ... |