forci/static-data-bundle
Forci StaticDataBundle helps Symfony applications manage static and reference data through reusable entities, configurable data sets, and simple CRUD workflows. Keep lookup values centralized and maintainable without duplicating code.
Reality check before you install: forci/static-data-bundle is a Symfony bundle, not a Laravel package. There is no ServiceProvider, no facade, no config/static-data.php, and no publishable assets. If you are on Laravel, "install and go" does not exist. Your first task is not integration — it is inspection.
Minimum steps before writing a single line of integration code:
composer require into your app yet):
git clone https://github.com/forci/static-data-bundle /tmp/static-data
cd /tmp/static-data
LICENSE — the composer metadata is NOASSERTION. This is a blocking gate. Do not copy code or data into your app until you know what rights you actually have.composer.json — check require, autoload, license, and the Symfony version constraints.src/*Bundle.php and src/DependencyInjection/*Extension.php — this tells you what the bundle actually wires up.src/DependencyInjection/Configuration.php — the config tree, which is the real schema of the package.src/ — look for the payload itself (arrays, JSON, YAML, or Doctrine entity fixtures). That payload is likely the only part worth preserving for a Laravel app.If the LICENSE does not confirm a permissive commercial grant, stop here. Everything below assumes you are extracting data, not depending on the bundle.
Because Laravel has no bundle registration, the sustainable workflow is to treat the repo as a source of data artifacts, not a Composer dependency:
# One-time extraction, no runtime dependency
cp -r /tmp/static-data/src/Resources/data resources/data/static
Then delete the Symfony wiring entirely (*Bundle.php, DependencyInjection/, Resources/config/services.*). What remains is an owned asset with a known schema and no transitive Composer surface. This is the pattern to default to.
Once the data is extracted into resources/data/static/*.php (or .json), expose it through a small accessor so consumers never read files directly:
// app/Support/StaticData/StaticDataRepository.php
namespace App\Support\StaticData;
class StaticDataRepository
{
private array $cache = [];
public function __construct(private array $paths) {}
public function get(string $set, string $key, mixed $default = null): mixed
{
$data = $this->load($set);
return $data[$key] ?? $default;
}
public function all(string $set): array
{
return $this->load($set);
}
private function load(string $set): array
{
return $this->cache[$set] ??= require $this->paths[$set];
}
}
Register it as a singleton so the array is loaded once per request/worker and is opcache-friendly:
// app/Providers/StaticDataServiceProvider.php
public function register(): void
{
$this->app->singleton(StaticDataRepository::class, fn () => new StaticDataRepository([
'statuses' => resource_path('data/static/statuses.php'),
'currencies' => resource_path('data/static/currencies.php'),
]));
}
If the data is meant to be joined against (foreign keys, admin CRUD, filters), don't serve it from PHP arrays — use it to drive a seeder and keep a single source of truth in the DB:
// database/seeders/StatusSeeder.php
public function run(): void
{
$statuses = require resource_path('data/static/statuses.php');
DB::table('statuses')->upsert(
collect($statuses)->map(fn ($s) => [
'code' => $s['code'],
'label' => $s['label'],
'updated_at' => now(),
'created_at' => now(),
])->all(),
['code'],
['label', 'updated_at']
);
}
The most common day-to-day use is turning reference data into validation and UI options:
// app/Rules/StaticDataRule.php
public function passes($attribute, $value): bool
{
return array_key_exists($value, app(StaticDataRepository::class)->all('statuses'));
}
Or a small enum generated from the dataset if you're on PHP 8.1+:
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
public static function labels(): array
{
return array_map(fn ($c) => $c->value, self::cases());
}
}
Enums are often the cleanest Laravel-native replacement for what this bundle would provide.
If the host project is Symfony and you are just bridging in a Laravel-adjacent context, the bundle's own workflow applies: register in config/bundles.php, map its config tree in config/packages/, and use whatever console commands (bin/console forci:static-data:*) it exposes to sync/import. But confirm the LICENSE and the PHP/Symfony version floor first — the metadata does not.
NOASSERTION is a hard blocker, not a soft warning. Composer does not know the license, which means there is no declared grant of rights. Do not vendor code or data into a commercial codebase until a human has read the repo's LICENSE and confirmed it permits redistribution of both code and the embedded dataset. Data compilations can carry their own upstream terms independent of the code license.
Zero stars, zero dependents means zero support corpus. There is no Stack Overflow trail, no issue history to learn failure modes from, and no downstream users proving the data is correct. Expect every bug to be yours, and treat correctness of the data as unverified unless you cross-check against an independent source.
The "bundle" prefix is a hard tell. If you try to composer require it into Laravel, you may pull Symfony DI constraints that conflict with Laravel's shipped Symfony components. Always composer require --dev first and run composer why-not forci/static-data-bundle before touching production constraints.
Silent staleness is the most likely production failure. Static reference data decays (currency codes, country lists, regulatory statuses). Record a provenance/version stamp alongside the extracted data and schedule a periodic freshness check — otherwise wrong values fail silently in the UI.
Enforce strict accessors. A missing key returning null produces blank form fields instead of loud failures. Use throw on unknown set/key, or validate the payload against a schema at boot:
private function load(string $set): array
{
$data = require $this->paths[$set];
if (! is_array($data)) {
throw new \RuntimeException("Static dataset [{$set}] is malformed");
}
return $this->cache[$set] = $data;
}
Don't call require/file_get_contents per lookup. The bundle's original accessors may resolve via container lookups; a naive port will do the same and burn I/O per request. Cache the compiled array on the singleton (Pattern 2) so it survives across calls and benefits from opcache.
Pin the exact commit if you keep any code. There is no visible SemVer discipline and no release history in the metadata. A "patch" version could reshape the payload schema. Vending a specific commit hash — or better, deleting the dependency post-extraction — removes that risk.
Watch for Doctrine/entity coupling. If the payload is expressed as Doctrine entities, you cannot lift it into a Laravel app without the ORM. In that case, read the rows and re-export as plain arrays before extraction; do not pull Doctrine in as a transitively required package.
Extension points you would lose by extracting. The bundle's DI extension, config tree, and tagged services are the only "extensibility" it offers. Extract them and you keep nothing configurable — which is fine, because for a handful of reference lists a single config/static-data.php array mapping set name → path is easier to reason about and easier to test than a Symfony compiler pass.
Debugging tip: when in doubt about what the package actually ships, composer show --all forci/static-data-bundle from a scratch dir and inspect the file tree. If src/Resources/config/ is the only substantive directory, you are looking at a very thin wrapper and the "value" is a data dump — which reinforces the mine-don't-depend recommendation.
How can I help you explore Laravel packages today?