├── .gitignore ├── LICENSE.md ├── MetaTagBehavior.php ├── MetaTags.php ├── MetaTagsComponent.php ├── README.md ├── composer.json ├── messages ├── config.php ├── de │ └── messages.php ├── en │ └── messages.php ├── fr │ └── messages.php ├── hr │ └── messages.php ├── it │ └── messages.php ├── pt │ └── messages.php ├── ru │ └── messages.php └── zh │ └── messages.php ├── migrations ├── m150312_172156_meta_tags.php └── readme.txt ├── models └── MetaTag.php └── views └── MetaTags.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | composer.lock 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Artem Shmanovsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MetaTagBehavior.php: -------------------------------------------------------------------------------- 1 | 'afterSave', 27 | ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave', 28 | ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete', 29 | ]; 30 | } 31 | 32 | 33 | public function afterSave($event) 34 | { 35 | if(Yii::$app->request instanceof Request){ 36 | $attributes = Yii::$app->request->post('MetaTag', Yii::$app->request->get('MetaTag', null) ); 37 | 38 | if($attributes) 39 | { 40 | $model = $this->getModel(); 41 | 42 | if(!isset($model)) 43 | $model = new MetaTag(); 44 | 45 | $attributes['model_id'] = $this->owner->id; 46 | $attributes['model'] = (new \ReflectionClass($this->owner))->getShortName(); 47 | 48 | $model->attributes = $attributes; 49 | $model->save(); 50 | } 51 | } 52 | } 53 | 54 | 55 | public function afterDelete($event) 56 | { 57 | MetaTag::deleteAll([ 58 | 'model_id' => $this->owner->id, 59 | 'model' => (new \ReflectionClass($this->owner))->getShortName() 60 | ]); 61 | } 62 | 63 | 64 | public function getModel() 65 | { 66 | $model = MetaTag::findOne([ 67 | 'model_id' => $this->owner->id, 68 | 'model' => (new \ReflectionClass($this->owner))->getShortName() 69 | ]); 70 | 71 | if($model == null) 72 | $model = new MetaTag(); 73 | 74 | $this->_model = $model; 75 | 76 | return $model; 77 | } 78 | 79 | 80 | public function getTitle() 81 | { 82 | $model = $this->_model; 83 | if(!isset($model)) 84 | $model = $this->getModel(); 85 | 86 | return isset($model) ? $model->title : ''; 87 | } 88 | 89 | public function getDescription() 90 | { 91 | $model = $this->_model; 92 | if(!isset($model)) 93 | $model = $this->getModel(); 94 | 95 | return isset($model) ? $model->description : ''; 96 | } 97 | 98 | public function getKeywords() 99 | { 100 | $model = $this->_model; 101 | if(!isset($model)) 102 | $model = $this->getModel(); 103 | 104 | return isset($model) ? $model->keywords : ''; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /MetaTags.php: -------------------------------------------------------------------------------- 1 | model->getBehavior(MetaTagsComponent::$behaviorName)) 31 | { 32 | throw new Exception(self::t('messages', 'widget_behavior_exception {behaviorName}', ['behaviorName' => MetaTagsComponent::$behaviorName]), 500); 33 | } 34 | } 35 | 36 | 37 | public static function registerTranslations() 38 | { 39 | $i18n = Yii::$app->i18n; 40 | $i18n->translations['metaTags/*'] = [ 41 | 'class' => 'yii\i18n\PhpMessageSource', 42 | 'sourceLanguage' => 'sys', 43 | 'basePath' => '@vendor/v0lume/yii2-meta-tags/messages', 44 | 'fileMap' => [ 45 | 'metaTags/messages' => 'messages.php', 46 | ], 47 | ]; 48 | } 49 | 50 | 51 | public function run() 52 | { 53 | $model = new MetaTag; 54 | 55 | if(!$this->model->isNewRecord) 56 | { 57 | $meta_tag = MetaTag::findOne([ 58 | 'model_id' => $this->model->id, 59 | 'model' => (new \ReflectionClass($this->model))->getShortName() 60 | ]); 61 | 62 | if(isset($meta_tag)) 63 | $model = $meta_tag; 64 | } 65 | 66 | return $this->render('MetaTags', [ 67 | 'model' => $model, 68 | 'form' => $this->form, 69 | ]); 70 | } 71 | 72 | 73 | public static function t($category, $message, $params = [], $language = null) 74 | { 75 | self::registerTranslations(); 76 | 77 | return Yii::t('metaTags/' . $category, $message, $params, $language); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /MetaTagsComponent.php: -------------------------------------------------------------------------------- 1 | generateCsrf && Yii::$app->request->enableCsrfValidation) 23 | { 24 | Yii::$app->view->registerMetaTag(['name' => 'csrf-param', 'content' => Yii::$app->request->csrfParam], 'csrf-param'); 25 | Yii::$app->view->registerMetaTag(['name' => 'csrf-token', 'content' => Yii::$app->request->csrfToken], 'csrf-token'); 26 | } 27 | 28 | if(isset($model) && $model->getBehavior(self::$behaviorName)) 29 | { 30 | $meta_tag = $model->getBehavior(self::$behaviorName)->model; 31 | 32 | Yii::$app->view->registerMetaTag(['name' => 'title', 'content' => $meta_tag->title], 'title'); 33 | Yii::$app->view->registerMetaTag(['name' => 'keywords', 'content' => $meta_tag->keywords], 'keywords'); 34 | Yii::$app->view->registerMetaTag(['name' => 'description', 'content' => $meta_tag->description], 'description'); 35 | 36 | if($this->generateOg) 37 | { 38 | Yii::$app->view->registerMetaTag(['property' => 'og:title', 'content' => $meta_tag->title], 'og:title'); 39 | Yii::$app->view->registerMetaTag(['property' => 'og:description', 'content' => $meta_tag->description], 'og:description'); 40 | Yii::$app->view->registerMetaTag(['property' => 'og:url', 'content' => \yii\helpers\Url::to('', true)], 'og:url'); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yii2-meta-tags 2 | DB based model meta data for SEO 3 | 4 | ![screenshot](https://cloud.githubusercontent.com/assets/5075100/6626492/fc4a689e-c907-11e4-85aa-af653a455ad0.jpg) 5 | 6 | Installation 7 | ------------ 8 | 9 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 10 | 11 | Either run 12 | 13 | ```php 14 | composer.phar require --prefer-dist v0lume/yii2-meta-tags "*" 15 | ``` 16 | 17 | or add 18 | 19 | ``` 20 | "v0lume/yii2-meta-tags": "*" 21 | ``` 22 | 23 | to the require section of your `composer.json` file. 24 | 25 | Usage 26 | ------------ 27 | 28 | Add `MetaTagBehavior` to your model, and configure it. 29 | 30 | ```php 31 | 32 | public function behaviors() 33 | { 34 | return [ 35 | 'MetaTag' => [ 36 | 'class' => MetaTagBehavior::className(), 37 | ], 38 | ]; 39 | } 40 | ``` 41 | 42 | Add `MetaTags` somewhere in you application, for example in editing form. 43 | 44 | ```php 45 | echo MetaTags::widget([ 46 | 'model' => $model, 47 | 'form' => $form 48 | ]); 49 | ``` 50 | 51 | Done! Now, you can get meta data of your current model: 52 | 53 | ```php 54 | echo $model->getBehavior('MetaTag')->title; 55 | echo $model->getBehavior('MetaTag')->keywords; 56 | echo $model->getBehavior('MetaTag')->description; 57 | ``` 58 | 59 | Or, by manually find model: 60 | 61 | ```php 62 | use v0lume\yii2\metaTags\model\MetaTag; 63 | 64 | ... 65 | 66 | $meta_tag = MetaTag::findOne([ 67 | 'model_id' => $id, 68 | 'model' => (new \ReflectionClass($model))->getShortName() 69 | ]); 70 | 71 | ... 72 | 73 | echo $meta_tag->title; 74 | echo $meta_tag->keywords; 75 | echo $meta_tag->description; 76 | ``` 77 | 78 | Auto registration meta tags 79 | ------------ 80 | You can use `MetaTagsComponent` to perform auto registration meta tags 81 | 82 | Configure `MetaTagsComponent` in `main.php` config: 83 | 84 | ```php 85 | 86 | ... 87 | 'components' => [ 88 | ... 89 | 'metaTags' => [ 90 | 'class' => 'v0lume\yii2\metaTags\MetaTagsComponent', 91 | 'generateCsrf' => false, 92 | 'generateOg' => true, 93 | ], 94 | ... 95 | ], 96 | ... 97 | 98 | ``` 99 | 100 | And then, in your layouts or views or controller action 101 | 102 | ```php 103 | $model = \common\models\Page::findOne(['url' => '/']); 104 | 105 | Yii::$app->metaTags->register($model); 106 | ``` 107 | 108 | If passed $model was attached `MetaTagBehavior`, component will register meta tags for that model. If `MetaTagBehavior` wasn't attached or model not passed, and `generateCsrf` is set to true, component will generate only csrf meta tags. 109 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v0lume/yii2-meta-tags", 3 | "description": "DB based model meta data for SEO", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","extension","meta","meta tags","seo","behavior","active record"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Artem Shmanovsky", 10 | "email": "heads@xakep.ru" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "*" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "v0lume\\yii2\\metaTags\\": "" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /messages/config.php: -------------------------------------------------------------------------------- 1 | __DIR__ . DIRECTORY_SEPARATOR . '..', 5 | // array, required, list of language codes that the extracted messages 6 | // should be translated to. For example, ['zh-CN', 'de']. 7 | 'languages' => ['en', 'ru', 'de', 'zh', 'fr', 'it', 'hr', 'pt'], 8 | // string, the name of the function for translating messages. 9 | // Defaults to 'Yii::t'. This is used as a mark to find the messages to be 10 | // translated. You may use a string for single function name or an array for 11 | // multiple function names. 12 | 'translator' => 'MetaTags::t', 13 | // boolean, whether to sort messages by keys when merging new messages 14 | // with the existing ones. Defaults to false, which means the new (untranslated) 15 | // messages will be separated from the old (translated) ones. 16 | 'sort' => false, 17 | // boolean, whether to remove messages that no longer appear in the source code. 18 | // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks. 19 | 'removeUnused' => false, 20 | // array, list of patterns that specify which files/directories should NOT be processed. 21 | // If empty or not set, all files/directories will be processed. 22 | // A path matches a pattern if it contains the pattern string at its end. For example, 23 | // '/a/b' will match all files and directories ending with '/a/b'; 24 | // the '*.svn' will match all files and directories whose name ends with '.svn'. 25 | // and the '.svn' will match all files and directories named exactly '.svn'. 26 | // Note, the '/' characters in a pattern matches both '/' and '\'. 27 | // See helpers/FileHelper::findFiles() description for more details on pattern matching rules. 28 | 'only' => ['*.php'], 29 | // array, list of patterns that specify which files (not directories) should be processed. 30 | // If empty or not set, all files will be processed. 31 | // Please refer to "except" for details about the patterns. 32 | // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. 33 | 'except' => [ 34 | '.DS_Store', 35 | '.svn', 36 | '.git', 37 | '.gitignore', 38 | '.gitkeep', 39 | '.hgignore', 40 | '.hgkeep', 41 | '/messages', 42 | '/migrations', 43 | ], 44 | // 'php' output format is for saving messages to php files. 45 | 'format' => 'php', 46 | // Root directory containing message translations. 47 | 'messagePath' => __DIR__, 48 | // boolean, whether the message file should be overwritten with the merged messages 49 | 'overwrite' => true, 50 | ]; 51 | -------------------------------------------------------------------------------- /messages/de/messages.php: -------------------------------------------------------------------------------- 1 | 'Beschreibung', 21 | 'model_keywords' => 'Schlüsselwörter', 22 | 'model_title' => 'Titel', 23 | 'widget_behavior_exception {behaviorName}' => 'Modell muss verhalten {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/en/messages.php: -------------------------------------------------------------------------------- 1 | 'Description', 21 | 'model_keywords' => 'Keywords', 22 | 'model_title' => 'Title', 23 | 'widget_behavior_exception {behaviorName}' => 'Model must have behavior {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/fr/messages.php: -------------------------------------------------------------------------------- 1 | 'Description', 21 | 'model_keywords' => 'Mots-clés', 22 | 'model_title' => 'Titre', 23 | 'widget_behavior_exception {behaviorName}' => 'Modèle doit avoir un comportement {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/hr/messages.php: -------------------------------------------------------------------------------- 1 | 'Opis', 21 | 'model_keywords' => 'Ključne riječi', 22 | 'model_title' => 'Naslov', 23 | 'widget_behavior_exception {behaviorName}' => 'Model mora imati ponašanje {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/it/messages.php: -------------------------------------------------------------------------------- 1 | 'Descrizione', 21 | 'model_keywords' => 'Parole chiave', 22 | 'model_title' => 'Titolo', 23 | 'widget_behavior_exception {behaviorName}' => 'Modello deve avere un comportamento {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/pt/messages.php: -------------------------------------------------------------------------------- 1 | 'Descrição', 21 | 'model_keywords' => 'Palavras-chave', 22 | 'model_title' => 'Título', 23 | 'widget_behavior_exception {behaviorName}' => 'Modelo deve ter comportamento {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/ru/messages.php: -------------------------------------------------------------------------------- 1 | 'Описание', 21 | 'model_keywords' => 'Ключевые слова', 22 | 'widget_behavior_exception {behaviorName}' => 'Модель должна иметь поведение {behaviorName}', 23 | 'model_title' => 'Заголовок', 24 | ]; 25 | -------------------------------------------------------------------------------- /messages/zh/messages.php: -------------------------------------------------------------------------------- 1 | '描述', 21 | 'model_keywords' => '關鍵詞', 22 | 'model_title' => '稱號', 23 | 'widget_behavior_exception {behaviorName}' => '模型必須有行為 {behaviorName}', 24 | ]; 25 | -------------------------------------------------------------------------------- /migrations/m150312_172156_meta_tags.php: -------------------------------------------------------------------------------- 1 | createTable('{{%meta_tags}}', [ 13 | 'id' => Schema::TYPE_PK, 14 | 'model' => Schema::TYPE_STRING . ' NOT NULL', 15 | 'model_id' => Schema::TYPE_INTEGER . ' NOT NULL', 16 | 'title' => Schema::TYPE_STRING . ' NOT NULL', 17 | 'keywords' => Schema::TYPE_STRING . ' NOT NULL', 18 | 'description' => Schema::TYPE_TEXT . ' NOT NULL', 19 | 'time_update' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0', 20 | ]); 21 | 22 | $this->createIndex('object', '{{%meta_tags}}', ['model', 'model_id'], true); 23 | } 24 | 25 | public function safeDown() 26 | { 27 | $this->dropTable('{{%meta_tags}}'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /migrations/readme.txt: -------------------------------------------------------------------------------- 1 | To run the migrations for this extension go to your app folder and run the following command 2 | 3 | ./yii migrate --migrationPath="@vendor/v0lume/yii2-meta-tags/migrations" 4 | -------------------------------------------------------------------------------- /models/MetaTag.php: -------------------------------------------------------------------------------- 1 | 255], 35 | [['model', 'model_id', 'title', 'keywords', 'description'], 'safe'], 36 | ]; 37 | } 38 | 39 | /** 40 | * @return array customized attribute labels (name=>label) 41 | */ 42 | public function attributeLabels() 43 | { 44 | return [ 45 | 'title' => MetaTags::t('messages', 'model_title'), 46 | 'keywords' => MetaTags::t('messages', 'model_keywords'), 47 | 'description' => MetaTags::t('messages', 'model_description'), 48 | ]; 49 | } 50 | 51 | 52 | public function behaviors() 53 | { 54 | return [ 55 | [ 56 | 'class' => TimestampBehavior::className(), 57 | 'createdAtAttribute' => false, 58 | 'updatedAtAttribute' => 'time_update', 59 | ], 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /views/MetaTags.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | field($model, 'title')->textInput(['maxlength' => 255]) ?> 11 | 12 | field($model, 'keywords')->textInput(['maxlength' => 255]) ?> 13 | 14 | field($model, 'description')->textArea() ?> 15 | --------------------------------------------------------------------------------