Database Model Basic
Introduction
FluentBoards models are built on the WPFluent ORM, an ActiveRecord implementation that follows the Laravel Eloquent API. Each database table has a model class that you use to query the table and to insert, update and delete rows.
NOTE
For most integrations, prefer the higher-level helpers such as FluentBoardsApi('tasks')->create(). They run the service layer, which sets positions and slugs, logs activity and fires hooks. Writing through models directly skips most of that. These model docs are for internal use and for advanced third-party developers.
Base Model
All FluentBoards models extend FluentBoards\App\Models\Model (fluent-boards/app/Models/Model.php), which extends the framework model FluentBoards\Framework\Database\Orm\Model.
The base model adds:
$guarded = ['id', 'ID'], so primary keys are never mass-assigned.$nullableTimestampAttributes: a list of date columns that are normalized on read and write. Empty strings,none,null,0000-00-00 00:00:00, years before 1900 and unparseable dates becomeNULL. The Task model uses this fordue_at,started_at,last_completed_at,archived_atandremind_at.
Pro models extend FluentBoardsPro\App\Models\Model, which is an empty subclass of the same base model.
Built-in Models
Free models live in fluent-boards/app/Models/. Pro models live in fluent-boards-pro/app/Models/ and fluent-boards-pro/app/Modules/.
| Model | Class | Table | Notes |
|---|---|---|---|
| Board | FluentBoards\App\Models\Board | fbs_boards | Global scope: type is to-do or roadmap |
| Folder | FluentBoards\App\Models\Folder | fbs_boards | Global scope: type = 'folder' |
| BoardTerm | FluentBoards\App\Models\BoardTerm | fbs_board_terms | Base class for Stage, Label, CustomField |
| Stage | FluentBoards\App\Models\Stage | fbs_board_terms | Global scope: type = 'stage' |
| Label | FluentBoards\App\Models\Label | fbs_board_terms | Global scope: type = 'label' |
| CustomField | FluentBoards\App\Models\CustomField | fbs_board_terms | Global scope: type = 'custom-field' |
| Task | FluentBoards\App\Models\Task | fbs_tasks | Global scope: type is task or roadmap |
| TaskMeta | FluentBoards\App\Models\TaskMeta | fbs_task_metas | |
| Comment | FluentBoards\App\Models\Comment | fbs_comments | Always eager-loads images and replies |
| Attachment | FluentBoards\App\Models\Attachment | fbs_attachments | Base class for TaskImage, CommentImage, TaskAttachment |
| TaskImage | FluentBoards\App\Models\TaskImage | fbs_attachments | Images in task descriptions |
| CommentImage | FluentBoards\App\Models\CommentImage | fbs_attachments | Images in comments |
| Activity | FluentBoards\App\Models\Activity | fbs_activities | |
| Notification | FluentBoards\App\Models\Notification | fbs_notifications | |
| NotificationUser | FluentBoards\App\Models\NotificationUser | fbs_notification_users | |
| Meta | FluentBoards\App\Models\Meta | fbs_metas | Base class for Webhook |
| Webhook | FluentBoards\App\Models\Webhook | fbs_metas | Global scope: object_type = 'webhook' |
| Relation | FluentBoards\App\Models\Relation | fbs_relations | Pivot rows |
| Team | FluentBoards\App\Models\Team | fbs_teams | |
| User | FluentBoards\App\Models\User | users (WordPress) | Primary key ID |
| TaskAttachment Pro | FluentBoardsPro\App\Models\TaskAttachment | fbs_attachments | |
| TimeTrack Pro | FluentBoardsPro\App\Modules\TimeTracking\Model\TimeTrack | fbs_time_tracks |
Pro also ships FluentBoardsPro\App\Models\CustomField, a copy of the Free CustomField model. New code should use the Free class.
There is no separate Subtask or Template model. Subtasks are Task rows with parent_id set. Templates are boards whose settings contain is_template = true; use the Board::onlyTemplates() and Board::excludeTemplates() scopes.
In this article we use FluentBoards\App\Models\Board as the example.
Global Scopes
Several models add a global scope that filters rows by type. This matters because a table can hold several kinds of rows:
use FluentBoards\App\Models\Board;
use FluentBoards\App\Models\Folder;
// Only boards with type 'to-do' or 'roadmap'. Folders are excluded.
$boards = Board::all();
// Only folders.
$folders = Folder::all();
// Remove the scope when you need every row in fbs_boards.
$everything = Board::withoutGlobalScope('type')->get();The same applies to Stage, Label and CustomField (all on fbs_board_terms), Task (task / roadmap types) and Webhook (fbs_metas rows with object_type = 'webhook').
Retrieving Models
Each model works as a query builder for its table:
use FluentBoards\App\Models\Board;
$boards = Board::all();
foreach ($boards as $board) {
echo $board->title;
}Adding Additional Constraints
all() returns every row the model can see. To add constraints, chain query builder methods and finish with get():
$boards = FluentBoards\App\Models\Board::where('type', 'to-do')
->whereNull('archived_at')
->orderBy('created_at', 'DESC')
->limit(10)
->offset(5)
->get();See the Query Builder page for the full list of methods.
Retrieving Single Models
Use find or first to get one model instance instead of a collection:
// By primary key
$board = FluentBoards\App\Models\Board::find(1);
// First row matching the constraints
$board = FluentBoards\App\Models\Board::where('type', 'to-do')->first();Pass an array to find to get a collection of matching rows:
$boards = FluentBoards\App\Models\Board::find([1, 2, 3]);Retrieving Aggregates
count, max, min, avg and sum return a scalar value:
$count = FluentBoards\App\Models\Board::where('type', 'to-do')->count();
$max = FluentBoards\App\Models\Board::where('type', 'to-do')->max('id');Inserting & Updating Models
Inserts
Pass the attributes to create(). Only attributes in the model's $fillable list (or not in $guarded) are saved:
$board = FluentBoards\App\Models\Board::create([
'title' => 'My First Board',
'description' => 'This is my first board',
]);The Board model sets type to to-do and created_by to the current user when you leave them empty. It does not create stages or labels. Use FluentBoardsApi('boards')->create() when you need a ready-to-use board.
Updates
Assign properties and call save():
$board = FluentBoards\App\Models\Board::find(1);
$board->title = 'Updated Title';
$board->description = 'Updated Description';
$board->save();Or pass an array to update():
$board = FluentBoards\App\Models\Board::find(1);
$board->update([
'title' => 'Updated Title',
'description' => 'Updated Description',
]);Accessing Attributes
Read columns as properties. Serialized columns such as settings are returned as arrays:
$board = FluentBoards\App\Models\Board::find(1);
$title = $board->title;
$settings = $board->settings; // arrayDeleting Models
Call delete() on a model instance:
$board = FluentBoards\App\Models\Board::find(1);
$board->delete();Deleting Models By Query
You can also delete every row that matches a query. Mass deletes do not fire model events (deleting / deleted) for the deleted rows:
FluentBoards\App\Models\Board::whereNotNull('archived_at')->delete();WARNING
Deleting a model does not delete its related rows (tasks, stages, relations, meta). The service layer, for example BoardService, handles that cleanup.
Query Scopes
A local scope is a reusable set of constraints defined as a scopeName() method. Call it without the scope prefix and with a lowercase first letter. For example, the Board model defines:
public function scopeOnlyTemplates($query)
{
return $query->whereNotNull('settings')
->where(function ($subQuery) {
$subQuery->where('settings', 'LIKE', '%"is_template";b:1%')
->orWhere('settings', 'LIKE', '%"is_template":true%');
});
}Use it like this:
$templates = FluentBoards\App\Models\Board::onlyTemplates()->get();Each model page lists the scopes that model provides.
Relationships
Models define relations to each other. For example, a board has many stages and tasks:
$board = FluentBoards\App\Models\Board::find(1);
$tasks = $board->tasks; // Collection of FluentBoards\App\Models\Task
$stages = $board->stages; // Collection of FluentBoards\App\Models\StageA task belongs to a board:
$task = FluentBoards\App\Models\Task::find(1);
$board = $task->board; // FluentBoards\App\Models\BoardEager-load relations with with() to avoid one query per row:
$boards = FluentBoards\App\Models\Board::with(['stages', 'users'])->get();Each model page lists that model's relations.