Extending¶
Replacing pieces¶
Yii2 has no application-wide interface bindings, so the component builds the core graph itself and exposes the seams as properties (an instance, a config array, a class name or a component id):
| Property | Interface | Use it for |
|---|---|---|
transport |
IndexNowKit\Http\TransportInterface |
a recording transport in tests, a proxy |
debounceStore |
IndexNowKit\Debounce\DebounceStoreInterface |
a shared store the cache component cannot express |
dispatcher |
IndexNowKit\Dispatch\DispatcherInterface |
another queue, an outbox table |
urlResolver |
IndexNowKit\Url\UrlResolverInterface |
replace the whole "object → URLs" step |
logger |
PSR-3 | anything but Yii's logger |
checks |
list of IndexNowKit\Check\CheckInterface |
extra lines in indexnow/check (a CDN purge, a tenant table) |
Underneath, Wiring describes the graph once with the core's Adapter\ServicesBuilder (the properties above
are its overrides, resolved by References; the Yii pieces are closures) and services() returns the lazy
Adapter\Services; nothing is
built before it is used, and a request that collects nothing builds nothing. Everything built is also readable, as
delegates: kit(), config(), submitter(), collector(), keys(), transport(), debounceStore(),
routeResolver(), observer(), staging(), checker(), rules(), and sitemapConfig() / sitemapSource() with
the optional indexnowkit/sitemap installed (sitemapInstalled(); without it they throw a LogicException naming
the package).
Custom resolvers¶
#[IndexNow(resolver: ProductUrlResolver::class)] // a class Yii::$container can build
#[IndexNow(resolver: 'productUrls')] // or an application component id
The class implements IndexNowKit\Url\UrlResolverInterface; constructor dependencies come from Yii::$container.
Rules at runtime¶
Yii::$app->indexnow->observe(Product::class, [new IndexNow(route: 'product/view', params: ['id' => 'self'])], new IndexNowDefaults(when: 'active'));
Yii::$app->indexnow->rules()->registerFor(Page::class, fn (Page $page): ?RuleSet => ...); // decided per object
observe() hooks the class through class-level events (yii\base\Event::on), the same mechanism as the
active_record.models list.
Manual submissions¶
submit(iterable $urls), submitRecord($record, Event $event), submitRecords(iterable $records) (one request for
many), urlsFor(), explain() return Results; collect() parks URLs in the request collector, flush() sends
them now. Listen to results the Yii way: Yii::$app->indexnow->on(IndexNowComponent::EVENT_RESULT, fn (ResultEvent $e) => ...)
(Event\ResultEvent::$result is the Result; the submitter and the command submitters raise it for every result), or
lower-level Yii::$app->indexnow->submitter()->addListener(fn (Result $r) => ...).
What is the core's¶
The observer keeps only what is Yii's (the change set from changedAttributes, the previous state, the
verify-on-commit staging); guarding, logging and the URLs of a row about to be deleted are the core's
Hook\ObserverHelper. SubmitUrlsJob is Retry\WorkerOutcome plus a delayed re-push of the rejected URLs (the delay
of Retry\RetryPolicy from retry.*, Retry-After first; yii2-queue's canRetry() only concerns exceptions,
see queue.md). The options of every indexnow/<action> come from Console\Definitions
and Sitemap\Console\Definitions, names, shortcuts, descriptions and defaults (getActionOptionsHelp() reads them
from the definitions, not from the property docblocks), so php yii help indexnow/submit-record matches the bundle
and artisan.
Console¶
controllerMap['indexnow'] accepts loader (IndexNowKit\Console\SubjectLoaderInterface, tenant scoping or another
id format), formatter (ResultFormatterInterface, your JSON envelope) and submitters (SubmitterFactoryInterface).
The command bodies are the IndexNowKit\Console\*Runner of indexnowkit/console; a tenant loop over SubmitSubjectsRunner is a
ten-line controller action.