PHPackages                             calebporzio/parental - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. calebporzio/parental

Abandoned → [tightenco/parental](/?search=tightenco%2Fparental)Library[Database &amp; ORM](/categories/database)

calebporzio/parental
====================

A simple eloquent trait that allows relationships to be accessed through child models.

1.6.0(2mo ago)1.5k3.3M↓26.7%106[5 issues](https://github.com/tighten/parental/issues)[4 PRs](https://github.com/tighten/parental/pulls)4MITPHPPHP ^8.1CI passing

Since Sep 1Pushed 2mo ago33 watchersCompare

[ Source](https://github.com/tighten/parental)[ Packagist](https://packagist.org/packages/calebporzio/parental)[ GitHub Sponsors](https://github.com/calebporzio)[ RSS](/packages/calebporzio-parental/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (36)Used By (4)

[![Parental - Use single table inheritance in your Laravel App](/art/parental-banner.png)](/art/parental-banner.png)

Parental
========

[](#parental)

Parental is a Laravel package that brings STI (Single Table Inheritance) capabilities to Eloquent.

### What is single table inheritance (STI)?

[](#what-is-single-table-inheritance-sti)

It's a fancy name for a simple concept: Extending a model (usually to add specific behavior), but referencing the same table.

Installation
------------

[](#installation)

```
composer require tightenco/parental
```

Simple Usage
------------

[](#simple-usage)

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

// The "parent"
class User extends Model
{
    use HasChildren;
    //
}
```

```
namespace App\Models;

use Parental\HasParent;

// The "child"
class Admin extends User
{
    use HasParent;

    public function impersonate($user) {
        //...
    }
}
```

```
use App\Models\Admin;

// Returns "Admin" model, but reference "users" table:
$admin = Admin::first();

// Can now access behavior exclusive to "Admin"s
$admin->impersonate($user);
```

### What problem did we just solve?

[](#what-problem-did-we-just-solve)

Without Parental, calling `Admin::first()` would throw an error because Laravel would be looking for an `admins` table. Laravel generates expected table names, as well as foreign keys and pivot table names, using the model's class name. By adding the `HasParent` trait to the Admin model, Laravel will now reference the parent model's class name `users`.

Accessing Child Models from Parents
-----------------------------------

[](#accessing-child-models-from-parents)

```
// First, we need to create a `type` column on the `users` table
Schema::table('users', function ($table) {
    $table->string('type')->nullable();
});
```

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

// The "parent"
class User extends Model
{
    use HasChildren;

    protected $fillable = ['type'];
}
```

```
namespace App\Models;

use Parental\HasParent;

// A "child"
class Admin extends User
{
    use HasParent;
}
```

```
namespace App\Models;

use Parental\HasParent;

// Another "child"
class Guest extends User
{
    use HasParent;
}
```

```
use App\Models\Admin;
use App\Models\Guest;
use App\Models\User;

// Adds row to "users" table with "type" column set to: "App/Admin"
Admin::create(...);

// Adds row to "users" table with "type" column set to: "App/Guest"
Guest::create(...);

// Returns 2 model instances: Admin, and Guest
User::all();
```

### What problem did we just solve?

[](#what-problem-did-we-just-solve-1)

Before, if we ran: `User::first()` we would only get back `User` models. By adding the `HasChildren` trait and a `type` column to the `users` table, running `User::first()` will return an instance of the child model (`Admin` or `Guest` in this case).

Type Aliases
------------

[](#type-aliases)

If you don't want to store raw class names in the type column, you can override them using the `$childTypes` property.

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

class User extends Model
{
    use HasChildren;

    protected $fillable = ['type'];

    protected $childTypes = [
        'admin' => Admin::class,
        'guest' => Guest::class,
    ];
}
```

Now, running `Admin::create()` will set the `type` column in the `users` table to `admin` instead of `App\Models\Admin`.

This feature is useful if you are working with an existing type column, or if you want to decouple application details from your database.

Custom Type Column Name
-----------------------

[](#custom-type-column-name)

You can override the default type column by setting the `$childColumn` property on the parent model.

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;

class User extends Model
{
    use HasChildren;

    protected $fillable = ['parental_type'];

    protected $childColumn = 'parental_type';
}
```

Transforming Models Between Types
---------------------------------

[](#transforming-models-between-types)

You may transform a model from one type to another using the `become()` method.

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Parental\HasChildren;
use Parental\HasParent;

class Order extends Model
{
    use HasChildren;

    protected $fillable = ['type', 'total'];

    protected $childTypes = [
        'pending' => PendingOrder::class,
        'shipped' => ShippedOrder::class,
    ];
}

class PendingOrder extends Order
{
    use HasParent;
}

class ShippedOrder extends Order
{
    use HasParent;
}
```

```
use App\Models\Order;
use App\Models\ShippedOrder;

// Retrieve a pending order
$order = Order::first();

// Ship the order by transforming it
$order = $order->become(ShippedOrder::class);

// Updates the "type" column to "shipped" and returns a ShippedOrder instance
$order->save();
```

### What problem did we just solve?

[](#what-problem-did-we-just-solve-2)

The `become()` method will return a new instance of the specified child model with all the attributes of the original model. You must call `save()` on the returned model to persist the change to the database. This allows you to easily transition a model between different types while maintaining its data integrity, such as changing an order from pending to shipped, or a draft post to a published post.

This is also useful when you're using observers or callbacks, since the specific child model's behavior will be triggered after the transition.

A new model event is fired when a model is *becoming* another type, you may listen to it like so:

```
ShippedOrder::becoming(function ($shippedOrder) {
    // Do something before the model is saved...
});
```

Eager Loading Child Models
--------------------------

[](#eager-loading-child-models)

Warning

Eager-loading relationships is only supported on Laravel 11 and above.

To help with eager-loading relationships on child models, Parental provides a set of helpers that you may use in your queries. For the examples, we'll use the following models:

```
class Message extends Model
{
    use HasChildren;

    protected $fillable = ['type', 'content'];

    protected $childTypes = [
        'text' => TextMessage::class,
        'image' => ImageMessage::class,
    ];
}

class TextMessage extends Message
{
    use HasParent;

    public function mentions(): HasMany
    {
        return $this->hasMany(User::class);
    }
}

class ImageMessage extends Message
{
    use HasParent;

    public function attachments(): HasMany
    {
        return $this->hasMany(Attachment::class);
    }
}
```

### Eager Loading From Model Instance

[](#eager-loading-from-model-instance)

You may eager-load relationships of different models from a parent model instance using the `loadChildren` method:

```
$message = Message::first();

$message->loadChildren([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
]);
```

This will ensure that, if `$message` is an instance of `TextMessage`, the `mentions` relationship will be eager-loaded. If it's an instance of `ImageMessage`, the `attachments` relationship will be eager-loaded.

Alternatively, you may eager-load the relationship counts using the `loadChildrenCount` method:

```
$message = Message::first();

$message->loadChildrenCount([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
]);
```

This will ensure that, if `$message` is an instance of `TextMessage`, the `mentions_count` attribute will be filled. If it's an instance of `ImageMessage`, the `attachments_count` attribute will be filled.

### Eager Loading From Eloquent Collection

[](#eager-loading-from-eloquent-collection)

You may eager-load relationships from an Eloquent Collection using the `loadChildren` method:

```
$messages = Message::all();

$messages->loadChildren([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
]);
```

This will ensure that the appropriate relationships are eager-loaded for each child model in the collection based on its type.

Alternatively, you may eager-load the relationship counts using the `loadChildrenCount` method:

```
$messages = Message::all();

$messages->loadChildrenCount([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
]);
```

This will ensure the `mentions_count` attribute will be filled for instances of the `TextMessage` model, and the `attachments_count` attribute will be filled for instances of the `ImageMessage` model.

### Eager Loading From Query and Relationship

[](#eager-loading-from-query-and-relationship)

You may eager-load relationships directly from a query or relationship using the `childrenWith` method:

```
// From a query...
$messages = Message::query()->childrenWith([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
])->get();
```

You may also eager-load from a relationship. For instance, if we had a `Room` parent model that had messages:

```
class Room extends Model
{
    public function messages(): HasMany
    {
        return $this->hasMany(Message::class);
    }
}
```

Then, we could eager-load child relationships like so:

```
// From a relationship...
$room = Room::first();
$messages = $room->messages()->childrenWith([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
])->get();
```

This will ensure that the appropriate relationships are eager-loaded for each child model in the result set based on its type.

Alternatively, you may eager-load the relationship counts using the `childrenWithCount` method:

```
// From a query...
$messages = Message::query()->childrenWithCount([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
])->get();

// From a relationship...
$room = Room::first();
$messages = $room->messages()->childrenWithCount([
    TextMessage::class => ['mentions'],
    ImageMessage::class => ['attachments'],
])->get();
```

This will ensure the `mentions_count` attribute is filled on instances of the `TextMessage` model, and the `attachments_count` attribute is filled on instances of the `ImageMessage` model.

Laravel Nova Support
--------------------

[](#laravel-nova-support)

If you want to use share parent Nova resources with child models, you may register the following provider at the end of the boot method of your NovaServiceProvider:

```
class NovaServiceProvider extends NovaApplicationServiceProvider
{
    public function boot() {
        parent::boot();
        // ...
        $this->app->register(\Parental\Providers\NovaResourceProvider::class);
    }
}
```

---

Thanks to [@sschoger](https://twitter.com/steveschoger) for the sick logo design, and [@DanielCoulbourne](https://twitter.com/DCoulbourne) for helping brainstorm the idea on [Twenty Percent Time](http://twentypercent.fm/).

###  Health Score

72

—

ExcellentBetter than 100% of packages

Maintenance85

Actively maintained with recent releases

Popularity67

Solid adoption and visibility

Community40

Growing community involvement

Maturity83

Battle-tested with a long release history

 Bus Factor3

3 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~94 days

Total

34

Last Release

76d ago

Major Versions

v0.11.0 → v1.0.02021-10-08

PHP version history (3 changes)v1.0.0PHP ^7.4|^8.0

v1.1.0PHP ^8.0

v1.5.0PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/151829?v=4)[Matt Stauffer](/maintainers/mattstauffer)[@mattstauffer](https://github.com/mattstauffer)

![](https://www.gravatar.com/avatar/4608292025df3cf1d9db20c0484c7d0a3542e9a63fc681fae175e0233f1c250a?d=identicon)[calebporzio](/maintainers/calebporzio)

---

Top Contributors

[![calebporzio](https://avatars.githubusercontent.com/u/3670578?v=4)](https://github.com/calebporzio "calebporzio (69 commits)")[![tonysm](https://avatars.githubusercontent.com/u/1178621?v=4)](https://github.com/tonysm "tonysm (37 commits)")[![driftingly](https://avatars.githubusercontent.com/u/194221?v=4)](https://github.com/driftingly "driftingly (24 commits)")[![JustSteveKing](https://avatars.githubusercontent.com/u/6368379?v=4)](https://github.com/JustSteveKing "JustSteveKing (18 commits)")[![gcavanunez](https://avatars.githubusercontent.com/u/9332696?v=4)](https://github.com/gcavanunez "gcavanunez (13 commits)")[![Smoggert](https://avatars.githubusercontent.com/u/4356026?v=4)](https://github.com/Smoggert "Smoggert (7 commits)")[![laravel-shift](https://avatars.githubusercontent.com/u/15991828?v=4)](https://github.com/laravel-shift "laravel-shift (6 commits)")[![mattstauffer](https://avatars.githubusercontent.com/u/151829?v=4)](https://github.com/mattstauffer "mattstauffer (4 commits)")[![LenRick](https://avatars.githubusercontent.com/u/91498612?v=4)](https://github.com/LenRick "LenRick (4 commits)")[![ziming](https://avatars.githubusercontent.com/u/679513?v=4)](https://github.com/ziming "ziming (3 commits)")[![tobyzerner](https://avatars.githubusercontent.com/u/128862?v=4)](https://github.com/tobyzerner "tobyzerner (3 commits)")[![dallincoons](https://avatars.githubusercontent.com/u/6494464?v=4)](https://github.com/dallincoons "dallincoons (2 commits)")[![deadpunk](https://avatars.githubusercontent.com/u/937672?v=4)](https://github.com/deadpunk "deadpunk (2 commits)")[![shuvroroy](https://avatars.githubusercontent.com/u/21066418?v=4)](https://github.com/shuvroroy "shuvroroy (2 commits)")[![josecanhelp](https://avatars.githubusercontent.com/u/2329654?v=4)](https://github.com/josecanhelp "josecanhelp (2 commits)")[![tikkibar](https://avatars.githubusercontent.com/u/49984583?v=4)](https://github.com/tikkibar "tikkibar (2 commits)")[![kbond](https://avatars.githubusercontent.com/u/127811?v=4)](https://github.com/kbond "kbond (2 commits)")[![likeadeckofcards](https://avatars.githubusercontent.com/u/6399755?v=4)](https://github.com/likeadeckofcards "likeadeckofcards (2 commits)")[![BertvanHoekelen](https://avatars.githubusercontent.com/u/7521173?v=4)](https://github.com/BertvanHoekelen "BertvanHoekelen (2 commits)")[![michelbrito](https://avatars.githubusercontent.com/u/43976?v=4)](https://github.com/michelbrito "michelbrito (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/calebporzio-parental/health.svg)

```
[![Health](https://phpackages.com/badges/calebporzio-parental/health.svg)](https://phpackages.com/packages/calebporzio-parental)
```

###  Alternatives

[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[owen-it/laravel-auditing

Audit changes of your Eloquent models in Laravel

3.4k33.0M95](/packages/owen-it-laravel-auditing)[dyrynda/laravel-cascade-soft-deletes

Cascading deletes for Eloquent models that implement soft deletes

1.2k3.1M5](/packages/dyrynda-laravel-cascade-soft-deletes)[tightenco/parental

A simple eloquent trait that allows relationships to be accessed through child models.

1.5k1.8M5](/packages/tightenco-parental)[watson/validating

Eloquent model validating trait.

9723.3M47](/packages/watson-validating)[cybercog/laravel-ban

Laravel Ban simplify blocking and banning Eloquent models.

1.1k651.8k11](/packages/cybercog-laravel-ban)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
