Search by

nedarta / yii2-seo

nedarta

SEO metadata behavior for Yii2 ActiveRecord models with multilingual support.

Package info

github.com/nedarta/yii2-seo

Type:yii2-extension

pkg:composer/nedarta/yii2-seo

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-10-03 17:34 UTC

This package is auto-updated.

Last update: 2026-10-03 20:32:35 UTC


README

A small Yii2 SEO extension that stores SEO metadata separately from the application model and supports multilingual fields through lav45/yii2-translated-behavior.

The extension is a clean rewrite of the original dvizh/yii2-seo concept, with these changes:

  • nedarta\seo namespace.
  • PSR-4 autoloading.
  • snake_case database column names.
  • multilingual title, description, keywords, h1 and text.
  • language-independent meta_index and redirect_301.
  • no Bootstrap dependency.
  • no JavaScript dependency.
  • no hard-coded application model classes.
  • one SEO record per (model_name, item_id).
  • translations stored in a dedicated seo_lang table.
  • normal Yii2 model/form validation and saving.

Installation

composer require nedarta/yii2-seo

The package requires:

yiisoft/yii2
lav45/yii2-translated-behavior

Database

Run the included migration:

yii migrate --migrationPath=@vendor/nedarta/yii2-seo/src/migrations

It creates:

seo

id
item_id
model_name
meta_index
redirect_301

seo_lang

seo_id
lang_id
title
description
keywords
h1
text

The unique key on seo(model_name, item_id) prevents duplicate SEO records for the same application model.

Configure the model

Attach SeoFields to any ActiveRecord model:

<?php

namespace common\models;

use nedarta\seo\behaviors\SeoFields;
use yii\db\ActiveRecord;

class Product extends ActiveRecord
{
    public function behaviors()
    {
        return [
            'seo' => [
                'class' => SeoFields::class,
            ],
        ];
    }
}

The behavior exposes:

$product->seo

which returns a nedarta\seo\models\Seo model.

Multilingual SEO

Seo uses LAV45's translated behavior internally.

Translated fields:

title
description
keywords
h1
text

Non-translated fields:

meta_index
redirect_301

For example:

$product->seo->title;
$product->seo->description;

return the values for the current language configured by lav45/yii2-translated-behavior.

The SEO model contains:

use lav45\translate\TranslatedBehavior;
use lav45\translate\TranslatedTrait;

class Seo extends ActiveRecord
{
    use TranslatedTrait;

    public function behaviors()
    {
        return [
            [
                'class' => TranslatedBehavior::class,
                'translateRelation' => 'seoLangs',
                'translateAttributes' => [
                    'title',
                    'description',
                    'keywords',
                    'h1',
                    'text',
                ],
            ],
        ];
    }
}

Form

In a normal Yii2 form:

<?php $form = ActiveForm::begin(); ?>

<?= $form->field($model, 'name')->textInput() ?>

<?= \nedarta\seo\widgets\SeoForm::widget([
    'model' => $model,
    'form' => $form,
]) ?>

<?php ActiveForm::end(); ?>

The widget does not save anything itself. It only renders fields for the SEO model. SeoFields persists the submitted SEO data when the owner model is saved.

Create/update flow

For an existing model:

$model->seo

loads the corresponding SEO row.

If no SEO row exists, the behavior returns a new Seo instance.

When the owner model is saved, the behavior saves the SEO record if SEO data was submitted.

The SEO translation rows are then handled by LAV45's TranslatedBehavior.

Explicit access

You can also work with the SEO model directly:

$seo = $product->seo;

$seo->title = 'Red bicycle';
$seo->description = 'A lightweight red bicycle.';

$seo->save();

The language used by translated attributes is controlled by LAV45's behavior configuration and application language.

Field requirements

Every SEO field - title, description, keywords, h1, text, meta_index, redirect_301 - is optional. Neither Seo nor SeoLang declares a required rule for them, since a page is allowed to have no SEO overrides at all. Add your own required rule in an application-level subclass if your project needs specific fields filled in.

Model name

By default, the extension uses the complete PHP class name:

common\models\Product

rather than only:

Product

This avoids collisions between classes with the same short name.

You can override this:

'seo' => [
    'class' => SeoFields::class,
    'modelName' => 'catalog_product',
],

Deleting

When an owner model is deleted, its SEO record is deleted as well. The database foreign key then cascades to seo_lang.

Migration path

The migration class is intentionally named with a current-style timestamp. If you copy the migration into an application's own migration directory, rename it according to that application's migration convention.

Testing

Install development dependencies:

composer install

Run:

vendor/bin/phpunit

The test suite covers:

  • SEO model schema rules.
  • SEO owner lookup.
  • creation of an SEO record.
  • model-name isolation.
  • multilingual SEO relation configuration.
  • deletion behavior.
  • form rendering.

License

BSD-3-Clause. See LICENSE.