Разбор кода приложения написанного на php c применением Yii Framework (исходник).
Из статьи можно вынести интересные куски кода на php, а так же тонкости и конструкции Yii Framework, расчитана она на поддержку php программистов и пользователей Yii Framework.
Поведения Behavior
Пример поведения для работы с изображениями (как мы знаем поведения прикрепляются к контроллеру и становятся частью его функционала):
class ImageBehavior extends CBehavior{
public $modelName = '';
const MAX_WIDTH = 450;
const MAX_HEIGHT = 450;
public function resize($path){
$mimeType = mime_content_type($path);
$type = str_replace('image/','',$mimeType);
list($width, $height) = getimagesize($path);
list($newWidth, $newHeight) = $this->getNewSize($width,$height);
$thumb = imagecreatetruecolor($newWidth, $newHeight);
$createFunction = 'imagecreatefrom' . $type;
$source = $createFunction($path);
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
$imageFunction = 'image' . $type;
ob_start();
header('Content-Type: ' . $mimeType);
$imageFunction($thumb,null,100);
$img = ob_get_contents();
@unlink($path);
@file_put_contents($path,$img);
}
public function getNewSize($width,$height){
$wK = $width/$height;
$hK = $height/$width;
$newWidth = $width;
$newHeight = $height;
if($width > self::MAX_WIDTH && ($width > $height)){
$newWidth = self::MAX_WIDTH;
$newHeight = $newWidth/$wK;
}
if($height > self::MAX_HEIGHT && ($height > $width)){
$newHeight = self::MAX_HEIGHT;
$newWidth = $newHeight/$hK;
}
return array($newWidth,$newHeight);
}
public function columnExists($column){
$modelName = $this->modelName;
$model = $modelName::model();
$table = Yii::app()->getDb()->getSchema()->getTable($model->tableName());
$columnNames = $table->getColumnNames();
return in_array($column,$columnNames);
}
public function deleteImage(){
if($this->columnExists('image_path')){
$path = $this->image_path;
$fullPath = Yii::getPathOfAlias('webroot') . $path;
if(file_exists($fullPath))
@unlink($fullPath);
}
}
public function getModelName(){
return get_called_class();
}
Давайте разберем поподробнее, в поведение вынесена часть функционала модели Yii + добавлены общие методы для работы с изображениями. Предположим у нас есть 5 контроллеров в которых используются изображения, для того что бы не дублировать код, мы просто выносим функционал в поведение. Так же в поведении можно переопределить часть стандартных функций класса.
Action действия
Для удобного повторго использования кода в Yii существуют Actions которые прикрепляются к контроллерам.
Во время генерации кода при помощи модуля gii, cоздаются множетсво почти одинаковых контроллеров функционал которых можно вынести в отдельный класс Action.
Создается CRUDAction который наследуется от CAction, определены 2 атрибута: название модели и страница редиректа на которую пользователь попадает после завершения действий CRUD(Create Read Update Delete), так же происходит загрузка модели:
<?php
class CRUDAction extends CAction{
public $modelName;
public $redirectTo = ['index'];
public function loadModel($id){
$m = $this->modelName;
$model = $m::model()->findByPk($id);
if($model===null)
throw new CHttpException(404,'The requested page does not exist.');
return $model;
}
}
CreateAction — наследуется от CRUDAction, динамически создается модель и задается название сценария, в методе run описывается стандартные действия:
<?php
Yii::import('application.components.actions.CRUDAction');
class CreateAction extends CRUDAction{
public function run(){
$model = new $this->modelName;
$model->scenario = 'create';
if(isset($_POST[$this->modelName])){
$model->attributes = $_POST[$this->modelName];
if($model->save())
$this->getController()->redirect($this->redirectTo);
}
$renderPath = strtolower("create");
$this->getController()->render($renderPath,[
'model' => $model,
]);
}
}
Виджет
Большое количество одинакового функционала — можно упаковать в виджет и вызывать там где понадобится.
Класс виджета:
class Citywidget extends CWidget{
public $cols = 10;
public $name;
public $dialogId;
public $city_id;
public $defaultName = 'Город';
public function init(){
parent::init();
}
public function run(){
$this->render('citywidget',[
'name' => $this->name,
'dialogId' => $this->dialogId,
'city_id' => $this->city_id,
'cols' => $this->cols,
'defaultName' => $this->defaultName,
]);
}
}
Представление:
<?php
$this->beginWidget('zii.widgets.jui.CJuiDialog',array(
'id' => $dialogId,
// additional javascript options for the dialog plugin
'options'=> [
'title' => 'Города',
'autoOpen'=>false,
'width' => '800px'
],
'htmlOptions' => [
'width' => '800px'
]
));
?>
<?php
$cities = Cities::getCities();
$count = count($cities);
$perCol = round($count/$cols);
if($city_id == 0){
$city_id = CityChooser::$cityId;
}
$cityName = Cities::getCityNameByPk($city_id,$defaultName);
?>
<style type="text/css">
#<?=$dialogId?>{
display:none;
}
#<?=$dialogId?> ul {
float:left;
clear:right;
padding:5px;
}
#<?=$dialogId?> li {
float:left;
clear:left;
}
</style>
<script type="text/javascript">
function changeCity<?=$dialogId?>(id,cityName,dialogId,inName){
$("#href-"+dialogId+"").text(cityName);
$("input[name*='"+inName+"']").val(id);
$("#"+dialogId).dialog("close");
if(dialogId == 'chooserDialogId'){
$.get( "/api/setcity/id/"+id, function( data ) {
});
}
//$("#"+dialogId).dialog("close");
}
</script>
<?='<ul>'?>
<?php $i=0; ?>
<?php foreach ($cities as $city):?>
<?php if($i>=$perCol):?>
<?='</ul><ul>'?>
<?php $i=0; ?>
<?php endif;?>
<li style="margin-top:4px;">
<a href="#" style="margin:8px;color: #428bca;" onClick="changeCity<?=$dialogId?>('<?=$city->id?>','<?=$city->name?>','<?=$dialogId?>','<?=$name?>')">
<?php if((int)$city->sort !== 0):?>
<b><?=$city->name?></b>
<?php else:?>
<?=$city->name?>
<?php endif;?>
</a>
</li>
<?php if($i>=$perCol):?>
<?='</ul>'?>
<?php endif;?>
<?php $i++; ?>
<?php endforeach;?>
<?='</ul>'?>
<?php $this->endWidget('zii.widgets.jui.CJuiDialog');?>
<input type="hidden" name="<?=$name?>" value="<?=$city_id?>">
<a href="#" id="href-<?=$dialogId?>" onclick='$("#<?=$dialogId?>").dialog("open"); return false;'><?= $cityName?></a>
Как видите с такой смесью php и html работать неудобно, а вот так выглядит вызов виджета:
<?php
$this->widget('Citywidget',[
'name' => 'Biztrade[city_id]',
'city_id' => $model->city_id,
'dialogId' => 'dialogId'
]);
?>
Из интересного наверное все, я приложил исходник — в нем вы сможете найти более стандартные вещи.Спасибо.
Комментарии (64)
SilverFire
29.03.2016 09:21+7Yii первой версии (о котором вы пишете) уже очень отстал от современных трендов PHP, потому прием улучшений прекращен, а датой End of life объявлен конец 2018 года. Если вы пишете новый проект — обязательно посмотрите на Yii2, там вы найдете гораздо более гамотные и современные архитектурные решения.
Padaboo
29.03.2016 09:30-2Eсть стандартные патерны проектирования, мне не всегда нравится реализация в фреймворках (можно запросто и свой написать) будь то php или другой язык — суть все равно остается прежней. Мне будучи новичком было очень тяжело найти толковые куски кода еще 5-7 лет назад.
oxidmod
29.03.2016 09:24В чем смысл выкладывания для ознакомления кода устаревшего фреймворка?
в чем смысл новичкам учить устаревший фреймворк и потом выбрасывать эти знания за ненадобностью?
в чем смысл начинать новый проект на yii?balamyt92
29.03.2016 09:57+4в чем смысл начинать новый проект на yii?
а почему нет? (если берется Yii 2)
pewpew
29.03.2016 10:03+3На GitHub есть более-менее грамотно написанные проекты на YII 1.1, например РосЯма:
https://github.com/RosYama/RosYama.2scarab
29.03.2016 11:11+2А не подскажете то же самое на Yii2?
Я пока пишу свои поделки на первой версии, но уже становится понятно, что если не хочу превратиться в мамонта — то пора осваивать вторую, и чем быстрее — тем лучше.Padaboo
29.03.2016 13:18+1Думаю лучше обратиться к документации, кстати отличная тема для статьи и хаба. Осваивать новую версию и описывать миграции.
scarab
29.03.2016 13:25Документация — само собой. Но обычно кроме документации, полезно видеть уже готовые, более-менее грамотно написанные проекты, чтобы понимать… даже не coding style, а просто процесс мышления, менталитет фреймворка. На примерах из документации это не всегда видно, они достаточно упрощённые и оторванные от реальной жизни.
В общем, если кто знает хорошие проекты на Yii2, на которые можно посмотреть в плане изучения кода — буду признателен.Padaboo
29.03.2016 14:31Есть проект, его делал один человек — его уволили. Пришли 3 новых программиста им код непонравился — начали переписывать — все писали на разных языках в разном стиле и парадигмах (сторонники разных подходов). И таких проектов много. Думаю от примеров из документации отталкиваться правильнее — он следует правилам кодирования язка и стилю фреймворка.
Я сейчас переезжаю с PHP на Java достаточно большая разница — постараюсь незахватить старых привычек.
oxidmod
29.03.2016 14:48-1посмотрите в сторону Laravel, только не увлекайтесь фасадами слишком сильно. Часто новички пихают в них слишком многое
или же на symfony
vladqa
29.03.2016 12:25Код там на 3 с минусом. Не стал бы рекомендовать новичкам смотреть его и, тем более, брать пример.
pewpew
29.03.2016 13:06Но в таком случае, какой можно порекомендовать?
Зачастую людям хотелось бы поглядеть на рабочий проект вместо очередного блога или hello world.
К сожалению не могу показать свой код, он под NDA. Но как и автор, я тоже отошёл от некоторых рекомендаций (например, меня коробит от CHtml).
YII — это только фреймворк, а демо-проекты — лишь рекомендация.
Мне, например многие подходы в YII 2 не понравились, и поэтому я пока остался на YII 1.1Padaboo
29.03.2016 13:14Совершенно согласен фреймворки, паттерны и подходы меняются время от времени — некоторые возможности зачастую не используются — по причине неудобства. Суть в рабочем коде, его можно менять как захочется.
Padaboo
30.03.2016 02:00Лицензия? Если вы не будите делиться наработками и техническими подробностями сектор остановится.
Я к примеру собрался писать игру на java: jmonkeyengine. Там будет достаточно проблем и на клиенте и на сервере. Наработки буду выкладывать в хаб так как люди которые затят научиться программированию или сделать тоже самое просто несмогут этого сделать. Неотчего будет отталкнуться.
xoma
29.03.2016 12:33а что там грамотного?
pewpew
29.03.2016 14:10Местами код сумбурный, но позвольте, вы сразу умным родились и всё умели?
Лично я, когда начинал разбираться с миграциями, глядел в их код и кое-что почерпнул.
Вьюхи мне откровенно не нравятся. Но глянуть, как другие люди пишут всегда полезно.xoma
29.03.2016 14:16+1Ну к чему сразу переход на личности? Вы посоветовали новичкам источник, который Вам самим не очень нравится, просто рассказали бы в добавок к ссылке, что там хорошо, а что нет. Иначе будут воспринимать как эталон.
pewpew
29.03.2016 14:50Простите, если задел. Но из того что мне в моём примере не по душе — только лапша в коде и куски с комментариями. Справедливости ради отмечу, что не везде. В остальном, вполне себе боевой проект.
А вот что неграмотного там заметили Вы?xoma
29.03.2016 15:00+1Ну, например:
— тонны логики в контроллерах https://github.com/RosYama/RosYama.2/blob/master/protected/controllers/HolesController.php#L140
— прямое обращение к $_GET и т.д. https://github.com/RosYama/RosYama.2/blob/master/protected/controllers/HolesController.php#L205
— регистрация статики в контроллере — https://github.com/RosYama/RosYama.2/blob/master/protected/controllers/HolesController.php#L237
— ужасное форматирование кода
— curl-лапша https://github.com/RosYama/RosYama.2/blob/master/protected/controllers/HolesController.php#L720
Это только то, что бросилось в глаза в первом открытом контроллере.Padaboo
29.03.2016 22:08-1Спорный вопрос: оборачивать каждую конструкцию языка в класс? — думаю это нехорошо, некоторые узкие места в PHP переписываются на С для улучшения производительности и экономии памяти. Тоесть с точностью до наоборот.
lnroma
30.03.2016 02:37+1https://github.com/RosYama/RosYama.2/blob/master/protected/controllers/HolesController.php#L205 присмотритесь тут у вас sql inj простейшая.
Padaboo
29.03.2016 14:33С вьюхами там несколько вариантов: 1)php сам по себе шаблонизатор. 2)стандартные вещи типа Chtml 3)Bootstrap 4) Любой вариант. Фреймворк очень гибкий.
awMinor
29.03.2016 10:13+1Достаточно интересная статья, но вот момент с экшенами несколько сбивает с толку, тоесть если я правильно понял, вы предлогаете использовать каждый экшн = класс, а не как изначально в Yii когда экшн = метод контроллера. Таким образом вы призываете уйти от абстракции контроллера к абстракции его экшенов, но зачем? В целом я правильно понимаю, что у нас будет 4 класса с экшенами для CRUD контроллера?
Mendel
29.03.2016 11:26в фреймворке есть возможность указывать в контроллере экшн в виде названия класса а не в виде метода класса.
Это предназначено как раз для такого решения — когда действие одинаковое во многих контроллерах, и отличаются только детали.
Собственно ориентируясь именно на встроенное решение автор и писал данный код. Он типовой, но его обычно игнорируют.
Padaboo
29.03.2016 10:15+1Суть в том что Экшен может быть один, а контроллеров с одинаковыми действиями 5 — повторное использование кода в данном случае. А вообще — пример использования поведений, экшенов и виджетов.
awMinor
29.03.2016 10:32Просто я не очень понимаю, зачем отрывать экшен от контроллера, тогда ведь нарушается абстракция вся этого контроллера? А дублирование кода, да это плохо. Но в данном случае у меня все равно экшены относятся к контроллерам и не пишется множество не нужных классов, так как вся бизнес логика хранится в модели, ну и экшены у меня все таки просто принимают значения и вызывают метод модели, не более. И тогда никакого дублирования кода, нарушения абстракций и горождения кучи классов ради одного метода. Ведь YII следует парадигме MVC, а значит вся бизнес логика должна быть с моделях.
Padaboo
29.03.2016 10:44+1Это в теории, вспомните нормальные формы баз данных, есть много примеров когда для одного поля лучше не создавать отдельную таблицу:
class Trucks extends Advert{ public $bodyTypes = [ 1 => "Закрытый-Тентованный", 2 => "Закрытый-Фургон", 3 => "Закрытый-Контейнер", 4 => "Закрытый-Цельнометаллич.", 5 => "Закрытый-Рефрижератор", 6 => "Закрытый-Изотермический", 7 => "Открытый-Бортовой", 8 => "Открытый-Контейнеровоз", 9 => "Открытый-Низкорамный", 10 => "Открытый-Самосвал", 11 => "Открытый-Шаланда", 12 => "Открытый-Платформа", 13 => "Открытый-Пирамида", 14 => "Автовоз", 15 => "Автотранспортер", 16 => "Эвакуатор", 17 => "Трал", 18 => "Автобус", 19 => "Микроавтобус", 20 => "Пикап", 21 => "Легковая-седан", 22 => "Легковая-хетчбек", 23 => "Легковая-универсал", 24 => "Ж/Д вагон", 25 => "Цистерна" ];
И тут таже история всмысле парадигмы, у нас есть кусок кода который повторяется во многих контроллерах, мы его выносим в экшен:
public function actionIndex(){ $model = new Model(); $this->render('index',['model' => $model]); }
Mendel
29.03.2016 11:32+1Тут скорее вопрос о том почему наследоваться от CRUDAction а не от CRUDController?
awMinor
29.03.2016 11:36Да, именно это я и пытался сказать. Зачем плодить класс для каждого экшена если можно наследовать контроллер и не разрушать абстракцию контроллер->экшн.
Padaboo
29.03.2016 12:41Именно в этом коде экшены созданы для тех контроллеров который были сгенерированы модулем gii для работы с моделями. 5 моделей и 5 контроллеров с одинаковым содержимым.
awMinor
29.03.2016 13:11Зачем 5 контроллеров с одинковым содержимым, если мы говорим о CRUD, то у нас одна сущность имеет 5 экшенов, тоесть если у нас 5 контроллеров (5 сущностей), то у нас 25 экшенов получается. И почему 5 моделей? Скорее 6, базовая модель плюс её наследеники если нужны. Таким образом у нас нормальный уровень абстракции, каждый экшн принадлежит к сущности контроллера и вызывает бизнес логику на основе входящих данных из модели. Все равно ведь Yii реализует парадигму MVC, тоесть даже в вашем случае вся бизнес логика должна быть в модели и у вас просто получается класс-экшн который просто вызывает бизнес логику из модели. Ваш подход применим только к RESTFull API и только если вы не реализуете бизнес логику в моделях, а реализуете её в экшенах.
awMinor
29.03.2016 11:34Но модель чаще всего будет разная в зависимости от сущности, а вот рендер, не думаю, что ради одной строки стоит создавать класс обертку для экшена. А по поводу вашего примера с грузовиками, так мне кажется, что применение нормальных форм баз данных к ООП не очень приминимо.
Padaboo
29.03.2016 12:49Нормальные формы — это тоже своеобразные правила которые можно и нужно нарушать в пользу скорости кода, просто аналогия. Модель может быть и будет разная, я про типовые вещи findByPk().
Смотрите как теперь выглядит контроллер — насколько меньше кода:
Если я захочу поменять что то в 10 контроллерах, мне нужно будет поменять это только в одном экшене который к ним прикрепляется, так выглядит контроллер:
Yii::import('application.controllers.CrudController');
class MaterialsController extends CrudController{}awMinor
29.03.2016 13:01+1Но так же вы можете поменять это в одной модели, ведь модель в парадигме MVC и модель в Yii вообще вещи разные, так же никто не запрещает абстракцию модели, тоесть таким образом вы наследуете базовую модель которая реализует базовую бизнес логику для всех моделей, которые в свою очередь реализуют только уникальные методы бизнес логики в зависимости от сущности.
Padaboo
29.03.2016 13:37Да, все стандартные методы реализованы в базовой модели, а уникальные в наследнике — но в итоге вызов методов происходит в контроллере. Мы же не переносим всю логику в модель — а только делаем вызов или нет? Это может по разному выглядеть в контроллерах так:
$model = Cities::model()->findAll([ 'condition' => 'name like :term', 'params' => [ ':term' => "%$term%" ] ]); $arr = []; foreach($model as $city){ $arr[] = ['id' => $city->id, 'value' => $city->name]; }
или так
$model = Cities::model()->findByName($name); $arr = []; foreach($model as $city){ $arr[] = ['id' => $city->id, 'value' => $city->name]; }
Или так:
$arr = Cities::model()->findByNameArray($name);
Но речь идет о повторном использовании кода контроллера.awMinor
29.03.2016 13:46Все правильно, и вся бизнес логика у вас находится в модели, так зачем тогда вам 25 классов экшенов если достаточно 5 классов контроллеров, ведь обычно контроллер = сущность и согласно вашему примеру в каждом контроллере будет использоваться разная модель? Или одна? Хорошо, даже предположим, что экшены полностью идентичны для разных 5-ти контроллеров. Но учитывая что вся бизнес логика в модели, вы просто создадите класс-экшн для одной, двух строк. Да вы избавитесь от повторного использования кода, но, возникает три вопроса:
- Почему нельзя реализовать все таки контроллер с этими экшенами и наследоватся от него?
- Что вы будете делать когда один экшн одного контроллера потребует изменения логики? Создавать новый экшн-класс?
- Стоит ли ради "избежания повторного использования кода" разрушать абстракцию данных сущность-экшн? Ведь этого можно избежать используя наследование контроллеров.
Padaboo
29.03.2016 14:21С первым согласен это в общем то одно и тоже создать экшены или наследоваться от общего. На мой взгляд отдельные экшены более гибкая штука. Похоже на хэндлер больше.
awMinor
29.03.2016 14:53Отдельные экшены имеет смысл только когда нужно использовать некий компонент который должен предоставлять экшн, для примера загрузка изображения в таком случае в контроллер просто подгружается компонент и экшн. Но вы же в статье показали пример на CRUD, а в CRUD мне сложно представить ситуацию, когда у двух сущностей будут идентичные экшены. А если создавать отдельный экшн для каждой сущности, то выйдет каша и классов.
lnroma
29.03.2016 10:32+2ob_start();
header('Content-Type: '. $mimeType);
$imageFunction($thumb,null,100);
$img = ob_get_contents();
@unlink($path);
@file_put_contents($path,$img);
а не проще сразу в фаил писать `$imageFunction($thumb,$path,100);` и ещё один момент.
@unlink($path);
@file_put_contents($path,$img);
тот кто скопирует это, долго будет искать почему картинка не сохранилась, ведь ошибок нет.
Padaboo
29.03.2016 11:15Чистота кода она конечно важна, но так можно: рефакторить, улучшать, "подкрашивать", переписывать код до бесконечности — потратить кучу времени которое стоит дороже чем красота кода.
lnroma
29.03.2016 16:28Так то да. Но если не тратить время на рефакторинг(можно сказать работу над ошибками). Движет всё таки изучать ЯП, и применять оптимальные решения. Да кто то зарабатывает деньги тратой времени на написание «нечистого кода» большого объёма, а кто то пишет «более чистый код», и зарабатывает на том что, элементарные задачи на подобие «вывести тайтл с префиксом» решает за 5 минут, а кто то разбираеться в своём же коде пишит велосипеды и тратит 30 минут, и того 25 минут разницы. По вашему кому заплатят больше? первому или второму. Конечно первому т.к. это вложение в будущее проэкта, это меньший порог вхождения и стабильность работы, а второй стоит дешевле так как, в будущем это высокий порог вхождения, низкая стабильность, разработка нового решения… А по статье правда отвлекает от чтения. И не обежайтесь это моё сугубо личное мнение.
Padaboo
29.03.2016 22:05Идея насчет тайтла в коде интересная мысль:
<?php class MetaTagMap extends CComponent{ public static $map = [ 'credits/index' => [ 'description' => 'Взять кредит без залогов и поручителей', 'keywords' => 'кредит,взять кредит,кредит без поручителей,кредит без залогов,кредит без залогов и поручителей', 'title' => "Взять кредит" ], 'trucks/index' => [ 'description' => 'Поиск грузоперевозчиков', 'keywords' => 'перевезти груз,грузоперевозки', 'title' => "Грузоперевозки" ], 'buildings/index' => [ 'description' => 'Коммерческая недвижимость', 'keywords' => 'аренда офиса,аренда склада,купить склад,купить офис,купить здание,арендовать здание', 'title' => "Недвижимость" ], 'biztrade/index' => [ 'description' => 'Купить бизнес', 'keywords' => 'купить бизнес,купить долю,купить франшизу', 'title' => "Купить бизнес" ], 'materials/index' => [ 'description' => 'Товары ', 'keywords' => 'купить,', 'title' => "Купить " ], ]; public static function getKeywords($key, $add = ''){ return self::$map[$key]['keywords'].$add; } public static function getTitle($key, $add = ''){ return self::$map[$key]['title'] . " ". $add ." | ".Yii::app()->name; } public static function getDescription($key, $add = ''){ return self::$map[$key]['description']. ' ' . $add; } }
lnroma
29.03.2016 22:12Сорри не понял Вашего заявления… Собственно, title в коде смотря как использовать:
1. Когда просто сайтик без админки — почему бы нет если это ваш сайт и вам в принципе удобно напрямую работать с файлами(кодом) или базой.
2. Когда это cms — и вам необходимо хранить их в базе данных 'как они есть' так как, мета title,description,keywords и теги такие как h1,h2,p их содержимое должно редактироваться из админки и отображаться та как было это вписано в поле в админки. Что собственно приводит к гибкости проэкта.Padaboo
29.03.2016 23:55С одной стороны да, с другой нужно писать "паттерны" та как мета теги почти везде и всегда динамические, например: Отделение ВТБ 24 ${Новосибирск} Филиал № 5411 | concat{Все для бизнеса 2biz.net}
lnroma
30.03.2016 01:31С точки зрения СЕО они не должны быть динамические, вот несколько аргументов почему:
1. 'Сухие' тайтлы, с точки зрения пользователя я перейду 'Отделение ВТБ-24 в новосибирске', да и в сео есть приёмы как сокращение слов к примеру городов, 'Отд-ние ВТБ в городе Новосибирске', т.е. Вам придёться писать чуть ли не каждой странице свой уникальный title и description.
2. Обязательно найдёться город/регион, филиал, другая информация, которая сломает title(вероятность генерации неверных словосочетаний, к примеру из жизни «купить зонта в нашем интернет магазине»).
3. Meta Description должен быть в своем роде уникален.
4. Keywords тем более (хотя вроде как их можно опустить)
5. h1 страницы не должен повторять title и зависить от него, и он должен встречаться только один раз на странице
6. h2 подобно пункту 5.
7. p — «сеотексты» (в большинстве случаев он отображаеться в описание при выдачи) должен быть уникальным и в краткой форме раскрывать суть страницы.
8. etc
Я мог бы перечислять дальше но думаю, понятен ход моих мыслей. По этому можно добавить ваш «паттерн» способностью гибко редактировать, любое из свойств для странице, допустим title, и приоритет вывода довать title из базы если он существует.
affka
inline js, css, куча говнокода… вы удалите этот пост скорей, пока ждуниоры не стали вас в пример брать!
Padaboo
Перфикционизм?