Skip to main content

Backend

The Projects module manages the project lifecycle, detail with aggregated data, and statistics for the index.

File Structure

app/
├── Http/
│ ├── Controllers/
│ │ ├── Projects/
│ │ │ └── ProjectController.php
│ │ └── Api/Projects/
│ │ └── ProjectSearchController.php
│ └── Requests/Projects/
│ ├── StoreProjectRequest.php
│ └── UpdateProjectRequest.php
├── Models/
│ └── Project.php
└── Queries/Projects/
├── ProjectIndexQuery.php
├── ProjectShowQuery.php
├── ProjectStatsQuery.php
└── ProjectProfitStatsQuery.php

Routes

MethodURIActionDescription
GET/projectsindexPaginated project list
POST/projectsstoreCreate new project
GET/projects/{project}showProject detail
PUT/projects/{project}updateUpdate project
DELETE/projects/{project}destroySoft delete
POST/projects/{id}/restorerestoreRestore project
DELETE/projects/{id}/force-deleteforceDeletePermanently delete
GET/api/search/projects__invokeProject search for navbar

Note: the /projects/{project}/chat* routes are part of the AI module and are documented in docs/modules/AI/backend.md.

Controller

The ProjectController uses the Query Classes pattern to keep the controller lean. Application queries are delegated to dedicated classes in app/Queries/Projects/.

Methods

  • index() - Uses ProjectIndexQuery for list/filters/pagination and ProjectStatsQuery for statistics cards
  • store() - Validation via StoreProjectRequest, then record creation
  • show() - Uses ProjectShowQuery for tab blocks (tasks, meetings, payments, costs, documents) and ProjectProfitStatsQuery for profit/margin
  • update() - Validation via UpdateProjectRequest, with conditional redirect (show or index)
  • destroy() - Soft delete
  • restore() - Restore deleted record
  • forceDelete() - Permanent deletion

API Controller

ProjectSearchController exposes a search endpoint for the navbar:

  • input q (minimum 2 characters)
  • matches on project name, client name, or client VAT number
  • limit 10 results

Model

The Project model is located in app/Models/Project.php.

Features

  • SoftDeletes - deleted projects are recoverable
  • Automatic slug - unique slug generation in creating
  • Relationships - client, tasks, meetings, payments, costs, documents
  • CalendarEventable - calendar integration with Google Calendar link
  • toFormPayload() - safe edit payload (id + editable fields)

Project Statuses

StatusDescription
draftDraft
in_progressIn progress
completedCompleted
archivedArchived

Priorities

PriorityDescription
lowLow
mediumMedium
highHigh

Project Types

TypeDescription
client_workClient work
productInternal product
contentContent
assetInternal assets

Form Requests

Validation handled by:

  • StoreProjectRequest - project creation
  • UpdateProjectRequest - project update

Required Fields

  • name - project name
  • status - status (draft, in_progress, completed, archived)
  • type - type (client_work, product, content, asset)

Optional Fields

  • dates: start_date, due_date (due_date >= start_date)
  • client link: client_id
  • priority: priority
  • project URLs: repo_url, staging_url, production_url, figma_url, docs_url
  • text: description, notes

Query Classes

The module uses the Query Classes pattern to separate query logic from the controller.

ProjectIndexQuery

Manages the project list with:

  • pagination (15)
  • status filter
  • priority filter
  • search by project name or client data
  • sorting with column/direction whitelist

ProjectShowQuery

Composes the show page payload:

  • latest records per tab (tasks, meetings, payments, costs, documents)
  • total counts per tab (tasksCount, meetingsCount, etc.)
  • configurable limit (default 10)

ProjectStatsQuery

Calculates index statistics:

  • total projects
  • count by status
  • new projects this month
  • completed in the current week
  • useful percentages for cards

ProjectProfitStatsQuery

Calculates financial KPIs for a single project, filtered by the default currency from BusinessSettings::current()->default_currency:

  • total collected payments
  • total costs
  • absolute profit
  • percentage margin