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

Generating demo data in Loader class using Loader::generateDemoData()

Purpose

Demo data shows what the app can do right after installation. It is used for presentations, trials, screenshots, manual testing and development. generateDemoData() creates such sample records for your app.

The default implementation in Hubleto\Framework\App does nothing:

public function generateDemoData(): void
{
  // to be overriden
}

When it runs

Demo data is generated by Hubleto\Erp\Cli\Agent\Project\GenerateDemoData. It runs at the end of php hubleto init when the init config contains generateDemoData: true (or you answer yes at the prompt).

The generator:

  1. temporarily grants all permissions (DANGEROUS__grantAllPermissions()),
  2. updates the default company and creates demo users with roles (chief officer, manager, employee, assistant, external),
  3. creates the core CRM data (customers, contacts, activities, leads, deals), if these apps are installed,
  4. calls generateDemoData() of every installed app,
  5. revokes the permissions again.
From Hubleto\Erp\Cli\Agent\Project\GenerateDemoData::run() (part)
foreach ($this->appManager()->getInstalledAppNamespaces() as $appNamespace => $appConfig) {
  $app = $this->appManager()->getApp($appNamespace);
  if ($app) {
    $app->generateDemoData();
  }
}

$permissionsManager->revokeGrantAllPermissions();

Because step 3 runs first, your generateDemoData() can use customers, contacts, users and deals that already exist.

NOTE help.md doesn't list it, but the CLI dispatcher also has php hubleto project generate-demo-data. The generator assumes that it runs right after init (it uses company #1), so on an existing database it may create duplicates.

Examples

Minimal

apps/Suppliers/Loader.php
public function generateDemoData(): void
{
  $mSupplier = $this->getModel(Models\Supplier::class);
  $mSupplier->record->recordCreate(['name' => 'ElectroCorp']);
}

Records linked to existing data

The Orders app creates a document template and nine orders for random existing customers and states:

apps/Orders/Loader.php (shortened)
public function generateDemoData(): void
{
  $mCustomer = $this->getModel(\Hubleto\App\Community\Customers\Models\Customer::class);
  $customerCount = $mCustomer->record->count();

  $mState = $this->getModel(Models\State::class);
  $stateCount = $mState->record->count();

  $mOrder = $this->getModel(Models\Order::class);
  $mHistory = $this->getModel(Models\History::class);

  for ($i = 1; $i <= 9; $i++) {
    $idOrder = $mOrder->record->recordCreate([
      'id_customer' => rand(1, $customerCount),
      'id_state' => rand(1, $stateCount),
      'identifier' => 'O' . date('Y') . '-00' . $i,
      'title' => 'This is a test bid #' . $i,
      'price' => rand(1000, 2000) / rand(3, 5),
      'id_currency' => 1,
      'date_order' => date('Y-m-d', strtotime('-' . rand(0, 10) . ' days')),
    ])['id'];

    $mHistory->record->recordCreate([
      'id_order' => $idOrder,
      'short_description' => $this->translate('Order created'),
      'date_time' => date('Y-m-d H:i:s'),
    ]);
  }
}

TIP rand(1, $customerCount) assumes that IDs are continuous. It works right after init. For robust code, load real IDs: $customerIds = $mCustomer->record->pluck('id')->toArray(); and pick from them.

Idempotent demo data

The HrLeave app checks whether a record exists before creating it, so running the generator twice doesn't create duplicates:

apps/HrLeave/Loader.php (shortened)
public function generateDemoData(): void
{
  $mUser = $this->getModel(\Hubleto\App\Community\Auth\Models\User::class);
  $user = $mUser->record->where('is_active', true)->orderBy('id')->first();
  if (!$user) return;

  $mLeaveType = $this->getModel(Models\LeaveType::class);
  $leaveTypes = [];

  foreach ([
    ['name' => $this->translate('Annual leave'), 'days' => 25, 'paid' => 1],
    ['name' => $this->translate('Sick leave'), 'days' => 10, 'paid' => 1],
    ['name' => $this->translate('Personal leave'), 'days' => 3, 'paid' => 0],
  ] as $typeData) {
    $type = $mLeaveType->record->where('name', $typeData['name'])->first();
    if (!$type) {
      $created = $mLeaveType->record->recordCreate([
        'name' => $typeData['name'],
        'annual_entitlement' => $typeData['days'],
        'is_paid' => $typeData['paid'],
        'requires_approval' => 1,
        'description' => $this->translate('Demo leave policy.'),
      ]);
      $type = $mLeaveType->record->find($created['id']);
    }
    $leaveTypes[] = $type;
  }

  $mRequest = $this->getModel(Models\LeaveRequest::class);
  $pendingType = $leaveTypes[0];

  $pendingRequestExists = $mRequest->record
    ->where('id_user', $user->id)
    ->where('id_leave_type', $pendingType->id)
    ->where('reason', 'Demo pending leave request')
    ->exists();

  if (!$pendingRequestExists) {
    $mRequest->record->recordCreate([
      'id_user' => $user->id,
      'id_leave_type' => $pendingType->id,
      'date_from' => date('Y-m-d', strtotime('+14 days')),
      'date_to' => date('Y-m-d', strtotime('+16 days')),
      'balance_year' => (int) date('Y'),
      'days_requested' => 3,
      'id_approver' => $user->id,
      'reason' => 'Demo pending leave request',
    ]);
  }
}

Hierarchical lists

The Products app creates a category tree with fixed IDs, so child categories can reference their parents:

apps/Products/Loader.php (part)
$categories = [
  [ 'id' => 100, 'id_parent' => 0, 'name' => $this->translate('Computers & Accessories') ],
  [ 'id' => 110, 'id_parent' => 100, 'name' => $this->translate('Laptops & Desktops') ],
  [ 'id' => 111, 'id_parent' => 110, 'name' => $this->translate('Laptop Computers') ],
  [ 'id' => 200, 'id_parent' => 0, 'name' => $this->translate('Mobile Devices & Communication') ],
  // ...
];

Rules

Demo data generated by the CLI template

php hubleto create app generates an empty method:

src/apps/MyFirstApp/Loader.php (generated)
// generateDemoData
public function generateDemoData(): void
{
  // Create any demo data to promote your app.
}

A complete example for the Book model created by php hubleto create model MyFirstApp Book:

public function generateDemoData(): void
{
  $mBook = $this->getModel(Models\Book::class);

  $demoBooks = [
    ['varchar_example' => 'The Pragmatic Programmer', 'text_example' => 'Hunt, Thomas', 'decimal_example' => 39.90],
    ['varchar_example' => 'Clean Code', 'text_example' => 'Robert C. Martin', 'decimal_example' => 35.50],
    ['varchar_example' => 'Domain-Driven Design', 'text_example' => 'Eric Evans', 'decimal_example' => 54.00],
  ];

  foreach ($demoBooks as $book) {
    $mBook->record->recordCreate($book);
  }
}