PHPackages                             whilesmart/eloquent-projects - 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. whilesmart/eloquent-projects

ActiveLibrary

whilesmart/eloquent-projects
============================

Polymorphic project and version management package for Laravel applications

01↓100%PHP

Since Mar 16Pushed 1mo agoCompare

[ Source](https://github.com/whilesmartphp/eloquent-projects)[ Packagist](https://packagist.org/packages/whilesmart/eloquent-projects)[ RSS](/packages/whilesmart-eloquent-projects/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Eloquent Projects
=================

[](#eloquent-projects)

Polymorphic project container for Laravel applications.

Features
--------

[](#features)

- **Polymorphic Ownership**: Any model can own projects (workspaces, teams, orgs, users)
- **Polymorphic Creator**: Track who or what created a project (users, agents, systems)
- **Polymorphic Assignments**: Assign any entity to a project with a role
- **Configurable Statuses**: Define project lifecycle states via database
- **Archiving**: Soft archive/unarchive without deletion
- **Lifecycle Events**: Hooks for created, updated, deleted, archived, assigned, etc.
- **Middleware Hooks**: Before/after hooks on all controller actions
- **Customizable Models**: Override Project, ProjectStatus, ProjectAssignment via config
- **Child Model Support**: Any model can belong to a project via `BelongsToProject` trait
- **Laravel 10+, 11+, 12+ Support**

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

[](#installation)

```
composer require whilesmart/eloquent-projects
```

### Publish Configuration (Optional)

[](#publish-configuration-optional)

```
php artisan vendor:publish --tag=projects-config
```

### Run Migrations

[](#run-migrations)

```
php artisan migrate
```

Quick Start
-----------

[](#quick-start)

### Owning Projects

[](#owning-projects)

Add the `HasProjects` trait to any model that should own projects:

```
use Whilesmart\Projects\Traits\HasProjects;

class Workspace extends Model
{
    use HasProjects;
}
```

```
$workspace->createProject('My Design', $user);
$workspace->createProject('Campaign Q2', $aiAgent, 'Q2 marketing assets');
$workspace->projects; // all projects owned by this workspace
```

### Child Models

[](#child-models)

Add `BelongsToProject` to models that live inside a project:

```
use Whilesmart\Projects\Traits\BelongsToProject;

class DesignVersion extends Model
{
    use BelongsToProject;
}
```

```
$version->project;                           // parent project
DesignVersion::forProject($projectId)->get(); // all versions for a project
```

### Assignments

[](#assignments)

Assign any entity (user, agent, team) to a project with a role:

```
use Whilesmart\Projects\Traits\AssignableToProject;

class User extends Model
{
    use AssignableToProject;
}
```

```
// Assign
$project->assign($user, 'editor');
$project->assign($aiAgent, 'generator', $admin);
$project->assign($team, 'stakeholder', null, ['notify' => true]);

// Query
$project->assignments;                    // all assignments
$project->assignees('editor')->get();     // editors only
$project->isAssigned($user, 'editor');    // true

// From the assignable side
$user->assignedProjects()->get();            // all projects
$user->assignedProjects('editor')->get();    // projects where user is editor
$user->isAssignedToProject($project);        // true

// Unassign
$project->unassign($user, 'editor');   // remove specific role
$project->unassign($user);            // remove all roles
```

### Statuses

[](#statuses)

```
use Whilesmart\Projects\Models\ProjectStatus;

ProjectStatus::create(['name' => 'Active', 'slug' => 'active', 'color' => '#22c55e']);
ProjectStatus::create(['name' => 'Paused', 'slug' => 'paused', 'color' => '#f59e0b']);
ProjectStatus::create(['name' => 'Completed', 'slug' => 'completed', 'color' => '#3b82f6']);

$project->update(['status_id' => $status->id]);
$project->status->name; // "Active"

Project::withStatus('active')->get();
```

### Archiving

[](#archiving)

```
$project->archive();
$project->isArchived();  // true
$project->unarchive();

Project::active()->get();    // non-archived
Project::archived()->get();  // archived only
```

### Metadata

[](#metadata)

```
$project = Project::create([
    'owner_type' => Workspace::class,
    'owner_id' => $ws->id,
    'title' => 'Campaign',
    'metadata' => ['budget' => 5000, 'deadline' => '2026-04-01'],
]);

$project->getMetadata('budget');              // 5000
$project->getMetadata('missing', 'default');  // 'default'
$project->setMetadata('priority', 'high');
$project->save();
```

Events
------

[](#events)

EventFired when`ProjectCreated`Project is created`ProjectUpdated`Project is updated`ProjectDeleted`Project is deleted`ProjectArchived`Project is archived`ProjectUnarchived`Project is unarchived`ProjectAssigned`Entity is assigned to a project`ProjectUnassigned`Entity is unassigned from a projectConfiguration
-------------

[](#configuration)

```
// config/projects.php
return [
    'models' => [
        'project' => \Whilesmart\Projects\Models\Project::class,
        'project_status' => \Whilesmart\Projects\Models\ProjectStatus::class,
        'project_assignment' => \Whilesmart\Projects\Models\ProjectAssignment::class,
    ],

    'register_routes' => true,
    'route_prefix' => '',
    'route_middleware' => ['auth:sanctum'],

    'middleware_hooks' => [],
];
```

### Custom Models

[](#custom-models)

```
class Project extends \Whilesmart\Projects\Models\Project
{
    // custom logic, relationships, scopes
}
```

```
// config/projects.php
'models' => [
    'project' => App\Models\Project::class,
],
```

### Middleware Hooks

[](#middleware-hooks)

```
use Whilesmart\Projects\Interfaces\MiddlewareHookInterface;

class ProjectHook implements MiddlewareHookInterface
{
    public function before(Request $request, string $action): ?Request
    {
        return $request;
    }

    public function after(Request $request, JsonResponse $response, string $action): JsonResponse
    {
        return $response;
    }
}
```

API Routes
----------

[](#api-routes)

```
GET    /projects                List projects (filter: ?owner_type=, ?owner_id=, ?status=, ?archived=)
POST   /projects                Create project
GET    /projects/{id}           Show project
PUT    /projects/{id}           Update project
DELETE /projects/{id}           Delete project
POST   /projects/{id}/archive   Archive project
POST   /projects/{id}/unarchive Unarchive project

```

Database Schema
---------------

[](#database-schema)

### projects

[](#projects)

ColumnTypeDescriptionowner\_type / owner\_idmorphPolymorphic ownercreator\_type / creator\_idmorph (nullable)Polymorphic creator (immutable)status\_idFK (nullable)References project\_statusestitlestringProject titledescriptiontext (nullable)Descriptionthumbnail\_urlstring (nullable)Preview imagemetadataJSON (nullable)Flexible dataarchived\_attimestamp (nullable)Archive timestamp### project\_statuses

[](#project_statuses)

ColumnTypeDescriptionnamestringDisplay nameslugstring (unique)Identifierdescriptionstring (nullable)Descriptioncolorstring (nullable)Hex colorsort\_orderintegerDisplay ordering### project\_assignments

[](#project_assignments)

ColumnTypeDescriptionproject\_idFKReferences projectsassignable\_type / assignable\_idmorphEntity assignedrolestringAssignment roleassigned\_by\_type / assigned\_by\_idmorph (nullable)Who assignedmetadataJSON (nullable)Flexible dataUnique constraint: `(project_id, assignable_type, assignable_id, role)`

Testing
-------

[](#testing)

```
make test       # Run tests via Docker
make pint       # Run code formatter
make check      # Run all checks (pint + tests)
```

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10.0, 11.0, or 12.0

License
-------

[](#license)

MIT License

Credits
-------

[](#credits)

Developed by the Whilesmart Team

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance62

Regular maintenance activity

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/a1ca6f6e01ecbfe6640ff410c4f0321054fb48d05a5f843c2b89887b90369bcd?d=identicon)[whilesmart](/maintainers/whilesmart)

---

Top Contributors

[![nfebe](https://avatars.githubusercontent.com/u/14317775?v=4)](https://github.com/nfebe "nfebe (1 commits)")

### Embed Badge

![Health badge](/badges/whilesmart-eloquent-projects/health.svg)

```
[![Health](https://phpackages.com/badges/whilesmart-eloquent-projects/health.svg)](https://phpackages.com/packages/whilesmart-eloquent-projects)
```

PHPackages © 2026

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