Record manager (Eloquent)
The data layer of Hubleto uses Eloquent, the ORM of Laravel. A record manager is the Eloquent model of a table. It extends Hubleto\Erp\RecordManager. You get it from the Hubleto model through the property record:
apps/Deals/Controllers/Api/LogActivity.php (excerpt)
/** @var Deal */
$mDeal = $this->getModel(Deal::class);
$deal = $mDeal->record->find($idDeal)->first()?->toArray();
$mDeal->record is the record manager, so everything that Eloquent offers is available: find(), where(), with(), count(), get(), ...
What a record manager contains
apps/Customers/Models/RecordManagers/Customer.php
<?php
namespace Hubleto\App\Community\Customers\Models\RecordManagers;
use Hubleto\App\Community\Auth\Models\RecordManagers\User;
use Hubleto\App\Community\Contacts\Models\RecordManagers\Contact;
use Hubleto\App\Community\Deals\Models\RecordManagers\Deal;
use Hubleto\App\Community\Leads\Models\RecordManagers\Lead;
use Hubleto\App\Community\Settings\Models\RecordManagers\Country;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Customer extends \Hubleto\Erp\RecordManager
{
public $table = 'customers';
/** @return HasMany<Contact, covariant Customer> */
public function CONTACTS(): HasMany
{
return $this->hasMany(Contact::class, 'id_customer');
}
/** @return HasOne<Country, covariant Customer> */
public function COUNTRY(): HasOne
{
return $this->hasOne(Country::class, 'id', 'id_country');
}
/** @return HasMany<CustomerActivity, covariant Customer> */
public function ACTIVITIES(): HasMany
{
return $this->hasMany(CustomerActivity::class, 'id_customer', 'id');
}
/** @return HasMany<CustomerDocument, covariant Customer> */
public function DOCUMENTS(): HasMany
{
return $this->hasMany(CustomerDocument::class, 'id_customer', 'id');
}
/** @return HasMany<CustomerTag, covariant Customer> */
public function TAGS(): HasMany
{
return $this->hasMany(CustomerTag::class, 'id_customer', 'id');
}
/** @return HasMany<Lead, covariant Customer> */
public function LEADS(): HasMany
{
return $this->hasMany(Lead::class, 'id_customer', 'id');
}
/** @return HasMany<Deal, covariant Customer> */
public function DEALS(): HasMany
{
return $this->hasMany(Deal::class, 'id_customer', 'id');
}
/** @return BelongsTo<User, covariant Customer> */
public function OWNER(): BelongsTo
{
return $this->belongsTo(User::class, 'id_owner', 'id');
}
/** @return BelongsTo<User, covariant Customer> */
public function MANAGER(): BelongsTo
{
return $this->belongsTo(User::class, 'id_manager', 'id');
}
public function prepareReadQuery(mixed $query = null, int $level = 0, array|null $includeRelations = null): mixed
{
$query = parent::prepareReadQuery($query, $level, $includeRelations);
// Virtual tag count
$query->selectSub(function($sub) {
$sub->from('cross_customer_tags')
->join('customer_tags', 'customer_tags.id', '=', 'cross_customer_tags.id_tag')
->whereColumn('cross_customer_tags.id_customer', 'customers.id')
->selectRaw("COUNT(DISTINCT customer_tags.id)");
}, 'tags_count');
return $query;
}
/**
* [Description for addUrlFiltersToQuery]
*
* @param mixed|null $query
*
* @return mixed
*
*/
public function addUrlFiltersToQuery(mixed $query): mixed
{
$query = parent::addUrlFiltersToQuery($query);
$hubleto = \Hubleto\Erp\Loader::getGlobalApp();
$filters = $hubleto->router()->urlParamAsArray("filters");
if (isset($filters["fCustomerActive"])) {
if ($filters["fCustomerActive"] == 1) {
$query = $query->where("customers.is_active", false);
} else {
$query = $query->where("customers.is_active", true);
}
}
if (isset($filters['fTag']) && is_array($filters['fTag']) && count($filters['fTag']) > 0) {
$query = $query->whereHas('TAGS', function($q) use ($filters) {
$q->whereIn('cross_customer_tags.id_tag', $filters['fTag']);
});
}
return $query;
}
/**
* [Description for addOrderByToQuery]
*
* @param mixed $query
* @param array $orderBy
*
* @return mixed
*
*/
public function addOrderByToQuery(mixed $query, array $orderBy): mixed
{
if (($orderBy['field'] ?? null) === 'virt_tags') {
return $query->orderBy('tags_count', $orderBy['direction']);
} else {
return parent::addOrderByToQuery($query, $orderBy);
}
}
/**
* [Description for addFulltextSearchToQuery]
*
* @param mixed $query
* @param string $fulltextSearch
*
* @return mixed
*
*/
public function addFulltextSearchToQuery(mixed $query, string $fulltextSearch): mixed
{
if (!empty($fulltextSearch)) {
$query = parent::addFulltextSearchToQuery($query, $fulltextSearch);
$like = "%{$fulltextSearch}%";
$query->orHaving('virt_tags', 'like', "%{$like}%");
}
return $query;
}
}
$table(the same as in the model),- the relations as Eloquent methods with UPPER_CASE names (
OWNER(),CONTACTS()), prepareReadQuery(),addUrlFiltersToQuery()andprepareLookupData().
Methods of the base class
| Method | Purpose |
|---|---|
prepareReadQuery($query, $level, $relations) |
The base query for every read. Applies ownership of records. |
addUrlFiltersToQuery($query) |
Applies filters[...] and other URL parameters. |
addFulltextSearchToQuery(), addColumnSearchToQuery(), addOrderByToQuery() |
Search and sorting of tables. |
loadTableData(), loadFormData(), loadTreeData() |
Loads the data for the table, the form and the tree. |
prepareLookupQuery(), prepareLookupData() |
Data of a lookup input. |
recordCreate($record) |
Validates and creates a record. |
recordUpdate($record) |
Validates and updates a record. |
recordSave($record) |
Creates or updates, depending on the id. |
recordDelete($id) |
Deletes a record. |
recordRead($id), recordReadMany() |
Reads one or more records. |
recordValidate($record) |
Validation against the columns. |
Methods of a record manager.
Reading with the permissions of the user
prepareReadQuery() limits the records to those that the user may see (see ownership columns). When you read records for a user, start from it:
apps/Customers/Models/RecordManagers/Customer.php (excerpt)
public function prepareReadQuery(mixed $query = null, int $level = 0, array|null $includeRelations = null): mixed
{
$query = parent::prepareReadQuery($query, $level, $includeRelations);
// Virtual tag count
$query->selectSub(function($sub) {
$sub->from('cross_customer_tags')
->join('customer_tags', 'customer_tags.id', '=', 'cross_customer_tags.id_tag')
->whereColumn('cross_customer_tags.id_customer', 'customers.id')
->selectRaw("COUNT(DISTINCT customer_tags.id)");
}, 'tags_count');
return $query;
}
$level says how many relations are loaded. A table needs 0, a form 1 or more. Pass $includeRelations to load only the relations you need.
