The Groups
The ODR software utilizes a number of group types:
The Lookup Value (LV) These groups take their options from a lookup table. Here is how one is defined in the backend:
namespace App\Services\App\Module\Specific\Ceramic;
class CeramicInitDetails implements InitDetailsInterface
{
// ...
public static function modelGroups(): array
{
return [
// ...
'Primary Classification' => [
'code' => 'LV',
'field_name' => 'ceramic_primary_classification_id',
'lookup_table_name' => 'ceramic_primary_classifications',
'lookup_text_field' => 'name',
'dependency' => ['Scope.Artifact'],
],
];
}
}
By convention, the first two values in all lookup tables are 1: Unassigned, 2: Unknown
Module Tag (TM) These groups take their options from the module specific module_tags, restricted to a specific group (see ERD).
namespace App\Services\App\Module\Specific\Ceramic;
class CeramicInitDetails implements InitDetailsInterface
{
// ...
public static function modelGroups(): array
{
return [
// ...
'Vessel Base' => [
'code' => 'TM',
'dependency' => ['Vessel Part.Base'],
'multiple' => true,
],
];
}
}
As these groups represent optional properties, they are commonly dependent on other options.
The 'multiple' property functions as its name suggests.
TIP
Whether to implement a groups as a lookup or a tag is subjective. Use your judgement!
Global Tag (TG) Similar in structure to the module tag group, these groups are common to multiple modules. The tables used to define and use them are:
- tags
- tag_groups
- taggable (polymorphic pivot)
Boolean Modified (BM) For lack of boolean support in MySql and in order to follow the lookup tables conventions, this group is defined with the following options (unsigned tinyInteger) - 0: Unassigned, 1: Unknown, 2: True, 3: False
The above groups define properties common to items in a module.
The following groups are used for filtering and defining dependencies:
Filter Only (FO) These groups typically consist of registration columns, or part of them. For example, small finds may have the following registration columns: locus_id, (registration) code, basket_no, artifact_no. where the locus_id is made of year + area + locus_no. In this case we may want to filter by Season, Area, and Registration Code:
namespace App\Services\App\Module\Specific\Ceramic;
class CeramicInitDetails implements InitDetailsInterface
{
// ...
public static function modelGroups(): array
{
return [
// ...
'Season' => [
'code' => 'FO',
'source' => ['module' => 'Season', 'field' => 'id'],
'field_name' => 'locus_id',
'manipulator' => function ($val) {
return (string) ($val + 10);
},
],
'Area' => [
'code' => 'FO',
'source' => ['module' => 'Area', 'field' => 'id'],
'field_name' => 'area_id',
],
'Registration Code' => [
'code' => 'FO',
'source' => ['module' => 'Ceramic', 'field' => 'code'],
'field_name' => 'code',
],
];
}
}
Categorized Groups (CG) These are used to manually define a discrete textual partition that may be dependent on multiple columns values. For example, we may want to define a “Registration Scope” group with the following options [Basket, Artifact] dependent on the condition artifact_no === 0.
In the backend we will implement the specific filter logic:
namespace App\Services\App\Module\Specific\Ceramic;
class CeramicReadDetails implements ReadDetailsInterface
{
private static Builder $builder;
public static function applyCategorizedFilters(Builder $builder, array $groups): Builder
{
self::$builder = $builder;
foreach ($groups as $key => $group) {
switch ($group['group_name']) {
case 'Registration Scope':
self::filterScope($group['selected']);
break;
default:
// Do nothing
}
}
return self::$builder;
}
private static function filterScope(array $vals)
{
if (count($vals) !== 1) {
return;
}
self::$builder->Where(function ($query) use ($vals) {
switch ($vals[0]['name']) {
case 'Basket':
$query->where('artifact_no', 0);
return;
case 'Artifact':
$query->Where('artifact_no', '!=', 0);
return;
default:
return;
}
});
}
}
Other filter groups are:
Column Search (CG) - Performs textual search on a specific column.
Order By (OB)
Media (MD) - Filter by existence of application-wide media types for an item
The implementation of the different filters can be found here.