www.hubleto.eu Reddit User Guide Stack Overflow LinkedIn Community Report an issue

Design principles

This chapter describes how the community apps are designed. Follow the same principles in your apps. Your code will then look familiar to other Hubleto developers, and it will work with the CLI generators, the Description API and the integrations.

The principles in one list:

Pages in this chapter

Page Summary
Models, record managers and migrations How to split data logic between the model, the record manager and migrations.
React components: tables and forms How to build Table*.tsx and Form*.tsx functional components.
Twig views What belongs in a view, available variables, views for boards.
API controllers and Cron controllers JSON endpoints and scheduled jobs.
Event listeners Reacting to model and controller events without coupling.
Using the hblreact HTML tag How Twig views render React components and pass typed props.

Pages in the Design principles chapter.

A complete CRUD feature in five files

This is the typical set of files for one entity, taken from the Contacts app:

Files for the Contact entity
Models/Contact.php                     # columns, relations, describeTable(), describeForm(), callbacks
Models/RecordManagers/Contact.php      # Eloquent relations, filters, fulltext search, lookups
Models/Migrations/Contact_0001.php     # CREATE TABLE and foreign keys
Controllers/Contacts.php               # sets the view
Views/Contacts.twig                    # renders <hblreact-contacts-table-contacts>
Components/FC/TableContacts.tsx        # table (list of records)
Components/FC/FormContact.tsx          # form (one record)
Controllers/Contacts.php: the controller only sets the view
<?php

namespace Hubleto\App\Community\Contacts\Controllers;

class Contacts extends \Hubleto\Erp\Controller
{
  public function prepareView(): void
  {
    parent::prepareView();
    $this->setView('@Hubleto:App:Community:Contacts/Contacts.twig');
  }
}
Views/Contacts.twig: the view only renders the React table
<hblreact-contacts-table-contacts
  string:tag="table-contacts"
  int:record-id="{{ viewParams.recordId }}"
  string:fulltext-search='{{ viewParams.q }}'
  json:column-search='{{ viewParams.search|json_encode }}'
  json:filters='{{ viewParams.filters|json_encode }}'
  string:form-active-tab-uid='{{ viewParams.tab }}'
></hblreact-contacts-table-contacts>

The table then asks the backend for its description and data, using the model name:

Request sent by TableContacts.tsx
GET api/table-describe-and-load?model=Hubleto/App/Community/Contacts/Models/Contact&page=1&itemsPerPage=35&...
  → Contact::describeTable()                      (columns, filters, UI settings)
  → RecordManagers\Contact::loadTableData(...)    (records of the current page)