indexnowkit / yii2
IndexNow for Yii2: notify Yandex, Bing, Naver and Seznam when ActiveRecord rows change. #[IndexNow] attribute on the model, commit-safe without transaction hooks, key file route, console commands, yii2-queue dispatch.
Requires
- php: ^8.2
- ext-json: *
- indexnowkit/core: ^0.5.1
- psr/log: ^1.1 || ^2.0 || ^3.0
- symfony/console: ^6.4 || ^7.0 || ^8.0
- yiisoft/yii2: ^2.0.45
- yiisoft/yii2-composer: ^2.0.10
Requires (Dev)
- indexnowkit/sitemap: ^0.1.1
- phpstan/phpstan: ^2.2.13
- phpstan/phpstan-strict-rules: ^2.0.12
- phpunit/phpunit: ^11.5
- yidas/yii2-composer-bower-skip: ^2.0
- yiisoft/yii2-queue: ^2.3
Suggests
- indexnowkit/sitemap: The indexnow/sitemap command (^0.1.1)
- nyholm/psr7: PSR-17 factories for the PSR-18 client
- symfony/http-client: PSR-18 client for the submissions (with nyholm/psr7); any PSR-18 client works
- yiisoft/yii2-queue: dispatch: queue — SubmitUrlsJob with retries in a worker
Provides
None
Conflicts
None
Replaces
None
README
Tell search engines about new, changed and deleted pages the moment an ActiveRecord row is committed. One attribute on the model, one component, done.
Who gets notified
Yandex, Bing (and DuckDuckGo via Bing), Naver, Seznam, Yep — every engine that implements the IndexNow protocol. One request to the shared endpoint reaches all of them.
Google: no. Google does not support IndexNow; this package will not pretend otherwise.
Install
composer require indexnowkit/yii2 composer require indexnowkit/sitemap # optional: the indexnow/sitemap command php yii indexnow/key-generate --write-env # INDEXNOW_KEY in .env (or print it) php yii indexnow/check # options, key file reachable, queue, cache, URL rules
// config/web.php and config/console.php 'bootstrap' => ['indexnow'], 'components' => [ 'indexnow' => [ 'class' => \IndexNowKit\Yii2\IndexNowComponent::class, 'options' => [ 'key' => getenv('INDEXNOW_KEY'), 'base_url' => 'https://www.example.com', // used by console commands and queue workers ], ], ],
The package needs a PSR-18 client (symfony/http-client + nyholm/psr7, or Guzzle); it discovers one, or takes
the component/class named in http.client. Pretty URLs (urlManager.enablePrettyUrl) are required for the key file
route /<key>.txt.
Declare what has a public page
#[IndexNow] is repeatable: one attribute per family of public URLs. IndexNowBehavior registers the hooks.
use IndexNowKit\Attribute\{IndexNow, IndexNowDefaults}; use IndexNowKit\Yii2\ActiveRecord\IndexNowBehavior; #[IndexNowDefaults(when: 'published', fields: ['slug', 'title', 'body', 'published'])] #[IndexNow(route: 'post/view', params: ['slug' => 'slug'])] #[IndexNow(route: 'post/amp', params: ['slug' => 'slug'], when: 'amp')] #[IndexNow(via: 'category')] // a changed post also refreshes its category page #[IndexNow(urls: ['/'])] // and the homepage final class Post extends ActiveRecord { public function behaviors(): array { return [IndexNowBehavior::class]; } }
| Option | Meaning |
|---|---|
route / params |
a Yii route (controller/action) and param => attribute, method, "self", dotted.path (self = the primary key) |
resolver |
a UrlResolverInterface class or component id for anything custom |
via |
a relation (or dotted path) whose pages are resubmitted |
url / urls |
a method returning the URL(s), or literal URLs |
when / whenFields |
bool attribute or method; drafts are skipped and published → draft is sent as a deletion |
fields |
for updates, submit only when one of these attributes changed |
events, locales, host, name |
subset of events; current/all/list (router.languages); another host; stable rule id |
Accessors read ActiveRecord attributes and relations (category.slug) and fall back to methods. A when column
that only has a database default is null on a fresh record: call $this->loadDefaultValues() in init() or set
the attribute before save().
Classes you cannot annotate: 'active_record' => ['models' => [Product::class]] in the options, or
Yii::$app->indexnow->observe(Product::class, [new IndexNow(...)]) at runtime.
Full model, typed parameters, inheritance and the semantics table: core attribute reference.
How it works
- URLs are resolved in the ActiveRecord event, while the old state is live (
changedAttributesonafterUpdate, the row and its relations inbeforeDelete). A renamed page announces its old URL as deleted. - Outside a transaction they go to the request collector right away. Inside one, Yii2 gives no savepoint events, so
they are held with a verifier and re-read by primary key when the transaction commits: a change the row does
not show (an inner
beginTransaction()that rolled back) is dropped with every URL it produced. A rollback drops everything. OneSELECTper changed record, only inside explicit transactions. Details: docs/commit-safety.md. - Everything collected during one request is sent after the response (
Response::EVENT_AFTER_SEND), in one batch; console commands flush when they end, queue workers after every job. dispatch: auto(default) pushes aSubmitUrlsJobto thequeuecomponent whenyiisoft/yii2-queueis configured (429/5xx re-pushed with the delay ofretry.*,Retry-Afterhonoured), else sends synchronously. Details: docs/queue.md.- Nothing thrown from a rule, a resolver or the HTTP layer reaches your application: it is logged under the
indexnowcategory, the save succeeds. An invalid configuration disables IndexNow with onecriticalline;php yii indexnow/checkprints the exact error.
Commands
| Command | Options |
|---|---|
indexnow/check |
--live real probe · --host= one host · --probe-url= page for the probe |
indexnow/submit <urls...> |
--force ignore debounce · --dry-run · --json |
indexnow/submit-record <class> [ids...] |
--event= · --limit= · --explain · --force · --dry-run · --json |
indexnow/explain <class> <id> |
--event= — rules, when, URLs, key, debounce; sends nothing |
indexnow/sitemap [sitemap] |
--changed-since="1 day" · --allow-foreign-hosts · --force · --dry-run · --json |
indexnow/key-generate |
--length · --alphanumeric · --write-env[=FILE] · --force rotate |
<class> is an FQCN or a short name under app\models. Ids are space- or comma-separated.
Sitemaps
composer require indexnowkit/sitemap # optional: the indexnow/sitemap command
indexnow/sitemap with no argument reads sitemap.url, else <base_url>/sitemap.xml; a local path works too.
Without the package everything else works unchanged: indexnow/sitemap says indexnowkit/sitemap is not installed: composer require indexnowkit/sitemap and exits 1, indexnow/check prints sitemap: not installed (…),
a sitemap block in the options is ignored, sitemapConfig() / sitemapSource() throw a LogicException with
the same sentence. Nothing is logged about it.
Configuration and docs
Every option, its default and what it does: docs/configuration.md. Commit safety: docs/commit-safety.md. Replacing pieces, custom resolvers, checks: docs/extending.md. Queue, retries, failures: docs/queue.md. Testing your integration: docs/testing.md.
Debugging
php yii indexnow/check validates the options, fetches the key file and reports how submissions are wired (queue,
cache, pretty URLs, ActiveRecord hooks, sitemap spool); php yii indexnow/explain app\models\Post 1 shows the
rules, guards and URLs of one record without sending anything; the indexnow log category at debug tells why a
URL was or was not submitted. Symptoms and fixes: docs/troubleshooting.md.
Limitations
updateAll(),deleteAll(),updateAttributes(),updateCounters()fire no events (conformance A13): callYii::$app->indexnow->submitRecords(Post::find()->where(...)->all())orphp yii indexnow/submit-recordafterwards.link()/unlink()write the junction row with a plain command, no event on the owner: save the owner with a bumped timestamp afterwards ($post->updated_at = time(); $post->save(false)), or callsubmitRecord($post).- The sync driver of
yii2-queueignores the delay between attempts: 429/5xx attempts run back-to-back (development only,checkwarns). - Without pretty URLs the key file cannot be routed: enable them, or serve
/<key>.txtas a static file and setkey_file.enabled: false.
Compatibility
Public API: the options tree, command names and options, IndexNowComponent methods and properties,
ActiveRecord\IndexNowBehavior, Queue\SubmitUrlsJob. The core's rules apply:
bc.md. Before 1.0 a minor version may break; every
break is listed under "Changed" in CHANGELOG.md. Yii 2.0.45+, PHP 8.2–8.5.
Other frameworks
| PHP | core, symfony-bundle, doctrine, laravel |
| JS/TS | @indexnowkit/core, next, prisma (soon) |
| Python | indexnowkit, indexnowkit-django (soon) |
MIT. IndexNow is a trademark of its owner; this project is independent and not affiliated with Microsoft, Yandex or indexnow.org.