Installation:
composer require pannella/laravel-cti:^3.5.1
Publish the config (optional):
php artisan vendor:publish --provider="Pannella\LaravelCTI\CTIServiceProvider"
Define CTI Structure:
assessments) with shared columns.assessment_technical, assessment_creative) with type-specific columns.type column to the parent table (e.g., assessment_type).subtype_id) to the parent table.First Use Case:
Define a parent model (Assessment) and subtype models (TechnicalAssessment, CreativeAssessment):
use Pannella\LaravelCTI\HasCTI;
class Assessment extends Model
{
use HasCTI;
protected $ctiTypeColumn = 'assessment_type';
protected $ctiSubtypeColumn = 'subtype_id';
protected $ctiSubtypeTable = 'assessment_subtypes';
protected $ctiSubtypeModels = [
'technical' => TechnicalAssessment::class,
'creative' => CreativeAssessment::class,
];
}
Create a subtype:
$assessment = Assessment::create([
'name' => 'Performance Review',
'assessment_type' => 'technical',
'technical_specific_field' => 'value',
]);
Model Instantiation: Fetch a parent record with its subtype automatically:
$assessment = Assessment::find(1); // Returns TechnicalAssessment or CreativeAssessment
Querying Subtypes: Query parent records and filter by subtype:
$technicalAssessments = Assessment::whereType('technical')->get();
Saving/Updating: Save subtype-specific data seamlessly:
$assessment->technical_specific_field = 'updated_value';
$assessment->save(); // Automatically persists to subtype table
Relationships: Define relationships on parent or subtype models. Note: Inherited relationships now work correctly (fixed in 3.5.1):
class Assessment extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
// Subtype-specific relationships also work as expected
class TechnicalAssessment extends Assessment
{
public function technicalReviews()
{
return $this->hasMany(TechnicalReview::class);
}
}
Batch Loading: Avoid N+1 queries with eager loading:
$assessments = Assessment::with('subtype')->get();
cti() method in migrations for subtype tables:
Schema::create('assessment_technical', function (Blueprint $table) {
$table->cti('assessments', 'technical');
$table->string('technical_specific_field');
});
created, updated).App\Http\Resources\AssessmentResource to handle subtype serialization:
public function toArray($request)
{
return [
'id' => $this->id,
'type' => $this->ctiType(),
'data' => $this->whenLoaded('subtype', function () {
return $this->subtype->toArray();
}),
];
}
Foreign Key Constraints:
subtype_id column in the parent table has a foreign key constraint pointing to the subtype table’s primary key.$table->unsignedBigInteger('subtype_id')->nullable();
$table->foreign('subtype_id')->references('id')->on('assessment_technical');
Type Resolution:
ctiType() method must return a value that matches the keys in $ctiSubtypeModels. Use a lookup table if types are dynamic:
protected $ctiSubtypeModels = [
AssessmentSubtype::where('name', 'technical')->value('id') => TechnicalAssessment::class,
AssessmentSubtype::where('name', 'creative')->value('id') => CreativeAssessment::class,
];
Mass Assignment:
$fillable for both the parent and subtype models:
class TechnicalAssessment extends Assessment
{
protected $fillable = ['technical_specific_field'];
}
Circular Dependencies:
Inherited Relationships:
$ctiTypeColumn matches the database column.$ctiSubtypeModels with the correct key.subtype_id is not null when a subtype is expected.DB::enableQueryLog();
$assessments = Assessment::all();
dd(DB::getQueryLog());
Custom Type Resolution:
Override the resolveSubtype() method in the parent model for dynamic logic:
protected function resolveSubtype()
{
if ($this->isPremium()) {
return 'premium';
}
return parent::resolveSubtype();
}
Subtype-Specific Logic: Use trait composition to add subtype-specific behavior:
trait HasTechnicalFields
{
public function getTechnicalScore()
{
return $this->technical_specific_field * 2;
}
}
Custom Lookup Tables:
Extend the Pannella\LaravelCTI\SubtypeLookup class to handle dynamic subtype definitions from a separate table.
Soft Deletes:
Ensure both parent and subtype tables use the same deleted_at column if using soft deletes:
use SoftDeletes;
class Assessment extends Model
{
use HasCTI, SoftDeletes;
}
How can I help you explore Laravel packages today?