Documentation Index Fetch the complete documentation index at: https://mintlify.com/appertafoundation/openeyes/llms.txt
Use this file to discover all available pages before exploring further.
OpenEyes modules encapsulate functionality for different event types and features. This guide covers how to create and structure custom modules.
Module Types
Event Type Modules
Event type modules represent clinical encounters. They follow naming conventions based on the type of event:
OphCi - Clinical Investigation
OphCiExamination - Clinical examination
OphCiPhasing - Phasing tests
OphCiDidNotAttend - DNA tracking
OphDr - Drug/Prescription related
OphDrPrescription - Prescriptions
OphDrPGDPSD - PGD/PSD protocols
OphTr - Treatment events
OphTrOperationnote - Operation notes
OphCo - Correspondence/Communication
OphCoCorrespondence - Letters
OphCoMessaging - Messages
OphCoCvi - Certificate of Vision Impairment
Administrative Modules
Admin - System administration
Api - API functionality
Genetics - Genetics features
Module Structure
A typical event type module has this structure:
protected/modules/OphCiExamination/
├── OphCiExaminationModule.php # Module definition
├── controllers/
│ └── DefaultController.php # Main event controller
├── models/
│ ├── Element_OphCiExamination_*.php # Element models
│ └── *.php # Supporting models
├── views/
│ └── default/
│ ├── create.php
│ ├── view.php
│ ├── update.php
│ └── form_*.php # Element forms
├── migrations/
│ └── m*.php # Database migrations
├── components/
│ └── OphCiExamination_API.php # API component
├── assets/
│ ├── js/
│ └── css/
├── widgets/ # Custom widgets
├── factories/ # Test factories
│ └── models/
└── tests/ # Module tests
├── unit/
└── feature/
Creating a Module
Step 1: Module Definition
Create the module class extending BaseEventTypeModule:
protected/modules/OphCiExample/OphCiExampleModule.php
<? php
namespace OEModule\OphCiExample ;
class OphCiExampleModule extends \ BaseEventTypeModule
{
public $controllerNamespace = '\OEModule\OphCiExample\controllers' ;
public function init ()
{
// Import other module models if needed
$this -> setImport ( array (
'application.modules.OphDrPrescription.models.*' ,
));
// Set sub-modules if needed
$this -> setModules ([ 'ExampleAdmin' ]);
// Import module-level components
$this -> setImport ( array (
'OphCiExample.components.*' ,
'OphCiExample.seeders.*' ,
));
parent :: init ();
}
}
Step 2: Controller
Create the default controller extending BaseEventTypeController:
protected/modules/OphCiExample/controllers/DefaultController.php
<? php
namespace OEModule\OphCiExample\controllers ;
class DefaultController extends \ BaseEventTypeController
{
// Define custom action types
protected static $action_types = array (
'customAction' => self :: ACTION_TYPE_FORM ,
);
/**
* Set to true if the index search bar should appear
*/
protected $show_index_search = true ;
/**
* Set to true if element sidebar should appear
*/
protected $show_element_sidebar = true ;
/**
* Set default options for elements
*/
protected function setElementDefaultOptions ( $element , $action )
{
parent :: setElementDefaultOptions ( $element , $action );
if ( $element instanceof models\ Element_OphCiExample_Data ) {
$element -> exam_date = date ( 'Y-m-d' );
}
}
/**
* Custom action example
*/
public function actionCustomAction ()
{
// Custom logic
$this -> renderJSON ([ 'success' => true ]);
}
}
Step 3: Element Models
Create element models for the event:
protected/modules/OphCiExample/models/Element_OphCiExample_Data.php
<? php
namespace OEModule\OphCiExample\models ;
use OE\factories\models\traits\ HasFactory ;
class Element_OphCiExample_Data extends \ BaseEventTypeElement
{
use HasFactory ;
/**
* Returns the static model
*/
public static function model ( $className = __CLASS__ )
{
return parent :: model ( $className );
}
/**
* Returns the database table name
*/
public function tableName ()
{
return 'et_ophciexample_data' ;
}
/**
* Validation rules
*/
public function rules ()
{
return array (
array ( 'event_id, exam_date' , 'required' ),
array ( 'exam_date' , 'date' , 'format' => 'yyyy-MM-dd' ),
array ( 'notes' , 'safe' ),
);
}
/**
* Attribute labels
*/
public function attributeLabels ()
{
return array (
'exam_date' => 'Examination Date' ,
'notes' => 'Clinical Notes' ,
);
}
/**
* Relations
*/
public function relations ()
{
return array (
'event' => array ( self :: BELONGS_TO , '\Event' , 'event_id' ),
'user' => array ( self :: BELONGS_TO , '\User' , 'created_user_id' ),
);
}
}
Step 4: Views
Create view templates:
protected/modules/OphCiExample/views/default/form_Element_OphCiExample_Data.php
<? php
/**
* @var Element_OphCiExample_Data $element
* @var BaseEventTypeController $this
*/
?>
< div class = "element-fields full-width" >
<? php echo $form -> datePicker (
$element ,
'exam_date' ,
array (),
array (),
array ( 'maxDate' => 'today' )
) ?>
<? php echo $form -> textArea (
$element ,
'notes' ,
array ( 'rows' => 4 , 'cols' => 80 ),
false ,
array ( 'class' => 'autosize' )
) ?>
</ div >
Step 5: Database Migration
Create the database structure:
protected/modules/OphCiExample/migrations/m240101_120000_initial_tables.php
<? php
class m240101_120000_initial_tables extends OEMigration
{
public function up ()
{
// Create event type
$this -> createOETable (
'event_type' ,
array (
'id' => 'pk' ,
'name' => 'varchar(40) NOT NULL' ,
'class_name' => 'varchar(64) NOT NULL' ,
),
true
);
$this -> insert ( 'event_type' , array (
'name' => 'Examination' ,
'class_name' => 'OphCiExample' ,
));
// Create element table
$this -> createOETable (
'et_ophciexample_data' ,
array (
'id' => 'pk' ,
'event_id' => 'int(10) unsigned NOT NULL' ,
'exam_date' => 'date NOT NULL' ,
'notes' => 'text' ,
),
true
);
$this -> addForeignKey (
'et_ophciexample_data_ev_fk' ,
'et_ophciexample_data' ,
'event_id' ,
'event' ,
'id'
);
}
public function down ()
{
$this -> dropOETable ( 'et_ophciexample_data' , true );
$this -> delete ( 'event_type' , 'class_name = ?' , array ( 'OphCiExample' ));
}
}
Module Elements
Element Basics
Elements are the building blocks of events. Each element:
Extends BaseEventTypeElement
Represents a section of the event form
Has its own database table
Can be required or optional
Multiple Elements
Events can have multiple elements:
class Element_OphCiExample_History extends BaseEventTypeElement { }
class Element_OphCiExample_Examination extends BaseEventTypeElement { }
class Element_OphCiExample_Diagnosis extends BaseEventTypeElement { }
Element Dependencies
Elements can depend on other elements:
public function getDependencies ()
{
return array (
'Element_OphCiExample_History' ,
);
}
Module API Component
Create an API component for module-specific functionality:
protected/modules/OphCiExample/components/OphCiExample_API.php
<? php
class OphCiExample_API extends \ BaseAPI
{
/**
* Get latest examination for patient
*/
public function getLatestExamination ( $patient_id )
{
$event = $this -> getMostRecentEventByType ( $patient_id , 'OphCiExample' );
if ( $event ) {
return \ Element_OphCiExample_Data :: model ()
-> find ( 'event_id = ?' , array ( $event -> id ));
}
return null ;
}
}
Module Configuration
Modules can have their own configuration:
protected/modules/OphCiExample/config/common.php
<? php
return array (
'params' => array (
'ophciexample_default_duration' => 30 ,
'ophciexample_enable_feature' => true ,
),
);
Testing Modules
Create factories for your models:
protected/modules/OphCiExample/factories/models/Element_OphCiExample_DataFactory.php
<? php
namespace OEModule\OphCiExample\factories\models ;
use OE\factories\ ModelFactory ;
use OEModule\OphCiExample\models\ Element_OphCiExample_Data ;
class Element_OphCiExample_DataFactory extends ModelFactory
{
public function definition () : array
{
return [
'exam_date' => $this -> faker -> date (),
'notes' => $this -> faker -> sentence (),
'event_id' => \Event :: factory (),
];
}
}
See Testing for more details on writing tests.
Real-World Example
The OphCiExamination module is a comprehensive example:
protected/modules/OphCiExamination/OphCiExaminationModule.php
namespace OEModule\OphCiExamination ;
class OphCiExaminationModule extends \ BaseEventTypeModule
{
public $controllerNamespace = '\OEModule\OphCiExamination\controllers' ;
public function init ()
{
$this -> setImport ( array (
'application.modules.OphDrPrescription.models.*' ,
));
$this -> setModules ([ 'ExaminationAdmin' ]);
$this -> setImport ( array (
'OphCiExamination.seeders.*' ,
));
parent :: init ();
}
}
Explore the OphCiExamination module source code for a complete implementation example.
Module Registration
Modules are automatically discovered if placed in protected/modules/. They can also be registered in the application configuration.
Best Practices
Follow naming conventions - Use appropriate prefixes (OphCi, OphDr, etc.)
Use namespaces - Organize code with PHP namespaces
Create factories - Support testing with model factories
Write migrations - Always version database changes
Document elements - Add PHPDoc comments to models
Use the API component - Expose module functionality through an API class
Test thoroughly - Write unit and feature tests
Next Steps
Architecture Understand the technical architecture
Testing Learn about testing your modules