Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Laravel Package

indexnowkit/doctrine

Doctrine ORM/DBAL integration for IndexNowKit. Track entity URL changes and submit them to IndexNow-compatible search engines with configurable listeners, repositories, and flexible mapping for Laravel and PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a low-risk 0.9.0 upgrade for most users. The functional surface is unchanged: IndexNowKit and IndexNowListener are still the entry points. The release mainly makes listener construction lazy and renames one constructor argument.

Minimal setup:

  1. Require the core package at the version this release expects:

    composer require indexnowkit/core:^0.13
    

    If you use a framework integration package, update it in the same command so it pulls core ^0.13 rather than mixing versions.

  2. Serve the IndexNow key file from the host root. The file must be publicly reachable at https://your-host/<key>.txt and contain exactly the key, with no redirects:

    // routes/web.php
    Route::get('/{key}.txt', function (string $key) {
        abort_unless($key === config('services.indexnow.key'), 404);
        return response(config('services.indexnow.key'), 200)
            ->header('Content-Type', 'text/plain');
    });
    
  3. Bind the kit in a service provider so it is shared and configurable:

    // app/Providers/AppServiceProvider.php
    $this->app->singleton(IndexNowKit::class, fn () => new IndexNowKit(
        key: config('services.indexnow.key'),
        host: config('services.indexnow.host'),
    ));
    
  4. First use case: submit a URL after publishing content. Use the same kit method the 0.9.0 sink example uses (collect()), or the kit’s documented submit/flush method:

    app(IndexNowKit::class)->collect(['https://example.com/blog/hello-world']);
    

Look first at IndexNowKit’s public methods and IndexNowListener’s constructor. In this release the listener can be built from a source closure and a sink closure, which is the important new integration point.

Implementation Patterns

Queue submissions after the transaction commits. IndexNow submissions are external HTTP calls; do not make them inline in a model save. Use an observer or event listener to dispatch a job:

// app/Observers/PostObserver.php
public function saved(Post $post): void
{
    SubmitToIndexNow::dispatch($post->canonicalUrl())->afterCommit();
}
// app/Jobs/SubmitToIndexNow.php
public function handle(IndexNowKit $kit): void
{
    $kit->collect($this->urls);
}

This also lets you retry on 429/5xx responses without holding a request open.

Use the 0.9.0 lazy listener pattern when you have a heavy graph. If you are adapting a Doctrine/Eloquent service graph, build the listener from closures so the change handler, submitter, client, and transport are not constructed until the first flush that actually has URLs:

$listener = new IndexNowListener(
    fn () => $services->changes(),   // $source, formerly $indexNow
    null,
    $staging,
    $logger,
    sink: fn (array $urls) => $services->kit()->collect($urls),
);

This is the main reason to adopt 0.9.0: an adapter with lazy services no longer boots the whole submission stack just to register the listener. Mirror this pattern in Laravel by resolving the kit and change source inside the closure, not in the service provider.

Batch by host and key. The conformance suite now runs scenario C04: URLs for two hosts produce one POST per host under that host’s own key. If you run multiple domains, group URLs by host before submitting and make sure each host maps to the correct key file. Do not send a mixed-host URL list to a single endpoint.

Use staging mode locally and in CI. Pass the staging flag into the listener so URLs are logged/collected without real HTTP calls. This is also useful in feature tests where you assert which URLs would be submitted.

Keep the facade constructor for simple apps. The release explicitly keeps the facade constructor working, including $autoFlush. If you do not need lazy construction, continue using it. Set autoFlush: false when you want to control submission timing manually, for example after a batch import.

Treat the sink as your collection boundary. The sink receives array $urls. In Laravel this is a clean place to de-duplicate, strip fragments, force https, and hand URLs to the kit. Keep the sink small and side-effect free where possible.

Gotchas and Tips

  • Constructor argument rename is the only breaking change. IndexNowListener::__construct() now names the first parameter $source (IndexNowKit|Closure), not $indexNow. Positional calls are unaffected. Search for indexNow: in your codebase; named calls must become source:.

  • Do not force symfony/var-exporter to v8 with Doctrine ORM 3. The package keeps ^6.4 || ^7.0 in require-dev because ORM 3 builds lazy ghosts with the 6.4/7 API and fails with “Symfony LazyGhost is not available” unless native lazy objects are enabled. If your app uses ORM 3, keep the same constraint or enable native lazy objects deliberately.

  • Closures run late. The source closure (fn () => $services->changes()) is evaluated on the first flush that has something to classify, not at construction. Do not capture request-scoped state that may be stale by flush time. Resolve services inside the closure.

  • The sink is not called on every flush. It is invoked for collected URLs when there is something to classify. Do not rely on it as a general-purpose “listener was registered” hook.

  • Named argument breakage may be hidden in bundle wiring. The release notes say the bundle used the named argument, so if you copied bundle config or a service definition, update indexNow: to source:. Positional service definitions are fine.

  • Key file must be exact and public. IndexNow validates that https://host/<key>.txt returns the key. No auth, no redirect, no HTML wrapper. A mismatch produces 403 responses that look like submission failures.

  • Submit absolute, canonical URLs only. Include the scheme and host. Do not submit localhost, signed preview URLs, or URLs with fragments. If your app uses trailing-slash redirects, submit the final URL.

  • Respect rate limits. Batch URLs per host and queue retries with backoff. Avoid submitting on every save; debounce or submit on publish/unpublish transitions.

  • ORM 3 + DBAL 3 is now actually installed in CI. The release fixes the composer ci:install:orm3dbal3 job and uses phpstan.dbal3.neon because the config switches on the DBAL major, not the ORM major. This is mostly relevant if you maintain extensions or your own compatibility matrix; for consumers it confirms the declared ORM 3 + DBAL 3 combination is exercised.

  • Requires indexnowkit/core ^0.13. Check composer show indexnowkit/core after upgrading. If you have a locked older core, the new listener signature may not line up with the integration package.

  • Extension point: the closure-based $source and sink are the right seam for non-Doctrine sources. In Laravel, use sink: fn (array $urls) => app(IndexNowKit::class)->collect($urls) and a source closure that returns your own change set rather than trying to reuse Adapter\Services.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings