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

Hstore Bundle Laravel Package

intaro/hstore-bundle

Symfony bundle that integrates PostgreSQL hstore with Doctrine, providing a custom DBAL type, schema support, and convenient entity mapping so you can store and query key-value data directly from your PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

intaro/hstore-bundle is a Symfony 2 bundle that registers a Doctrine DBAL type named hstore, letting a PostgreSQL hstore column hydrate into a PHP array on an entity. It is marked deprecated, last tagged in November 2014, has 5 stars and no dependents — so treat it as a reference implementation for how to map hstore in an ORM, not as something you install into a modern app. The transferable idea (a string→string key/value column on a model) is what you actually want; the wiring is Symfony-specific and will not load in Laravel.

Minimal steps (the package's own path, for reference):

  1. composer require intaro/hstore-bundle (pin to the 2014 tag; expect conflicts with any DBAL ≥ 2.5 era constraint).
  2. Enable the bundle in AppKernel.php: new Intaro\HStoreBundle\IntaroHStoreBundle().
  3. Ensure the extension exists in the database: CREATE EXTENSION IF NOT EXISTS hstore;.
  4. Map the column on the entity with @ORM\Column(type="hstore") and declare the property as array.
  5. The bundle's boot registers the type and calls registerDoctrineTypeMapping('hstore', 'hstore') so schema tooling and hydration both understand the column.

Minimal steps (Laravel equivalent):

// migration
DB::statement('CREATE EXTENSION IF NOT EXISTS hstore');
DB::statement('ALTER TABLE products ADD COLUMN attributes hstore');
// app/Casts/Hstore.php  — implement CastsAttributes, see below
protected $casts = ['attributes' => \App\Casts\Hstore::class];

First use case: a per-row bag of arbitrary attributes — feature flags, translated labels, scrape metadata — where you want WHERE attributes @> 'color=>"red"' filtering without an EAV table or a jsonb column. If you control the schema and are on PostgreSQL 9.4+, reach for jsonb and a plain array cast first; hstore is a string-only map and earns its place only when you need the @> / ? / -> operators on flat string pairs or are joining against an existing hstore schema.

Implementation Patterns

Entity mapping (Doctrine — the package's pattern). Keep the property initialized so hydration of NULL and empty maps stay distinguishable:

/** @ORM\Column(type="hstore", nullable=true) */
protected $attributes = [];

Filtering with native SQL. Doctrine DQL has no hstore operators, so drop to the connection and bind a literal — quoting is on you:

$rows = $em->getConnection()->fetchAllAssociative(
    'SELECT * FROM product WHERE attributes @> :attrs',
    ['attrs' => 'color=>"dark red"']   // quote values containing spaces
);

Use ? for key existence, -> for value extraction, and @> for containment.

Index it or it will table-scan. GIN is the only index type that supports @>, ?, and ->:

CREATE INDEX products_attributes_gin ON products USING gin (attributes);

Laravel custom cast (the pattern you'll actually ship). A plain 'array' cast does not work — Eloquent runs json_decode, and the hstore text output is "a"=>"1", "b"=>"2", not JSON. Write a cast that parses and serializes the literal:

class Hstore implements CastsAttributes
{
    public function get($model, $key, $value, $attributes): array
    {
        if ($value === null) return [];
        preg_match_all('/"((?:[^"\\\\]|\\\\.)*)"=>"((?:[^"\\\\]|\\\\.)*)"/', $value, $m, PREG_SET_ORDER);
        $out = [];
        foreach ($m as [, $k, $v]) {
            $out[stripcslashes($k)] = stripcslashes($v);
        }
        return $out;
    }

    public function set($model, $key, $value, $attributes): ?string
    {
        if ($value === null) return null;
        $parts = [];
        foreach ($value as $k => $v) {
            $parts[] = '"'.addcslashes($k, '"\\').'"=>"'.addcslashes((string) $v, '"\\').'"';
        }
        return implode(', ', $parts);   // no braces: that's array syntax, not hstore
    }
}

Testing. In RefreshDatabase runs, create the extension in a dedicated first migration or a setUpBeforeClass statement; tests on a fresh database fail with type "hstore" does not exist otherwise. The postgres Docker image ships hstore in contrib, so no extra build is needed.

Serialization / API layer. Both patterns expose a plain associative array, so $model->attributes round-trips through json_encode unchanged. Keep the cast as the single boundary and never leak the raw literal string.

Gotchas and Tips

  • Deprecated and effectively unmaintained. Last release 2014-11-10, zero dependents, and the type relies on Type::getName(), which Doctrine DBAL 3 removed. Do not add it to a new build; copy the ~60-line type class if you truly need Doctrine support.
  • Symfony-only. The bundle registers via the service container and AppKernel; it cannot be autoloaded usefully from a Laravel service provider. The DBAL type itself is portable, the bundle is not.
  • Registration order matters. Unknown database type hstore requested means the platform mapping was registered after schema introspection/doctrine:schema:validate ran. In the Symfony path this is bundle boot; in Laravel, if you use doctrine/dbal for ->change() migrations, you'll hit the same error and must register the mapping first.
  • Text vs hstore on bind. If PostgreSQL complains column is of type hstore but expression is of type text, append ::hstore to the placeholder or set the parameter type explicitly.
  • NULL vs empty map. convertToPHPValue in the original returns [] for null in some paths and null in others. Decide up front (the cast above returns [] only when the column is NULL) and be consistent, or whereNotNull('attributes') filters will surprise you.
  • String-only. No ints, bools, or nesting — everything comes back as a string. Cast at the edge of your domain, not in the DB.
  • Escaping. Values containing " or \ must be backslash-escaped in both directions; a naive implode over k=>v will silently corrupt data. Always go through addcslashes/stripcslashes.
  • Prefer jsonb unless you need the operators. jsonb gives you GIN, nesting, real types, and a native Eloquent array cast, with better tooling and no deprecated dependency. Migrating later is easy with hstore_to_jsonb(attributes).
  • Extension points. The clean escape hatch on the Doctrine side is to subclass the type and override convertToDatabaseValue/convertToPHPValue to go through JSON; on the Laravel side, override get/set in the cast rather than reaching for raw DB::raw in every query.
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.
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
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent