BaseType
The BaseType abstract class is the foundation for type registries. Subclasses register named types (keys) and attach parameters (label, description, flags) to each. State is stored in the Laravel container under the key returned by typeName().
Namespace
JobMetric\Typeify\BaseType
Overview
- define(string $type) – Register a new type and set it as current; chain trait methods to set parameters.
- type(string $type) – Switch current type to an existing one; throws if not registered.
- get() – Get all parameters for the current type.
- getTypes() – List all registered type keys.
- hasType(string $type) – Check if a type is registered.
- ensureTypeExists(string $type) – Throw if the type is not registered (use for validation).
Abstract Method
typeName(): string
Return a unique container key for this registry (e.g. 'post-type', 'product-type'). All type data is stored in the Laravel container under this key. Must be unique across all your type registries so that different subclasses do not overwrite each other.
protected function typeName(): string
{
return 'post-type';
}
Use short, kebab-case names that describe the registry: post-type, product-type, taxonomy-type, media-type.
Lifecycle
- Bootstrap – You create an instance of your type class (e.g.
new PostType()) and calldefine('key')for each type, chaining trait methods. This fills the Laravel container undertypeName(). - Usage – Anywhere in the same request you can instantiate the same class again (or resolve from container), call
type('key')to select a type, then useget(),getLabel(),getDescription(), or trait getters. - Persistence – Data lives only in the container for the current request. Typically you register types once per request (e.g. in a service provider or when building the registry) and then read them in controllers, APIs, or views.
Defining Types
After define($type) the current type is set to $type, so you can chain methods from traits (label(), description(), import(), export(), etc.). Each call to define() adds or overwrites one type in the container.
$postType = new PostType();
$postType->define('blog')
->label('Blog Post')
->description('Posts for the blog section');
$postType->define('news')
->label('News')
->description('News articles');
Defining the same key again overwrites that type's parameters. The order of definition does not matter for reading; use getTypes() to get the list of keys.
Selecting a Type
Use type($type) to switch the current type. All subsequent getters (e.g. get(), getLabel()) refer to this type until you call type() again or define().
$postType->type('blog');
$params = $postType->get();
$label = $postType->getLabel();
Throws: TypeifyTypeNotFoundException if the type is not registered.
Reading Parameters
- get() – Returns the full array of parameters for the current type. Throws if no type is selected or the current type is not in the container.
- getTypes() – Returns the list of all registered type keys. Does not require a type to be selected.
- hasType(string $type) – Returns whether a type key is registered. Does not require a type to be selected.
- ensureTypeExists(string $type) – Throws if the type is not registered. Use this to validate user input or request parameters before calling
type($type).
$postType->type('blog');
$all = $postType->get();
$postType->ensureTypeExists('blog');
$keys = $postType->getTypes();
$exists = $postType->hasType('blog');
Throws: TypeifyTypeNotMatchException when no type is selected or when using get() and the current type is not registered.
Container Storage
Type data is stored as a singleton in the Laravel container:
- Key: The value returned by
typeName()(e.g.'post-type'). - Value: An array of the form
[ 'typeKey' => [ 'param' => value, ... ], ... ].
The same data is shared for the whole request. If you register types in a service provider or at the start of a request, controllers, APIs, and CLI commands will see the same definitions. No need to pass the registry instance around unless you want to encapsulate access.
Internal Methods (Protected)
Subclasses and traits use these; you typically do not call them from application code.
- setInContainer(array $params) – Replaces the entire type map in the container with
$params. Used internally afterdefine()or when a trait sets a parameter. - getInContainer() – Returns the full type map from the container. If the key is not bound, it binds an empty array and returns it.
- setTypeParam(string $key, mixed $params) – Sets one parameter for the current type. Throws if no type is selected. Used by traits (e.g.
label(),import()). - getTypeParam(string $key, mixed $default) – Gets one parameter for the current type, or
$defaultif missing. Used by traits (e.g.getLabel(),hasImport()).
The TraitBooter is used after each define() so that traits can run custom logic when a type is defined (e.g. set defaults).
Validation and Safe Usage
When the type key comes from user input (e.g. request parameter, API query), validate it before use:
$requestedType = $request->input('type');
$postType = new PostType();
if (!$postType->hasType($requestedType)) {
abort(404, 'Invalid type');
}
$postType->type($requestedType);
Or use exception-based validation:
$postType->ensureTypeExists($requestedType);
$postType->type($requestedType);
This avoids TypeifyTypeNotFoundException in production and gives you control over the HTTP response.
Multiple Registries
You can have several type registries in the same application, each with its own typeName(). For example: PostType (post-type), ProductType (product-type), TaxonomyType (taxonomy-type). They do not interfere because each uses a different container key.
$postType = new PostType();
$productType = new ProductType();
$postType->define('blog')->label('Blog');
$productType->define('physical')->label('Physical Product');
$postType->type('blog');
$productType->type('physical');
Example Registry
namespace App\Type;
use JobMetric\Typeify\BaseType;
class PostType extends BaseType
{
protected function typeName(): string
{
return 'post-type';
}
}
Add traits as needed:
use JobMetric\Typeify\Traits\HasImportType;
use JobMetric\Typeify\Traits\HasExportType;
use JobMetric\Typeify\Traits\HasHierarchicalType;
class PostType extends BaseType
{
use HasImportType, HasExportType, HasHierarchicalType;
protected function typeName(): string
{
return 'post-type';
}
}
Best Practices
- Register types once – Prefer registering all types in a service provider or a dedicated bootstrap class so the container is filled at the start of the request.
- Use translation keys – For
label()anddescription()pass translation keys (e.g.posts.types.blog) sogetLabel()andgetDescription()work withtrans()and localization. - Validate input – Use
hasType()orensureTypeExists()beforetype()when the type key comes from the request. - One registry per concern – Keep one subclass per logical registry (post types, product types, etc.) and use a clear, unique
typeName().
Common Mistakes
- Calling get() or getLabel() without selecting a type – Always call
define('key')ortype('key')first; otherwise TypeifyTypeNotMatchException is thrown. - Using the same typeName() in two classes – Two subclasses with the same
typeName()will share the same container key and overwrite each other's data. - Assuming types persist across requests – The container is per-request. Re-register types every request (e.g. in a provider) or in a bootstrap script that runs when building the registry.
Related Documentation
- HasLabelType
- HasDescriptionType
- TypeifyTypeNotFoundException
- TypeifyTypeNotMatchException
- Showcase – Real-world examples