# Entity Classes

## Overview

Entity classes represent database records as PHP objects with type safety, validation, and business logic.

## Architecture

```
includes/Entities/
├── Language.php           # Language entity
├── Translation.php        # Translation entity
├── Traits/
│   └── Timestamps.php     # Shared timestamp handling
└── README.md              # This file
```

## Language Entity

### Purpose
Represents a language available in the multilingual system.

### Database Mapping
Maps to: `wp_mpz_languages`

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `id` | `?int` | Primary key |
| `code` | `string` | Language code (en, es, fr) |
| `locale` | `string` | Full locale (en_US, es_ES) |
| `name` | `string` | English name (English, Spanish) |
| `native_name` | `string` | Native name (English, Español) |
| `flag_code` | `?string` | ISO country code for flag (US, ES) |
| `is_default` | `bool` | Is this the default language? |
| `is_active` | `bool` | Is this language active? |
| `sort_order` | `int` | Display order |
| `url_structure` | `string` | subdirectory, subdomain, or parameter |
| `text_direction` | `string` | ltr or rtl |
| `created_at` | `?DateTime` | Creation timestamp |
| `updated_at` | `?DateTime` | Last update timestamp |

### Usage Examples

#### Creating a Language

```php
use MultilingualPressZone\Entities\Language;

// From array (e.g., database row)
$language = new Language([
    'id' => 1,
    'code' => 'en',
    'locale' => 'en_US',
    'name' => 'English',
    'native_name' => 'English',
    'flag_code' => 'US',
    'is_default' => true,
    'is_active' => true,
    'text_direction' => 'ltr',
]);

// Or build manually
$spanish = new Language();
$spanish->setCode('es');
$spanish->setLocale('es_ES');
$spanish->setName('Spanish');
$spanish->setNativeName('Español');
$spanish->setFlagCode('ES');
$spanish->setTextDirection('ltr');
```

#### Checking Properties

```php
// Check text direction
if ($language->isRTL()) {
    echo 'This is a right-to-left language';
}

// Check if default
if ($language->isDefault()) {
    echo 'This is the default language';
}

// Get URL prefix
$prefix = $language->getUrlPrefix(); // '' for default, 'es' for others

// Get display name
echo $language->getDisplayName(); // "Spanish (Español)"
echo $language->getDisplayName(false); // "Spanish"
```

#### Converting to Array

```php
// Full array with all properties
$array = $language->toArray();

// Database-ready array (for INSERT/UPDATE)
$dbArray = $language->toDatabaseArray();
```

### Validation

All setters include validation:

```php
// Code validation
$language->setCode('en'); // Valid
$language->setCode('en_US'); // Valid
$language->setCode('invalid!'); // Throws InvalidArgumentException

// Locale validation
$language->setLocale('en_US'); // Valid
$language->setLocale('invalid'); // Throws InvalidArgumentException

// Text direction validation
$language->setTextDirection('ltr'); // Valid
$language->setTextDirection('rtl'); // Valid
$language->setTextDirection('invalid'); // Throws InvalidArgumentException
```

## Translation Entity

### Purpose
Represents a translation mapping between content elements and languages.

### Database Mapping
Maps to: `wp_mpz_translations`

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `id` | `?int` | Primary key |
| `translation_group_id` | `?int` | Groups related translations |
| `element_type` | `string` | post, page, product, term |
| `element_id` | `int` | WordPress element ID |
| `language_code` | `string` | Language for this element |
| `source_element_id` | `?int` | Original element ID |
| `translation_status` | `string` | original, translated, needs_update, draft |
| `content_hash` | `?string` | SHA256 hash for change detection |
| `created_at` | `?DateTime` | Creation timestamp |
| `updated_at` | `?DateTime` | Last update timestamp |

### Usage Examples

#### Creating a Translation

```php
use MultilingualPressZone\Entities\Translation;

// Original post (English)
$original = new Translation([
    'translation_group_id' => 100,
    'element_type' => 'post',
    'element_id' => 123,
    'language_code' => 'en',
    'translation_status' => 'original',
]);

// Spanish translation
$spanish = new Translation([
    'translation_group_id' => 100,
    'element_type' => 'post',
    'element_id' => 456,
    'language_code' => 'es',
    'source_element_id' => 123,
    'translation_status' => 'translated',
]);
```

#### Status Management

```php
// Check status
if ($translation->isTranslated()) {
    echo 'Translation is complete';
}

if ($translation->needsUpdate()) {
    echo 'Source content has changed';
}

if ($translation->isDraft()) {
    echo 'Translation is in draft';
}

// Update status
$translation->markAsTranslated();
$translation->markAsNeedsUpdate();
$translation->markAsDraft();
$translation->markAsOriginal();
```

#### Content Change Detection

```php
// Set initial content hash
$translation->updateContentHash($post->post_content);

// Later, check if content changed
if ($translation->hasContentChanged($post->post_content)) {
    $translation->markAsNeedsUpdate();
}
```

#### Group Management

```php
// Check if translations are in same group
if ($trans1->isInSameGroupAs($trans2)) {
    echo 'These are translations of the same content';
}
```

### Validation

All setters include validation:

```php
// Element type validation
$translation->setElementType('post'); // Valid
$translation->setElementType('custom_type'); // Valid (logs warning)
$translation->setElementType(''); // Throws InvalidArgumentException

// Status validation
$translation->setTranslationStatus('translated'); // Valid
$translation->setTranslationStatus('invalid'); // Throws InvalidArgumentException

// Content hash validation
$translation->setContentHash(hash('sha256', 'content')); // Valid
$translation->setContentHash('invalid'); // Throws InvalidArgumentException
```

## Timestamps Trait

### Purpose
Provides common timestamp handling for all entities.

### Methods

```php
// Get timestamps
$created = $entity->getCreatedAt(); // DateTime|null
$updated = $entity->getUpdatedAt(); // DateTime|null

// Set timestamps
$entity->setCreatedAt(new DateTime());
$entity->setCreatedAt('2024-01-01 00:00:00'); // Auto-converts string
$entity->setUpdatedAt(new DateTime());

// Update updated_at to now
$entity->touch();
```

### Internal Methods

```php
// Format for database (protected)
$formatted = $this->formatDateTime($datetime); // '2024-01-01 00:00:00'

// Parse from database (protected)
$datetime = $this->parseDateTime('2024-01-01 00:00:00'); // DateTime
$datetime = $this->parseDateTime(null); // null
```

## Best Practices

### 1. Always Use Setters

```php
// ✓ Good - uses validation
$language->setCode('en');

// ✗ Bad - bypasses validation (not possible due to private properties)
$language->code = 'en';
```

### 2. Handle Exceptions

```php
try {
    $language->setCode($userInput);
} catch (InvalidArgumentException $e) {
    // Handle validation error
    wp_die(esc_html($e->getMessage()));
}
```

### 3. Use toDatabaseArray for Persistence

```php
global $wpdb;

$data = $language->toDatabaseArray();

// Insert
$wpdb->insert(
    $wpdb->prefix . 'mpz_languages',
    $data,
    ['%s', '%s', '%s', ...]
);

// Update
$wpdb->update(
    $wpdb->prefix . 'mpz_languages',
    $data,
    ['id' => $language->getId()],
    ['%s', '%s', '%s', ...],
    ['%d']
);
```

### 4. Hydrate from Database Rows

```php
global $wpdb;

$row = $wpdb->get_row(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}mpz_languages WHERE id = %d",
        $id
    ),
    ARRAY_A
);

$language = new Language($row);
```

## Testing

Run the test script:

```bash
php tmp/test-entities.php
```

This validates:
- Entity creation from arrays
- Array conversions (toArray, toDatabaseArray)
- Business logic methods
- Validation rules
- Timestamp handling

## Future Enhancements

Potential additions:
- JSON serialization (`JsonSerializable` interface)
- Dirty tracking (detect changed properties)
- Validation groups
- Custom validation rules
- Event dispatching on state changes
