PHPackages                             banulakwin/laravel-metadata - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. banulakwin/laravel-metadata

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

banulakwin/laravel-metadata
===========================

Config-driven application metadata as key/value rows per group, with typed defaults and sync from PHP config.

v1.0.1(3w ago)031MITPHPPHP ^8.2CI passing

Since May 17Pushed 3w agoCompare

[ Source](https://github.com/banulalakwindu/laravel-metadata)[ Packagist](https://packagist.org/packages/banulakwin/laravel-metadata)[ Docs](https://github.com/banulalakwindu/laravel-metadata)[ RSS](/packages/banulakwin-laravel-metadata/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (8)Versions (3)Used By (1)

Laravel Metadata (`banulakwin/laravel-metadata`)
================================================

[](#laravel-metadata-banulakwinlaravel-metadata)

[![Latest Version on Packagist](https://camo.githubusercontent.com/f10c0411da42c4d72f3b7eed9c3fb80d617c31f0649b2429a4e537e816c2c35c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f62616e756c616b77696e2f6c61726176656c2d6d657461646174612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/banulakwin/laravel-metadata)[![Tests](https://github.com/banulalakwindu/laravel-metadata/actions/workflows/tests.yml/badge.svg)](https://github.com/banulalakwindu/laravel-metadata/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/ab96392772a9d165bbeb0ed2ac329d4a81a73fda03c494d9f586bcf6f6b6770c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f62616e756c616b77696e2f6c61726176656c2d6d657461646174612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/banulakwin/laravel-metadata)[![License](https://camo.githubusercontent.com/942e017bf0672002dd32a857c95d66f28c5900ab541838c6c664442516309c8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Portable Laravel package: **config-driven** application metadata stored as **key/value rows per group**, with **typed field definitions** in PHP config and a **sync command** that inserts missing rows from defaults.

Use this for **site settings**, **feature flags**, **integration toggles**, and other **global key/value** data without new migrations when you add keys.

---

Requirements
------------

[](#requirements)

- PHP `^8.2`
- Laravel `illuminate/*` `^11.0|^12.0|^13.0` (see `composer.json` for split packages)

---

Installation
------------

[](#installation)

Registration is automatic via Composer `extra.laravel.providers`:

- `Banulakwin\Metadata\MetadataServiceProvider`

Optional facade alias (see **Resolving `MetadataService`**): `Meta` → `Banulakwin\Metadata\Facades\Meta`.

### Configure, migrate, sync

[](#configure-migrate-sync)

```
php artisan vendor:publish --tag=meta-config
php artisan vendor:publish --tag=metadata-config
php artisan migrate
php artisan meta:sync
```

TagCopies`meta-config``config/meta.php` — define groups and fields (types + defaults)`metadata-config``config/metadata.php` — migration registration`metadata-migrations`Package migration files → `database/migrations/` (optional; see **Database migrations** below)### Database migrations

[](#database-migrations)

By default, migrations are registered with **`loadMigrationsFrom()`** when **`config('metadata.register_migrations')`** is **`true`** (default). Run **`php artisan migrate`** — no publish step is required.

To **own** migrations in the app: publish with **`php artisan vendor:publish --tag=metadata-migrations`**, then set **`register_migrations` =&gt; false** in **`config/metadata.php`** (or **`METADATA_REGISTER_MIGRATIONS=false`** in `.env`) so Laravel does not load the same files twice.

---

Configuration overview
----------------------

[](#configuration-overview)

### `config/meta.php` (merged key: `meta`)

[](#configmetaphp-merged-key-meta)

Top-level keys are **group names** (e.g. `site`, `integrations`). Each group has a **`fields`** array: field key → definition with at least **`type`**, optional **`default`**.

Supported types for **`meta:sync`** (default encoding) and **`MetadataService::get()` / `getGroupDecoded()`** (decoding):

TypeStored in DBNotes`text`, `textarea`, `url`, `email`Plain stringDefault: empty string if omitted.`image`String path or nullSame idea as page-builder image path.`integer` / `int`Numeric string or nullDefault omitted → `null` in DB.`float` / `decimal` / `double`Numeric string or nullDefault omitted → `null` in DB.`boolean` / `bool`JSON boolean string (`true` / `false`)Uses `json_encode` for storage.`json`JSON string`default` is `json_encode`d.`repeater`JSON arraySame pattern as page-builder repeaters.`group`JSON objectNested `fields` with recursive defaults (like page-builder `group`).Optional **`default`** is used by **`php artisan meta:sync`** only when creating a missing row (**existing values are never overwritten**).

### `config/metadata.php` (merged key: `metadata`)

[](#configmetadataphp-merged-key-metadata)

Config keyPurpose`register_migrations`Load package migrations via `loadMigrationsFrom()` (default `true`). Set `false` after publishing migrations into the app. Env: `METADATA_REGISTER_MIGRATIONS`.`definitions`Optional inline group definitions (same shape as `meta`); merged **after** `config('meta')`.`definitions_path`Absolute path, or empty string to use **`app/Cms/Metadata`**. Each **`{group}.php`** returns `['fields' => [...]]`; basename is the group name. Env: `METADATA_DEFINITIONS_PATH`.`definitions_cache_ttl`Seconds to cache merged definitions (`0` = off). **`meta:sync`** clears this cache when TTL is greater than zero. Env: `METADATA_DEFINITIONS_CACHE_TTL`.`definitions_cache_key`Cache store key for merged definitions. Env: `METADATA_DEFINITIONS_CACHE_KEY`.### `MetadataRegistry` (file + config merge)

[](#metadataregistry-file--config-merge)

`Banulakwin\Metadata\Support\MetadataRegistry` mirrors the **page-builder** idea: **`all()`** returns merged groups (for **`meta:sync`** and for **`MetadataService`** field types). **`fieldsForGroup('site')`** returns the `fields` map for one group. **`forgetCache()`** is called at the start of **`meta:sync`**.

---

Rules (design constraints)
--------------------------

[](#rules-design-constraints)

- **Do not change the DB schema** to add metadata keys — add them in **`config/meta.php`**, optional **`config/metadata.definitions`**, and/or **`app/Cms/Metadata/{group}.php`**, then run **`meta:sync`** for new keys only.
- **Complex values** (`json`, `repeater`, `group`) are stored as **JSON** in **`metadata_entries.value`**.
- If a field has **no `type` in config**, **`get()`** returns the **raw string** from the database; **`getGroupDecoded()`** leaves unknown keys as raw strings.

---

Database
--------

[](#database)

Table: **`metadata_entries`**

ColumnNotes`group`Logical namespace (`site`, `integrations`, …)`key`Field name`value``longText`, nullable — scalar string or JSON for complex types`deleted_at`Nullable timestamp — **soft deletes** (`SoftDeletes` on `MetadataEntry`)Unique index: **`(group, key)`** (applies to soft-deleted rows too). **`meta:sync`** uses **`withTrashed()->firstOrCreate()`** so it does not insert a duplicate when a trashed row still exists; if the row was trashed, it is \*\*`restore()`\*\*d (defaults are not written over existing values).

---

Architecture
------------

[](#architecture)

### Model

[](#model)

`Banulakwin\Metadata\Models\MetadataEntry` — fillable: `group`, `key`, `value`; uses **`SoftDeletes`** (`deleted_at`).

### Metadata service (singleton)

[](#metadata-service-singleton)

`Banulakwin\Metadata\Services\MetadataService`:

MethodReturns`getGroup(string $group)``[key => raw string or null]` from the database`getRaw(string $group, string $key)`Single raw value or `null``get(string $group, string $key, mixed $default = null)`Value decoded using **registry** field `type` when defined; otherwise raw string`getGroupDecoded(string $group)`All keys in that group with decoding applied where config exists### Resolving `MetadataService` (prefer dependency injection)

[](#resolving-metadataservice-prefer-dependency-injection)

**Recommended — method injection:**

```
use Banulakwin\Metadata\Services\MetadataService;
use Inertia\Inertia;

public function index(MetadataService $metadata)
{
    return Inertia::render('Home', [
        'site' => $metadata->getGroupDecoded('site'),
    ]);
}
```

**Optional — facade:**

```
use Banulakwin\Metadata\Facades\Meta;
use Inertia\Inertia;

return Inertia::render('Home', [
    'site' => Meta::getGroupDecoded('site'),
]);
```

**Optional — global helper** (`meta_group('site')`) returns **raw** pairs only; use sparingly. Prefer **`MetadataService`** or **`Meta::getGroupDecoded()`** when you need typed values.

### Sync command

[](#sync-command)

`php artisan meta:sync` walks **`config('meta')`** and uses **`firstOrCreate`** on **`(group, key)`** with encoded defaults. **Does not overwrite** existing rows.

### Helper

[](#helper)

`meta_group(string $group): array` — convenience wrapper around **`MetadataService::getGroup()`** (raw strings). Defined in **`src/helpers.php`** (Composer **`autoload.files`**).

---

Laravel / Inertia usage
-----------------------

[](#laravel--inertia-usage)

### Controller (read, typed)

[](#controller-read-typed)

```
use Banulakwin\Metadata\Services\MetadataService;
use Inertia\Inertia;

public function index(MetadataService $metadata)
{
    return Inertia::render('Home', [
        'supportEmail' => $metadata->get('site', 'support_email'),
        'flags' => $metadata->getGroupDecoded('feature_flags'),
    ]);
}
```

### React (Inertia props)

[](#react-inertia-props)

Scalars arrive as strings/numbers/booleans/objects depending on type and **`get()`** / **`getGroupDecoded()`**. JSON-backed fields are already decoded on the server when you use **`get()`** or **`getGroupDecoded()`**.

---

Example config shape
--------------------

[](#example-config-shape)

After publishing **`meta-config`**, define groups in **`config/meta.php`**. Minimal example:

```
return [
    'site' => [
        'fields' => [
            'support_email' => ['type' => 'email', 'default' => 'hello@example.com'],
            'maintenance' => ['type' => 'boolean', 'default' => false],
            'max_items' => ['type' => 'integer', 'default' => 10],
            'notes' => ['type' => 'json', 'default' => ['lines' => []]],
        ],
    ],
    'integrations' => [
        'fields' => [
            'analytics_id' => ['type' => 'text', 'default' => ''],
        ],
    ],
];
```

---

Testing
-------

[](#testing)

```
composer test          # Run PHPUnit
composer pint          # Fix code style
composer phpstan       # Static analysis
composer quality       # Run all (pint + phpstan + test)
```

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for details.

---

Contributing
------------

[](#contributing)

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/your-feature`)
3. Run `composer quality` to ensure tests and style pass
4. Commit and push
5. Open a pull request

---

Package layout (reference)
--------------------------

[](#package-layout-reference)

```
config/
  metadata.php
  meta.php
database/migrations/
  *_create_metadata_entries_table.php
src/
  Console/SyncMetadata.php
  Facades/Meta.php
  Models/MetadataEntry.php
  Services/MetadataService.php
  MetadataServiceProvider.php
  helpers.php

```

---

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance95

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

23d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/64958389?v=4)[Banula Lakwindu](/maintainers/banulalakwindu)[@banulalakwindu](https://github.com/banulalakwindu)

---

Top Contributors

[![banulalakwindu](https://avatars.githubusercontent.com/u/64958389?v=4)](https://github.com/banulalakwindu "banulalakwindu (2 commits)")

---

Tags

laravelSettingsconfigmetadataKey valuefeature-flags

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/banulakwin-laravel-metadata/health.svg)

```
[![Health](https://phpackages.com/badges/banulakwin-laravel-metadata/health.svg)](https://phpackages.com/packages/banulakwin-laravel-metadata)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[spatie/laravel-health

Monitor the health of a Laravel application

88011.3M149](/packages/spatie-laravel-health)[illuminate/queue

The Illuminate Queue package.

20432.2M1.5k](/packages/illuminate-queue)[calebdw/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

15104.9k4](/packages/calebdw-larastan)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
