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 definition –
define('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