Skip to main content

Typeify Showcase

Real-world examples of type registries with Typeify: post types, product types, taxonomies, content types, media types, and validation patterns.

Understanding Typeify

Typeify gives you:

  • One registry per concern – PostType, ProductType, TaxonomyType, etc.
  • Fluent definitiondefine('key')->label(...)->description(...)->import()->export()
  • Container storage – Same definitions everywhere in the request
  • Composable traits – Label, description, import/export, hierarchical, list options, driver namespace

Post Type Registry (CMS)

Post Types for CMS

Blog, news, page with labels and descriptions

Overview

Define post types (blog, news, page) once with translatable labels and descriptions. Use the same definitions in admin dropdowns, API responses, and frontend filters.

Features
  • Labels and descriptions
  • Translation via trans()
  • Single source of truth
Use cases
  • Admin type selector
  • API type list
  • Validation against allowed types

Implementation

namespace App\Type;

use JobMetric\Typeify\BaseType;

class PostType extends BaseType
{
protected function typeName(): string
{
return 'post-type';
}
}

// In AppServiceProvider::boot() or a dedicated TypeServiceProvider
$postType = new PostType();
$postType->define('blog')
->label('Blog Post')
->description('Posts for the blog section');
$postType->define('news')
->label('News')
->description('News articles');
$postType->define('page')
->label('Page')
->description('Static pages');

// In controller or API
$postType = new PostType();
$types = $postType->getTypes();
$options = [];
foreach ($types as $key) {
$postType->type($key);
$options[] = ['value' => $key, 'label' => $postType->getLabel()];
}
return response()->json(['types' => $options]);

Product Type Registry (E-Commerce)

Product Types with Import/Export

Physical, digital, subscription with flags

Overview

Use traits to mark which product types support import/export or hierarchical structure. Check these flags in import/export UI and in product forms (e.g. show parent product only for hierarchical types).

Traits used
  • HasImportType
  • HasExportType
  • HasHierarchicalType
Use cases
  • Show/hide import button by type
  • Enable export only for allowed types
  • Parent product selector for hierarchical

Implementation

namespace App\Type;

use JobMetric\Typeify\BaseType;
use JobMetric\Typeify\Traits\HasImportType;
use JobMetric\Typeify\Traits\HasExportType;
use JobMetric\Typeify\Traits\HasHierarchicalType;

class ProductType extends BaseType
{
use HasImportType, HasExportType, HasHierarchicalType;

protected function typeName(): string
{
return 'product-type';
}
}

// Bootstrap
$productType = new ProductType();
$productType->define('physical')
->label('Physical Product')
->description('Shippable goods')
->import()
->export();
$productType->define('digital')
->label('Digital Product')
->description('Downloadable content')
->export();
$productType->define('subscription')
->label('Subscription')
->description('Recurring subscription')
->hierarchical();

// In import controller
if ($productType->type($requestedType)->hasImport()) {
return $this->showImportForm();
}
abort(403, 'Import not allowed for this type');

Taxonomy Type (Categories & Tags)

Taxonomy Types (Category vs Tag)

Hierarchical categories and flat tags

Overview

One registry for taxonomy types: category (hierarchical) and tag (flat). Use hasHierarchical() to decide whether to show a parent selector or a flat list in the admin.

Implementation

namespace App\Type;

use JobMetric\Typeify\BaseType;
use JobMetric\Typeify\Traits\HasHierarchicalType;

class TaxonomyType extends BaseType
{
use HasHierarchicalType;

protected function typeName(): string
{
return 'taxonomy-type';
}
}

$taxonomyType = new TaxonomyType();
$taxonomyType->define('category')
->label('Category')
->description('Hierarchical categories')
->hierarchical();
$taxonomyType->define('tag')
->label('Tag')
->description('Flat tags');

// In taxonomy form
$taxonomyType->type($request->input('taxonomy_type'));
if ($taxonomyType->hasHierarchical()) {
return view('taxonomy.form-with-parent', ['parentOptions' => Taxonomy::where('type', $request->input('taxonomy_type'))->get()]);
}
return view('taxonomy.form-flat');

List Options (Admin Table Behavior)

List Options Per Type

Show description, remove filter, change status in list

Overview

Use HasListOptionType to control list view per type: show description column, hide filter bar, or allow status change. Useful when different content types need different table columns and actions.

Implementation

use JobMetric\Typeify\Traits\HasListOptionType;

class PostType extends BaseType
{
use HasListOptionType;
// ...
}

$postType->define('blog')
->label('Blog')
->showDescriptionInList()
->changeStatusInList();
$postType->define('page')
->label('Page')
->removeFilterInList();

// In list view controller
$postType->type($currentType);
$showDescriptionColumn = $postType->hasShowDescriptionInList();
$showFilterBar = !$postType->hasRemoveFilterInList();
$canChangeStatus = $postType->hasChangeStatusInList();
return view('posts.index', compact('showDescriptionColumn', 'showFilterBar', 'canChangeStatus'));

Validation in Controller

Validate Type From Request

Use hasType() or ensureTypeExists() before type()

Implementation

// Option 1: Manual check
public function store(Request $request, PostType $postType)
{
$type = $request->input('type');
if (!$postType->hasType($type)) {
return response()->json(['error' => 'Invalid type'], 422);
}
$postType->type($type);
$label = $postType->getLabel();
// ...
}

// Option 2: Exception-based (ensureTypeExists)
public function show(string $type, PostType $postType)
{
$postType->ensureTypeExists($type);
$postType->type($type);
return view('posts.by-type', ['typeLabel' => $postType->getLabel(), 'typeKey' => $type]);
}

// Option 3: Form request validation
class StorePostRequest extends FormRequest
{
public function rules()
{
$postType = new PostType();
return [
'type' => ['required', 'string', Rule::in($postType->getTypes())],
];
}
}

API Response with All Types

API: List Types with Labels

Expose type options for frontend or mobile apps

Implementation

// API Controller
class PostTypeController extends Controller
{
public function index(PostType $postType)
{
$items = [];
foreach ($postType->getTypes() as $key) {
$postType->type($key);
$items[] = [
'key' => $key,
'label' => $postType->getLabel(),
'description' => $postType->getDescription(),
];
}
return response()->json(['data' => $items]);
}
}

// Example response
// {"data":[{"key":"blog","label":"Blog Post","description":"Posts for the blog section"},{"key":"news","label":"News","description":"News articles"}]}

Service Provider Registration

Register All Types in One Place

Bootstrap post, product, taxonomy types in AppServiceProvider or TypeServiceProvider

Implementation

namespace App\Providers;

use App\Type\PostType;
use App\Type\ProductType;
use App\Type\TaxonomyType;
use Illuminate\Support\ServiceProvider;

class TypeServiceProvider extends ServiceProvider
{
public function register() {}

public function boot()
{
$this->registerPostTypes();
$this->registerProductTypes();
$this->registerTaxonomyTypes();
}

private function registerPostTypes(): void
{
$postType = new PostType();
$postType->define('blog')->label('Blog Post')->description('Posts for the blog section');
$postType->define('news')->label('News')->description('News articles');
$postType->define('page')->label('Page')->description('Static pages');
}

private function registerProductTypes(): void
{
$productType = new ProductType();
$productType->define('physical')->label('Physical')->import()->export();
$productType->define('digital')->label('Digital')->export();
$productType->define('subscription')->label('Subscription')->hierarchical();
}

private function registerTaxonomyTypes(): void
{
$taxonomyType = new TaxonomyType();
$taxonomyType->define('category')->label('Category')->hierarchical();
$taxonomyType->define('tag')->label('Tag');
}
}

Multiple Registries in One App

PostType + ProductType + TaxonomyType

Each registry has its own typeName(); they do not conflict

Implementation

// Each class uses a different typeName() so container keys are:
// post-type, product-type, taxonomy-type
$postType = new PostType();
$productType = new ProductType();
$taxonomyType = new TaxonomyType();

$postType->define('blog')->label('Blog');
$productType->define('physical')->label('Physical Product');
$taxonomyType->define('category')->label('Category')->hierarchical();

// Later, in a controller that handles both posts and products
$postType->type('blog');
$productType->type('physical');
$postLabel = $postType->getLabel();
$productLabel = $productType->getLabel();

Translation Keys for Labels

Use trans() Keys for i18n

getLabel() and getDescription() pass values through trans()

Implementation

// Define with translation keys
$postType->define('blog')
->label('posts.types.blog')
->description('posts.types.blog_description');

// lang/en/posts.php: 'types' => ['blog' => 'Blog Post', 'blog_description' => 'Posts for the blog section']
// lang/fa/posts.php: 'types' => ['blog' => 'پست وبلاگ', 'blog_description' => 'پست‌های بخش وبلاگ']

// getLabel() and getDescription() call trans() internally
$postType->type('blog');
app()->setLocale('fa');
$postType->getLabel();

Driver Namespace (Custom Drivers Per Type)

Media Type with Driver Namespace

Attach driver classes per type via HasDriverNamespaceType

Overview

For media or content that is handled by different driver classes per type (e.g. image, video, document), use HasDriverNamespaceType. You need to implement namespaceDriver() and optionally register custom driver classes per type.

Implementation

namespace App\Type;

use JobMetric\Typeify\BaseType;
use JobMetric\Typeify\Traits\HasDriverNamespaceType;

class MediaType extends BaseType
{
use HasDriverNamespaceType;

protected function typeName(): string
{
return 'media-type';
}

protected function namespaceDriver(): string
{
return 'Media';
}
}

$mediaType = new MediaType();
$mediaType->define('image')->driverNamespace([
\App\Media\ImageDriver::class => ['deletable' => true],
]);
$mediaType->define('video')->driverNamespace([
\App\Media\VideoDriver::class => ['deletable' => true, 'streamable' => true],
]);

$mediaType->type('video');
$drivers = $mediaType->getDriverNamespace();

Next Steps

  • BaseType – Full API and lifecycle
  • Traits – Label, description, import/export, list options, driver namespace
  • Installation – Install and first registry