Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Todo Laravel Package

charleslightjarvis/laravel-todo

A Laravel package for adding todo lists and task management to your application. It offers a simple way to create, track, and organize to-dos, with models, migrations, and a clean API that fits naturally into Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its migration before writing any code:

composer require charleslightjarvis/laravel-todo
php artisan vendor:publish --tag="todo-migrations"
php artisan migrate

Optionally publish the config if you need to change defaults:

php artisan vendor:publish --tag="todo-config"

Start by reading three things, in order:

  1. The package README for the canonical API.
  2. The published config/todo.php to see what is actually configurable.
  3. The published migration to confirm column names, nullable fields, indexes, and polymorphic columns.

Add the HasTodos trait to the first model that should own todos:

use CharlesLightJarvis\LaravelTodo\Traits\HasTodos;

class Project extends Model
{
    use HasTodos;
}

The quickest first use case is attaching a todo to a model and querying it back:

$project->todos()->create([
    'title' => 'Fix checkout bug',
    'priority' => 'high',
]);

$pending = $project->todos()->pending()->get();

Use the Todo facade when you want to work contextually against a model without going through the relation:

use Todo;

Todo::for($project)->create(['title' => 'Update dependencies']);
Todo::for($team)->overdue()->get();

Implementation Patterns

Trait-first for model-owned workflows. Prefer $model->todos() when the todo belongs to the page/model you are already working with. It keeps controllers and actions explicit and avoids resolving the facade for simple CRUD.

$user->todos()->create([
    'title' => 'Review PR',
    'priority' => 'medium',
    'status' => 'pending',
]);

Facade for cross-model dashboards and contextual queries. The Todo::for($model) API is useful when the owning model is polymorphic or comes from a route binding:

Route::get('/projects/{project}/todos', function (Project $project) {
    return Todo::for($project)
        ->pending()
        ->highPriority()
        ->get();
});

Compose built-in scopes instead of writing local filters. The package ships pending(), completed(), overdue(), highPriority(), and dueToday(). Chain them for common views:

$user->todos()->pending()->dueToday()->get();
$user->todos()->overdue()->highPriority()->get();
Todo::for($team)->completed()->get();

Model the status workflow explicitly. Supported statuses are pending, in_progress, completed, and cancelled. Keep transitions in one place rather than scattering string updates:

$todo->update(['status' => 'in_progress']);

$user->completeTodo($todo);

Use priority for triage views. Priority values are low, medium, and high. Combine priority with due-date scopes for “what needs attention today” widgets:

$urgent = $user->todos()
    ->pending()
    ->highPriority()
    ->dueToday()
    ->get();

Handle creator tracking deliberately. Todos have a polymorphic creator relation. Do not assume the authenticated user is attached automatically. Set the creator where the todo is created, using the relation API documented in the README, and eager load it in list views:

$todos = $user->todos()->with('creator')->pending()->get();

Use eager loading and counts for indexes. Todo lists commonly show owner, creator, and counts. Avoid N+1 queries:

$projects = Project::withCount([
    'todos as pending_todos_count' => fn ($q) => $q->pending(),
])->get();

Keep validation at the application boundary. Because status and priority are string values in v1, validate them with Rule::in([...]) in form requests rather than relying on the package to reject invalid values.

Test with the real relationship. Use RefreshDatabase and assert against the trait/facade API so package upgrades surface behavioural changes:

it('completes a todo', function () {
    $user = User::factory()->create();
    $todo = $user->todos()->create(['title' => 'Test']);

    $user->completeTodo($todo);

    expect($todo->fresh()->status)->toBe('completed');
});

Gotchas and Tips

  • Publish the migration before migrating. The release documents vendor:publish --tag="todo-migrations" as a required step. If the table is missing, check that this ran and that the migration is in database/migrations.
  • The config is optional but deploy-sensitive. If you publish config/todo.php, include php artisan config:cache in deployment. If you change table names or model bindings later, clear cached config before debugging “wrong table” errors.
  • Do not assume completeTodo() is idempotent. Check the implementation before calling it from retries, jobs, or UI double-submits. Guard with a status check where completion triggers side effects.
  • Events are not available yet. TodoCreated, TodoCompleted, and TodoCancelled are listed as “Coming Soon.” Do not design listeners around them in v1; put side effects in your service/action layer for now.
  • There is no prune command yet. The todos:prune artisan command is also “Coming Soon.” Plan your own cleanup for cancelled or old completed todos.
  • Overdue and due-today scopes depend on due dates. If you create todos without due-date values, overdue() and dueToday() will return nothing useful. Make due dates optional but intentional in your forms.
  • Polymorphic type storage matters. If your application uses Relation::morphMap(), confirm how the package stores todos.todoable_type and creator_type. Changing morph maps after data exists can orphan relationships.
  • The creator is not automatic. Set it explicitly at creation time. In list views, eager load creator to avoid one query per row.
  • Facade alias registration may depend on package discovery. If Todo::for() fails with a class-not-found or alias error, check config/app.php aliases and whether package discovery is enabled in your environment.
  • Validate status and priority values yourself. Strings are flexible but unsafe if accepted directly from request input. Use allow-lists and return 422s for invalid values.
  • Watch for N+1 on polymorphic owners. When listing todos globally, eager load both the todoable owner and creator as needed:
Todo::with(['todoable', 'creator'])->pending()->get();
  • Prefer published migrations over vendor edits. If you need extra columns or indexes, publish and modify the migration in your app rather than editing files under vendor/.
  • Treat “Coming Soon” as roadmap, not API. Do not build production workflows that depend on events or pruning until a release explicitly ships them.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin