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

Scolta Php Laravel Package

tag1/scolta-php

A Composer-installable PHP integration for Scolta, offering a clean wrapper around its API so you can add Scolta-powered search and content discovery to PHP or Laravel applications with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Scolta is Tag1's embeddable search engine; tag1/scolta-php is the PHP binding that loads the scolta-core WebAssembly module through the Extism PHP SDK and exposes it to platform adapters (Drupal, WordPress, Laravel). Treat it as an early-stage, single-maintainer binding (3 stars, 0 dependents), not as a drop-in replacement for Scout/Meilisearch. Budget a spike day before committing.

Prerequisites, in order — the extension is the hard part, not the composer package:

  1. PHP 8.2+ and a working Extism PHP extension (php -m | grep extism). Install via pie install extism/extism, pecl install extism, or compile from the extism/php-sdk release for your platform. This is a native Rust extension — most cheap shared hosts and some managed PaaS will refuse it.
  2. The scolta-core .wasm artifact. The composer package ships the binding, not the engine. Pull the .wasm from the tag1consulting/scolta-core releases and commit/pin it as a deploy artifact.
  3. composer require tag1/scolta-php

Where to look first: the README is thin, so read src/ in this order — the Laravel/platform adapter, the document/indexing DTOs, then the Extism call site (the file that instantiates the WASM module). That call site tells you the real API surface: function names, JSON payload shapes, and whether the index is memory-resident or file-backed. That single question determines your whole architecture.

First use case: index one posts table and query it. Not a full site search — one model, id/title/body/url, ten documents, one query, assert the right ID comes back. If that works end-to-end inside a queued job and inside a web request without the index vanishing, you've validated the only two things that matter.

Implementation Patterns

Bind the client as a singleton, never new it per request. WASM instantiation + module init is the dominant cost. Register it in a service provider:

$this->app->singleton(ScoltaClient::class, fn ($app) => new ScoltaClient(
    modulePath: config('scolta.module'),
    indexPath:  storage_path('app/scolta'),
));

Do not put env() or closures in config/scolta.php — php artisan config:cache will bite you.

Separate "build" from "serve." The pattern that holds up:

  • A scolta:build artisan command (you'll write it; don't assume the package ships one) that constructs the index from scratch.
  • A queued RebuildSearchIndex job triggered on deploy, plus model observers dispatching per-document upserts on saved/deleted.
  • A SearchService / repository class that controllers call — never the client directly. Return DTOs (SearchResult) with id, score, highlight, so you can swap backends later without touching views.

Keep the index outside the request lifecycle. Write to storage_path('app/scolta'), not sys_get_temp_dir() (per-worker, wiped unpredictably) and not public/. On multi-server or ephemeral-filesystem setups, build the index during deploy and treat it as a read-only artifact baked into the image.

Wrap it in a fallback. Because the adapter is young, put the whole thing behind an interface with a DatabaseSearch implementation as backstop:

interface Search { public function query(string $q, array $filters = []): Collection; }

Document shape matters more than you think. Scolta's value is filtering/sorting, so flatten aggressively: status, published_at (epoch int, not string), category_ids as an array, url precomputed. Store the URL in the doc — you do not want to re-hit the DB for 20 results.

Testing: use a committed fixture index, assert on returned IDs and counts, never on score floats (they drift between scolta-core versions).

Gotchas and Tips

The index may be per-process, not persistent. Verify early whether the WASM module holds its index in linear memory or writes to disk. If it's in-memory, every PHP-FPM worker starts empty and your "index" is a per-request illusion. If it's file-backed, you have a write-concurrency problem instead. Either answer changes the design, and the README probably doesn't tell you.

Concurrency will corrupt your index if you index in parallel. Multiple workers writing the same index file is last-write-wins at best, corrupt at worst. Serialize indexing: ShouldBeUnique on the job, or withoutOverlapping() on the command. On Laravel Horizon, force the indexing queue to a single process.

Octane/Swoole is a trap door. A singleton holding a WASM instance persists across requests — great for warm start, dangerous if the module keeps query state. Test for state bleed between requests; if present, resolve the client per-request or scope it with app()->scoped().

Pin everything, three ways. tag1/scolta-php version, scolta-core .wasm version, and the Extism extension version. With 0 dependents and a single maintainer, a patch bump can change the WASM ABI with no visible notice in semver. Vendor the .wasm into your repo or your Docker image build — never download it at runtime.

Errors are opaque. Extism surfaces generic "call failed" exceptions. Wrap every client call in a try/catch that logs the request payload and the exception, and add a boot-time health check (php artisan about-style) that instantiates the module and runs a trivial query. Fail the deploy, not the first user search.

Docker: build the extension in a separate stage against the same PHP version, then copy the .so — the compile pulls in a Rust toolchain that will balloon your final image otherwise. docker-php-ext-enable extism plus a .ini in conf.d.

Debugging the module: enable Extism's logging, and dump the module manifest's exported function list before writing adapter code. Reading the actual call signatures beats guessing from the source.

Extension points worth knowing: the platform-adapter contract (write your own if the Laravel one lags), and whatever document-mapper/DTO the package uses to shape docs before sending — that's where you add fields without forking.

The honest verdict for a Laravel team: this makes sense if you want search that runs with zero external infrastructure (no Meilisearch cluster, no Algolia bill) and you're willing to read source instead of docs. If you need a documented, battle-tested API today, use Laravel Scout with the database or Meilisearch driver, and revisit Scolta once it has a real release history. If you do adopt it, isolate it behind an interface so the exit is a one-file change.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
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