amarenkov / laravel-mutable-content-tasks
Task domain for laravel-mutable-content: tasks, participants and time tracking with their services and events, without a UI.
Package info
github.com/amarenkov/laravel-mutable-content-tasks
pkg:composer/amarenkov/laravel-mutable-content-tasks
Requires
- php: ^8.4
- amarenkov/laravel-mutable-content: ^0.7.2
Requires (Dev)
- larastan/larastan: ^3.0
- orchestra/testbench: ^11.0
- phpunit/phpunit: ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-19 21:56:23 UTC
README
Task domain for amarenkov/laravel-mutable-content:
tasks, their participants and time tracking, with services and events to build any interface on.
Scope
- Models with mutable fields, so an application adds its own task fields without migrations.
- Services and events: creating and changing tasks, assigning participants, starting and pausing time tracking. Every change lands in the core change log with its author.
- No user interface. Screens, bots and notifications channels belong to the application; the package does not depend on any UI kit, so it fits any project built on laravel-mutable-content.
Requirements
- PHP 8.4
- Laravel 13
- PostgreSQL or MariaDB 10.7+, as required by
amarenkov/laravel-mutable-content
Installation
composer require amarenkov/laravel-mutable-content-tasks
Usage
Every change goes through a service. by() names the actor doing it: its user becomes the author
of the change log entry; logComment() adds a comment to the entry.
use Amarenkov\MutableContentTasks\Lovs\ParticipantRole; use Amarenkov\MutableContentTasks\Lovs\PointType; use Amarenkov\MutableContentTasks\Models\Actor; use Amarenkov\MutableContentTasks\Models\Point; use Amarenkov\MutableContentTasks\Models\TimeEntry; use Amarenkov\MutableContentTasks\Services\Points; use Amarenkov\MutableContentTasks\Services\Time; use Amarenkov\MutableContentTasks\Support\Duration; $me = Actor::forUser($request->user()); $task = Points::new(PointType::TASK)->by($me)->fields(['description' => 'Rewrite the app'])->save(); $part = Points::new(PointType::TASK)->in($task)->by($me)->fields(['description' => 'Models'])->save(); Points::of($task)->by($me)->addParticipant($executor, ParticipantRole::EXECUTOR); Points::of($task)->by($me)->syncParticipants(ParticipantRole::EXECUTOR, [$executor]); // the only executor Points::of($part)->by($me)->logComment('Typo')->update(['description' => 'Domain models']); Points::of($part)->by($me)->moveTo(null); Points::of($task)->by($me)->close(); // time running on it stops Points::of($task)->by($me)->reopen(); Points::of($part)->by($me)->delete(); Time::of($me)->on($task)->comment('Models')->start(); Time::of($me)->stop(); Time::entry($entry)->by($me)->update([TimeEntry::FIELD_FROM => now()->subHour(), TimeEntry::FIELD_POINT_ID => $task->id]); Time::entry($entry)->by($me)->delete(); Point::query()->whereParticipant($me, ParticipantRole::EXECUTOR)->whereOpen()->get(); $task->isClosed(); TimeEntry::last($me, onPoint: true); TimeEntry::secondsOn($me, $task); Duration::clock(5400); // 01:30:00 Duration::hours(5400); // 1.5 h TimeEntry::report($me, now()->startOfDay(), now()->endOfDay()); // [point id => seconds], most first
The creator of a point becomes its author participant. A point with children cannot be deleted,
and a point cannot be moved inside itself; both throw DomainException. Starting time tracking
stops the running entry of the actor, unless it runs on the same point: then it goes on.
A point is open or closed (the point_status LOV; a point without a status is open). Closing
stops the time running on the point, and time cannot be started on a closed point until it is
reopened (DomainException).
A corrected entry keeps its actor, must end after it starts, not in the future and not overlap
other entries of the actor; a finished entry cannot be made running again (DomainException).
Events are dispatched after the transaction commits: PointCreated, PointUpdated (with the
changed fields), PointMoved, PointClosed, PointReopened, PointDeleted, ParticipantAdded, ParticipantRemoved,
TimeEntryStarted, TimeEntryStopped, TimeEntryUpdated (with the changed fields and the
previous point), TimeEntryDeleted. They carry ids, so listeners may be queued.
Translations
Strings are in lang/{en,ru}/tasks.php under the mutable-content-tasks namespace. Publish them
to override:
php artisan vendor:publish --tag=mutable-content-tasks-lang
License
MIT. See LICENSE.