> ## 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.

# Module Development

> Learn how to create and customize OpenEyes modules

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`:

```php protected/modules/OphCiExample/OphCiExampleModule.php theme={null}
<?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`:

```php protected/modules/OphCiExample/controllers/DefaultController.php theme={null}
<?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:

```php protected/modules/OphCiExample/models/Element_OphCiExample_Data.php theme={null}
<?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:

```php protected/modules/OphCiExample/views/default/form_Element_OphCiExample_Data.php theme={null}
<?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:

```php protected/modules/OphCiExample/migrations/m240101_120000_initial_tables.php theme={null}
<?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:

```php theme={null}
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:

```php theme={null}
public function getDependencies()
{
    return array(
        'Element_OphCiExample_History',
    );
}
```

## Module API Component

Create an API component for module-specific functionality:

```php protected/modules/OphCiExample/components/OphCiExample_API.php theme={null}
<?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:

```php protected/modules/OphCiExample/config/common.php theme={null}
<?php

return array(
    'params' => array(
        'ophciexample_default_duration' => 30,
        'ophciexample_enable_feature' => true,
    ),
);
```

## Testing Modules

Create factories for your models:

```php protected/modules/OphCiExample/factories/models/Element_OphCiExample_DataFactory.php theme={null}
<?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](/development/testing) for more details on writing tests.

## Real-World Example

The OphCiExamination module is a comprehensive example:

```php protected/modules/OphCiExamination/OphCiExaminationModule.php theme={null}
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

1. **Follow naming conventions** - Use appropriate prefixes (OphCi, OphDr, etc.)
2. **Use namespaces** - Organize code with PHP namespaces
3. **Create factories** - Support testing with model factories
4. **Write migrations** - Always version database changes
5. **Document elements** - Add PHPDoc comments to models
6. **Use the API component** - Expose module functionality through an API class
7. **Test thoroughly** - Write unit and feature tests

## Next Steps

<CardGroup cols={2}>
  <Card title="Architecture" icon="sitemap" href="/development/architecture">
    Understand the technical architecture
  </Card>

  <Card title="Testing" icon="vial" href="/development/testing">
    Learn about testing your modules
  </Card>
</CardGroup>
