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

Model

A model extends Hubleto\Erp\Model. It describes one database table: its columns, relations, indexes, and how the table and the form look in the user interface. The model does not run queries. The record manager does.

Anatomy

apps/Customers/Models/Tag.php
<?php

namespace Hubleto\App\Community\Customers\Models;

use Hubleto\Framework\Db\Column\Color;
use Hubleto\Framework\Db\Column\Varchar;

class Tag extends \Hubleto\Erp\Model
{
  public string $table = 'customer_tags';
  public string $recordManagerClass = RecordManagers\Tag::class;
  public ?string $lookupSqlValue = '{%TABLE%}.name';

  public function describeColumns(): array
  {
    return array_merge(parent::describeColumns(), [
      'name' => (new Varchar($this, $this->translate('Name')))->setRequired(),
      'color' => (new Color($this, $this->translate('Color')))->setRequired(),
    ]);
  }

  public function describeTable(): \Hubleto\Framework\Description\Table
  {
    $description = parent::describeTable();

    // $description->ui['title'] = $this->translate('Customer Tags');
    $description->ui['addButtonText'] = $this->translate('Add Customer Tag');
    $description->ui['showHeader'] = true;
    $description->ui['showFulltextSearch'] = true;
    $description->ui['showFooter'] = false;

    return $description;
  }

}
Member Purpose
$table Name of the table (plural, snake_case).
$recordManagerClass The record manager that works with the table.
$lookupSqlValue SQL that is shown when a record is selected in a lookup. Uses {%TABLE%}.
$lookupUrlDetail URL of the detail of a record. Uses {%ID%}.
$relations HAS_MANY, HAS_ONE and BELONGS_TO relations, UPPER_CASE names.
describeColumns() The columns. Merge with parent::describeColumns().
describeTable() Texts, visibility and filters of the table.
describeForm() Defaults and texts of the form.
describeInput($column) Customizes the input of one column.
indexes() Indexes.
onBefore*, onAfter* Callbacks of saving and deleting. Call parent::.
migrations() The migrations of the model (found in Models/Migrations).
upgradeSchema() Creates or upgrades the table. Called in installApp().
upgradeForeignKeys() Adds the foreign keys. Called in install round 3 and by php hubleto migrate.

Members of a model.

Columns

The column types are in Hubleto\Framework\Db\Column: PrimaryKey, Varchar, Text, Integer, Decimal, Currency, Boolean, Date, DateTime, Time, Year, Lookup, Json, Email, Password, Color, Image, File and Virtual. Each column is a fluent object:

apps/Deals/Models/Deal.php (excerpt)
      'identifier' => (new Varchar($this, $this->translate('Deal Identifier')))->setCssClass('badge badge-info')->setDefaultVisible(),
      'title' => (new Varchar($this, $this->translate('Title')))->setRequired()->setDefaultVisible()->setCssClass('font-bold'),
      'id_customer' => (new Lookup($this, $this->translate('Customer'), Customer::class))->setDefaultValue($this->router()->urlParamAsInteger('idCustomer')),
      'id_contact' => (new Lookup($this, $this->translate('Contact'), Contact::class)),
      'id_lead' => (new Lookup($this, $this->translate('Lead'), Lead::class))->setReadonly(),
      'version' => (new Integer($this, $this->translate('Version'))),
      // 'price' => (new Decimal($this, $this->translate('Price')))->setDecimals(2),
      'price_excl_vat' => new Decimal($this, $this->translate('Price excl. VAT')),
      'price_incl_vat' => new Decimal($this, $this->translate('Price incl. VAT')),
      'id_currency' => (new Lookup($this, $this->translate('Currency'), Currency::class))->setFkOnUpdate('RESTRICT')->setFkOnDelete('SET NULL')->setReadonly(),
      'date_expected_close' => (new Date($this, $this->translate('Expected close date'))),
      'id_owner' => (new Lookup($this, $this->translate('Owner'), User::class))->setReactComponent('InputUserSelect')->setDefaultValue($this->authProvider()->getUserId()),

See Understanding columns in a model and describeColumns().

Callbacks

The callbacks have a fixed signature and must call parent:::

Callback Receives → returns
onBeforeValidate($record), onAfterValidate($record) record → record
onBeforeCreate($record), onAfterCreate($saved) record → record
onBeforeUpdate($record) record → record
onAfterUpdate($original, $saved) records → record
onBeforeDelete($id), onAfterDelete($id) int → int
onAfterLoadRecord($record), onAfterLoadRecords($records) record(s) → record(s)

Callbacks.

apps/Customers/Models/Customer.php (excerpt)
  public function onAfterUpdate(array $originalRecord, array $savedRecord): array
  {
    $savedRecord = parent::onAfterUpdate($originalRecord, $savedRecord);

    if (isset($savedRecord["TAGS"])) {
      $helper = $this->getService(Helper::class);
      $helper->deleteTags(
        array_column($savedRecord["TAGS"], "id"),
        $this->getModel("Hubleto/App/Community/Customers/Models/CustomerTag"),
        "id_customer",
        $savedRecord["id"]
      );
    }
    return $savedRecord;
  }

Migrations

A migration is a class in Models/Migrations named <Model>_<NNNN>. The first migration creates the table and the next ones change it. The model finds its migrations by the file-name prefix and runs the missing ones in upgradeSchema().

apps/Customers/Models/Migrations/Customer_0002.php
<?php

namespace Hubleto\App\Community\Customers\Models\Migrations;

use Hubleto\Framework\Migration;

class Customer_0002 extends Migration
{

  public function upgradeSchema(): void
  {
    $this->db->execute("alter table `customers` change `customer_id` `company_id` varchar(255)");
  }

  public function downgradeSchema(): void
  {
    $this->db->execute("alter table `customers` change `company_id` `customer_id` varchar(255)");
  }

  public function upgradeForeignKeys(): void
  {
  }

  public function downgradeForeignKeys(): void
  {
  }
}

More in Models, record managers and migrations.