ronappleton / organisational-unit
Pattern for managing organisational structures, from management structures, to company structures to any kind or organisational structure.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/ronappleton/organisational-unit
Requires
- php: >8.3
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/framework: ^11.25
- laravel/pint: ^1.18
- orchestra/testbench: ^9.5
- phpstan/phpstan: ^1.12
- phpunit/phpunit: ^11.3
This package is auto-updated.
Last update: 2025-12-09 08:40:37 UTC
README
📦 Organisational Unit Backbone
A lightweight, high-performance hierarchical structure & metadata system for Laravel applications.
This package provides a generic organisational unit backbone, designed as a universal building block for representing anything hierarchical:
- Schools → Year Groups → Classes
- Warehouses → Zones → Aisles → Racks → Bins
- Corporate Org Charts
- Facilities → Buildings → Floors → Rooms
- Taxonomies
- Asset Locations
- Multi-tenant logical structures
It provides:
- A lean organisational_units table (bigint, indexed, fast)
- Optional morph link to any model
- Fully managed parent/child tree structure
- Typed, polymorphic metadata
- Clean, extensible query builder helpers
- PHP-side tree utilities (descendants, ancestors, etc.)
- Automatic cascading of soft deletes / restores
🚀 Why start your project with this backbone?
Most systems accidentally recreate these same problems:
- “We need a structure of buildings → rooms → sensors.”
- “We need departments → teams → roles.”
- “We need product categories.”
- “We need a place hierarchy.”
- “We need dynamic classifications.”
- “We need custom per-node metadata.”
Every time, developers start from scratch.
By beginning with this backbone you get:
- Universal applicability
- Strong consistency
- Flexibility without performance loss
- Extensibility without schema rewrites
- Scalable metadata system
- Tree tools baked in
This becomes a foundation your entire ecosystem can depend on.
📥 Installation
composer require appleton/organisational-unit php artisan migrate
🗂️ Database Structure
The organisational_units table contains:
- id (bigint PK)
- parent_id (nullable FK)
- entity_type / entity_id (nullable morph)
- name
- code
- type
- tenant_id
- soft deletes + timestamps
Indexes exist on:
- parent_id
- entity_type, entity_id
- type
- code
- tenant_id
- tenant_id, type
🌳 Model Usage
$unit = OrganisationalUnit::create([ 'name' => 'Warehouse A', 'type' => 'warehouse', ]);
Parent / Children
$unit->parent; $unit->children;
Linking Entities
$unit->entity()->associate($model)->save();
Moving Units
$unit->moveToParent($newParentId);
Building Trees
$tree = OrganisationalUnit::buildTree();
Descendants / Ancestors
$unit->descendants(); $unit->getParentChain();
🧠 Query Builder
OrganisationalUnit::query() ->root() ->tenant(5) ->ofType('bin') ->entityType(User::class) ->get();
🏷️ Metadata
Set metadata:
$unit->setMeta('capacity', 300); $unit->setMeta('is_active', true); $unit->setMeta('config', ['threshold' => 10]);
Get metadata:
$unit->getMeta('capacity');
Remove:
$unit->forgetMeta('capacity');
🏫 Example: School
$school = OU::create(['name' => 'Greenfields Primary', 'type' => 'school']); $ks1 = OU::create(['name' => 'Key Stage 1', 'parent_id' => $school->id]); $y1 = OU::create(['name' => 'Year 1', 'parent_id' => $ks1->id]); $classA = OU::create([ 'name' => '1A', 'parent_id' => $y1->id, ]); $classA->setMeta('max_size', 30);
📦 Example: WMS
$wh = OU::create(['name' => 'Warehouse 1', 'type' => 'warehouse']); $zone = OU::create(['name' => 'Zone A', 'parent_id' => $wh->id]); $aisle = OU::create(['name' => 'Aisle 12', 'parent_id' => $zone->id]); $bin = OU::create(['name' => 'Bin 7', 'parent_id' => $aisle->id]); $bin->setMeta('max_weight', 250);
🏛️ Example: Facilities
$campus = OU::create(['name' => 'North Campus']); $building = OU::create(['name' => 'Block A', 'parent_id' => $campus->id]); $room101 = OU::create(['name' => 'Room 101', 'parent_id' => $building->id']); $room101->setMeta('capacity', 40);
⚙️ Extensibility
- Attach any model via
entity_type/entity_id - Add metadata dynamically
- Use
typeto build domain-specific trees - Use
tenant_idfor SaaS isolation
🔧 Performance Notes
- Bigint PKs allow fast joins and small indexes
- Narrow row design supports millions of units
- Typed metadata avoids unbounded JSON blobs
- Recursion is PHP-based for reliability
🧱 Philosophy Summary
- A universal tree system
- A universal metadata system
- A universal linking system
- Extendable into any domain
- Highly scalable and predictable
Example Domains
This solves a problem once so your whole ecosystem doesn't need to reinvent structure management repeatedly.