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

Loader (the app class)

The Loader class in the root of an app is the backend entry point of the app. It extends Hubleto\Erp\App. The core creates it for each enabled app and calls init(). The same class is used when the app is installed.

Smallest possible Loader

apps/Customers/Loader.php
<?php

namespace Hubleto\App\Community\Customers;

class Loader extends \Hubleto\Erp\App
{

  /**
   * Inits the app: adds routes, settings, calendars, event listeners, menu items, ...
   *
   * @return void
   * 
   */
  public function init(): void
  {
    parent::init();

    $this->router()->get([
      '/^customers\/api\/get-customer\/?$/' => Controllers\Api\GetCustomer::class,
      '/^customers\/api\/log-activity\/?$/' => Controllers\Api\LogActivity::class,

      '/^customers(\/(?<recordId>\d+))?\/?$/' => Controllers\Customers::class,
      '/^customers\/add\/?$/' => ['controller' => Controllers\Customers::class, 'vars' => ['recordId' => -1]],
      '/^customers\/tags\/?$/' => Controllers\Tags::class,
    ]);

    $this->addSearchSwitch('c', 'customers');

    /** @var \Hubleto\App\Community\Calendar\Manager $calendarManager */
    $calendarManager = $this->getService(\Hubleto\App\Community\Calendar\Manager::class);
    $calendarManager->addCalendar($this, 'customers', Calendar::class);

    /** @var \Hubleto\App\Community\Settings\Loader $settingsApp */
    $settingsApp = $this->appManager()->getApp(\Hubleto\App\Community\Settings\Loader::class);
    $settingsApp->addSetting($this, [
      'title' => $this->translate('Customer Tags'),
      'icon' => 'fas fa-tags',
      'url' => 'customers/tags',
    ]);
  }

  /**
   * [Description for upgradeSchema]
   *
   * @param int $round
   * 
   * @return void
   * 
   */
  public function installApp(int $round): void
  {

    if ($round == 1) {
      $mCustomer = $this->getModel(Models\Customer::class);
      $mCustomerDocument = $this->getModel(Models\CustomerDocument::class);
      $mCustomerTag = $this->getModel(Models\Tag::class);
      $mCrossCustomerTag = $this->getModel(Models\CustomerTag::class);

      $mCustomer->upgradeSchema();
      $mCustomerTag->upgradeSchema();
      $mCrossCustomerTag->upgradeSchema();
      $mCustomerDocument->upgradeSchema();

      $mCustomerTag->record->recordCreate([ 'name' => $this->translate("VIP"), 'color' => '#D33115' ]);
      $mCustomerTag->record->recordCreate([ 'name' => $this->translate("Partner"), 'color' => '#4caf50' ]);
      $mCustomerTag->record->recordCreate([ 'name' => $this->translate("Public"), 'color' => '#2196f3' ]);
    }

    if ($round == 2) {
      $mCustomerActivity = $this->getModel(\Hubleto\App\Community\Customers\Models\CustomerActivity::class);
      $mCustomerActivity->upgradeSchema();
    }
  }

  /**
   * Implements fulltext search functionality for customers
   *
   * @param array $expressions List of expressions to be searched and glued with logical 'or'.
   * 
   * @return array
   * 
   */
  public function search(array $expressions): array
  {
    $mCustomer = $this->getModel(Models\Customer::class);
    $qCustomers = $mCustomer->record->prepareReadQuery();

    foreach ($expressions as $e) {
      $qCustomers = $qCustomers->where(function($q) use ($e) {
        $q->orWhere('customers.name', 'like', '%' . $e . '%');
        $q->orWhere('customers.city', 'like', '%' . $e . '%');
        $q->orWhere('customers.vat_id', 'like', '%' . $e . '%');
        $q->orWhere('customers.tax_id', 'like', '%' . $e . '%');
        $q->orWhere('customers.company_id', 'like', '%' . $e . '%');
      });
    }

    $customers = $qCustomers->get()->toArray();

    $results = [];

    foreach ($customers as $customer) {
      $results[] = [
        "id" => $customer['id'],
        "label" => $customer['name'],
        "url" => 'customers/' . $customer['id'],
        "description" => $customer['company_id'] . ' ' . $customer['city'],
      ];
    }

    return $results;
  }

}

Methods you can override

Method Called Default behavior
init() On every request, for every enabled app. Loads the manifest. Call parent::init() first.
installApp(int $round) When the app is installed, rounds 1, 2 and 3. Nothing.
generateDemoData() php hubleto project generate-demo-data. Nothing.
renderSecondSidebar() When the desktop renders the app's menu. Empty.
renderAlerts() On the desktop for every user. Empty string.
getSidebarBadgeNumber() On the desktop, cached per user. 0.
search(array $expressions) When the user types into the search box. No results.
assignPermissionsToRoles() Install round 3. Grants the generic API controllers.
installDefaultPermissions() Install round 3. One permission for each controller.
installForeignKeys() Install round 3 and php hubleto migrate. Adds the foreign keys of all models.
onBeforeRender() Before a page of the app is rendered. Nothing.
getMcpTools() When the MCP server lists the tools. []. Return your tool classes, as Contacts does.

Overridable methods.

Helpers

Helper Purpose
$this->router()->crud($slug, $controllerClass) Registers list, detail and "add" routes.
$this->router()->get($regex, $controllerClass) Registers one route.
$this->addSearchSwitch($switch, $slug) Registers a search switch (d for Deals).
$settingsApp->addSetting($app, [...]) On the Settings app: adds a setting.
$this->secondSidebarTitle(), secondSidebarButton() Build the second sidebar.
$this->configAsString() … setConfigAsString(), saveConfig() Configuration of the app.
$this->collectExtendibles($name) Collects an extendible.
$this->getModel(), getService(), getController() Instances.

Helpers of App.

Full example

Deals registers everything in init():

apps/Deals/Loader.php (excerpt)
  public function init(): void
  {
    parent::init();

    $this->router()->crud('deals', Controllers\Deals::class);

    $this->router()->get([
      '/^deals\/api\/log-activity\/?$/' => Controllers\Api\LogActivity::class,
      '/^deals\/api\/get-preview-vars\/?$/' => Controllers\Api\GetPreviewVars::class,
      '/^deals\/api\/create-from-lead\/?$/' => Controllers\Api\CreateFromLead::class,
      '/^deals\/api\/generate-quotation-pdf\/?$/' => Controllers\Api\GenerateQuotationPdf::class,
      '/^deals\/api\/generate-invoice\/?$/' => Controllers\Api\GenerateInvoice::class,
      '/^deals\/api\/set-parent-lead\/?$/' => Controllers\Api\SetParentLead::class,

      '/^deals\/boards\/deal-warnings\/?$/' => Controllers\Boards\DealWarnings::class,
      '/^deals\/boards\/most-valuable-deals\/?$/' => Controllers\Boards\MostValuableDeals::class,
      '/^deals\/boards\/deal-value-by-result\/?$/' => Controllers\Boards\DealValueByResult::class,

      '/^deals\/tags\/?$/' => Controllers\Tags::class,
      '/^deals\/lost-reasons\/?$/' => Controllers\LostReasons::class,

      '/^deals\/plan\/?$/' => Controllers\Plan::class,
    ]);

    $this->addSearchSwitch('d', 'deals');

    /** @var \Hubleto\App\Community\Settings\Loader $settingsApp */
    $settingsApp = $this->appManager()->getApp(\Hubleto\App\Community\Settings\Loader::class);
    $settingsApp->addSetting($this, [
      'title' => $this->translate('Deal Tags'),
      'icon' => 'fas fa-tags',
      'url' => 'deals/tags',
    ]);
    $settingsApp->addSetting($this, [
      'title' => $this->translate('Deal Lost Reasons'),
      'icon' => 'fas fa-tags',
      'url' => 'deals/lost-reasons',
    ]);

    /** @var \Hubleto\App\Community\Calendar\Manager */
    $calendarManager = $this->getService(\Hubleto\App\Community\Calendar\Manager::class);
    $calendarManager->addCalendar($this, 'deals', Calendar::class);

    /** @var \Hubleto\App\Community\Workflow\Manager */
    $workflowManager = $this->getService(\Hubleto\App\Community\Workflow\Manager::class);
    $workflowManager->addWorkflowGroup($this, 'deals', Workflow::class);

    /** @var \Hubleto\App\Community\Dashboards\Manager */
    $dashboardManager = $this->getService(\Hubleto\App\Community\Dashboards\Manager::class);
    $dashboardManager->addBoard($this, $this->translate('Deal warnings'), 'deals/boards/deal-warnings');
    $dashboardManager->addBoard($this, $this->translate('Most valuable deals'), 'deals/boards/most-valuable-deals');
    $dashboardManager->addBoard($this, $this->translate('Deal value by result'), 'deals/boards/deal-value-by-result');

  }

More in Loader.php and the App creation checklist.