# Model Layer Guide

The model layer supports multiple game backends while keeping a shared API for controllers and views.

For database table keys and logical joins, use [04-database-relationships.md](04-database-relationships.md) before inspecting SQL snapshots.

## Layout

```text
src/model/
|- base/           shared base classes
|- cms/            CMS defaults and bridges
|- emulator/       source-specific models
|- stream/         source-specific models and file readers
|- stream-pod/     stream variant
`- trinity-stream/ stream variant
```

### Layout when FILE Database parser is required

```
src/model/
├── sourceType/         # Stream source (file-based + MySQL)
│   ├── {modelFile.php}
│   ├── ...
│   └── filesReader/        # File parsers
│       ├── entities-file.iterable.php
│       ├── guilds-file.iterable.php
│       ├── arena-file.iterable.php
│       └── deleted-entities-file.iterable.php
```

## Loading rule

`loadModel()` tries the active source first and falls back to `src/model/cms/` when no source-specific file exists.

That means behavior can differ by source even when controllers call the same model name.

`MODELS_SOURCE` is defined as:
```php
define('MODELS_SOURCE', ROOT.'model/'.SOURCE_TYPE.'/');
```

So with `SOURCE_TYPE = 'stream'`, it tries:
1. `src/model/stream/{model}.model.php`
2. Falls back to `src/model/cms/{model}.model.php` (if stream doesn't have one)

## Common pattern

- Models are static classes.
- Shared logic usually lives in `src/model/base/`.
- Thin bridge classes in `src/model/cms/` often just extend a base class.
- Source directories override only the parts that differ.

Example: `StoreModel` uses `Username` by default but stream-based sources override the owner field to `UID`.

### The Bridge Pattern

Most CMS models are **thin bridges** that just extend a base:

```php
// src/model/cms/store.model.php
require_once MODELS_BASE.'store.model.php';
class StoreModel extends StoreBaseModel { }
```

Source models override specific behavior. For example, Stream uses UID instead of Username as the owner key:

```php
// src/model/stream/store.model.php
require_once MODELS_BASE.'store.model.php';
class StoreModel extends StoreBaseModel {
  public static $ownerField = 'UID';  // Stream uses UID, not Username
}
```

## Capabilities

Each source exposes `SOURCE_CAPABILITIES` in its `capabilities.php`. Controllers and views use those flags to decide which features are available.

When changing behavior for a feature, check both the model implementation and the matching capability flags.

```php
// src/model/stream/capabilities.php
const SOURCE_CAPABILITIES = [
  'RANKINGS' => ['arena','class','cps','guild','level','money','nobility','pk','player'],
  'PLAYERS'  => [
    'USE_NAME_FOR_URLS' => false,
    'ONLINE_COUNT' => true,
    'ONLINE_LIST' => true,
    'ITEMS_LIST' => false,
    'RECOVER_CHARACTER' => true,
  ],
  'GUILDS' => ['USE_NAME_FOR_URLS' => false],
];
```

Controllers and views check these flags to enable/disable features:
```php
if (SOURCE_CAPABILITIES['PLAYERS']['RECOVER_CHARACTER']) {
  // Show character recovery UI
}
```

## When adding or changing a source

1. Update the relevant files in `src/model/<source>/`.
2. Keep method signatures aligned with the shared API expected by controllers and views.
3. Add or update the source bootstrap under `tests/auto/models/config/`.
4. Run the narrowest source-specific tests first.

## Stream-based sources

`stream`, `stream-pod`, and `trinity-stream` may read from files as well as the database. Their parsers live under `filesReader/`, so data changes in those sources often require checking both model queries and file readers.

## Database Connections

The `Model` base class manages two connections:

```php
class Model {
  public static $conn;      // Game database
  public static $connCMS;   // CMS database (can be same as game)

  public static function connectGame($host, $user, $pass, $name, $port);
  public static function connectCMS($host, $user, $pass, $name, $port);
}
```

Models use `$conn` for game data and `$connCMS` for CMS data (rankings, shop, payments).

## Adding a New Source

1. Create `src/model/<source>/` directory
2. Copy `capabilities.php` from an existing source and adjust
3. Implement required models:
   - `account.model.php`
   - `entities.model.php`
   - `guilds.model.php`
   - `ranking.model.php` (can extend CmsRankingModel)
4. Test with a new bootstrap in `tests/auto/models/config/`
5. Add the test to composer scripts

## File-Based Sources (Stream)

Stream sources read game data from flat files:

| File Type | Parser Class | Location |
|---|---|---|
| Player .ini | `StreamEntitiesFileIterable` | `filesReader/entities-file.iterable.php` |
| Guild .txt | `StreamGuildsFileIterable` | `filesReader/guilds-file.iterable.php` |
| Arena .ini | `StreamArenaFileIterable` | `filesReader/arena-file.iterable.php` |
| Deleted chars | `StreamDeletedEntitiesFileIterable` | `filesReader/deleted-entities-file.iterable.php` |

These are used by CLI commands to dump data into CMS tables, which are then queried by the ranking models.
