# Recherches Eloquent avec Champs Chiffrés

## Problème

Avec les champs chiffrés (`'encrypted'` cast), les requêtes SQL standards ne fonctionnent pas car Laravel ne peut pas comparer directement les valeurs chiffrées dans la base de données.

## Solution : Trait `HasEncryptedAttributes`

Tous les modèles avec des champs chiffrés incluent maintenant le trait `HasEncryptedAttributes` qui fournit des méthodes de recherche spéciales.

### Modèles Concernés

- **User** : `name`, `email`
- **Employee** : `telephone`, `email`, `address`, `number`, `street`, `city`, `province`, `postal_code`, `firstName`, `lastName`, `job`, `hiringDate`
- **Absence** : `notes`
- **AccidentTravail** : `notes`
- **DossierDisciplinaire** : `description`, `enquete`, `notes`
- **EvaluationAnnuelle** : `description`, `notes`
- **Formation** : `notes`
- **Invalidite** : `notes`
- **SoutienPedagogique** : `description`, `notes`

## Méthodes Disponibles

### 1. `scopeWhereEncrypted($field, $value)`

Recherche exacte sur un champ chiffré.

```php
// ❌ NE FONCTIONNE PLUS (champ chiffré)
$user = User::where('email', 'john@example.com')->first();

// ✅ UTILISER À LA PLACE
$user = User::whereEncrypted('email', 'john@example.com')->first();

// Autre exemple
$employees = Employee::whereEncrypted('city', 'Montreal')->get();
```

### 2. `scopeWhereEncryptedLike($field, $pattern)`

Recherche avec pattern (LIKE) sur un champ chiffré.

```php
// Recherche avec wildcard
$employees = Employee::whereEncryptedLike('lastName', 'Dupon%')->get();

// Recherche contenant
$employees = Employee::whereEncryptedLike('email', '%@example.com')->get();

// Recherche contenant n'importe où
$notes = Absence::whereEncryptedLike('notes', '%urgent%')->get();
```

### 3. `findByEncrypted($field, $value)` (static)

Méthode statique pour trouver UN seul enregistrement. Plus efficace que `whereEncrypted()->first()`.

```php
// ❌ ANCIEN CODE
$user = User::where('email', 'john@example.com')->first();

// ✅ NOUVEAU CODE
$user = User::findByEncrypted('email', 'john@example.com');

// Autre exemple
$employee = Employee::findByEncrypted('telephone', '514-555-1234');
```

### 4. `getAllByEncrypted($field, $value)` (static)

Méthode statique pour obtenir TOUS les enregistrements correspondants.

```php
// Trouver tous les employés d'une ville
$employees = Employee::getAllByEncrypted('city', 'Montreal');

// Trouver tous les utilisateurs avec un nom spécifique
$users = User::getAllByEncrypted('name', 'John Doe');
```

### 5. `getEncryptedAttributes()` (instance)

Obtenir la liste des attributs chiffrés pour un modèle.

```php
$user = new User();
$encryptedFields = $user->getEncryptedAttributes();
// Returns: ['name', 'email']

$employee = new Employee();
$encryptedFields = $employee->getEncryptedAttributes();
// Returns: ['telephone', 'email', 'address', 'number', 'street', 'city', 'province', 'postal_code', 'firstName', 'lastName', 'job', 'hiringDate']
```

## Exemples d'Utilisation

### Recherche d'Utilisateur par Email

```php
// Dans un contrôleur
public function findUser(Request $request)
{
    $email = $request->input('email');
    
    // Méthode 1 : Scope
    $user = User::whereEncrypted('email', $email)->first();
    
    // Méthode 2 : Static method (plus efficace)
    $user = User::findByEncrypted('email', $email);
    
    return response()->json($user);
}
```

### Recherche d'Employés par Ville

```php
// Récupérer tous les employés de Montréal
$montrealEmployees = Employee::whereEncrypted('city', 'Montreal')->get();

// Ou avec la méthode statique
$montrealEmployees = Employee::getAllByEncrypted('city', 'Montreal');

// Avec pagination
$employees = Employee::whereEncrypted('city', 'Montreal')->paginate(20);
```

### Recherche avec Pattern

```php
// Trouver tous les employés dont le nom commence par "Dup"
$employees = Employee::whereEncryptedLike('lastName', 'Dup%')->get();

// Trouver toutes les notes contenant "urgent"
$absences = Absence::whereEncryptedLike('notes', '%urgent%')->get();
```

### Combinaison avec d'autres Clauses

```php
// Les scopes peuvent être combinés avec d'autres clauses
$activeEmployees = Employee::whereEncrypted('city', 'Montreal')
    ->where('is_archived', false)
    ->orderBy('created_at', 'desc')
    ->get();

// Avec relations
$user = User::whereEncrypted('email', 'john@example.com')
    ->with('teams')
    ->first();
```

## Migration du Code Existant

### Avant (ne fonctionne plus)

```php
User::where('email', $email)->first();
User::where('name', 'like', '%John%')->get();
Employee::where('city', 'Montreal')->get();
```

### Après (fonctionne avec encryption)

```php
User::findByEncrypted('email', $email);
User::whereEncryptedLike('name', '%John%')->get();
Employee::whereEncrypted('city', 'Montreal')->get();
```

## Performance

⚠️ **Important** : Ces méthodes chargent TOUS les enregistrements en mémoire pour les filtrer, car les champs chiffrés ne peuvent pas être recherchés directement dans la base de données.

### Pour Optimiser :

1. **Limitation des résultats** : Utilisez `findByEncrypted()` quand vous cherchez UN seul enregistrement
2. **Index sur d'autres colonnes** : Filtrez d'abord par des colonnes non-chiffrées
3. **Hash de recherche** : Pour les champs très utilisés (comme email), envisagez d'ajouter une colonne `email_hash` avec index

Exemple d'optimisation :

```php
// ❌ Lent si beaucoup d'utilisateurs
$user = User::whereEncrypted('email', $email)->first();

// ✅ Plus rapide avec findByEncrypted
$user = User::findByEncrypted('email', $email);

// ✅ Encore plus rapide si on filtre d'abord par team_id
$user = User::where('current_team_id', $teamId)
    ->get()
    ->first(fn($u) => $u->email === $email);
```

## Détection Automatique

Le trait détecte automatiquement si un champ est chiffré. Si vous utilisez ces méthodes sur un champ non-chiffré, elles fonctionneront quand même en utilisant les requêtes SQL standard.

```php
// Fonctionne même si 'created_at' n'est PAS chiffré
$users = User::whereEncrypted('created_at', '2025-01-01')->get();
// Équivalent à: User::where('created_at', '2025-01-01')->get();
```

## Tests

```php
// Test de recherche par email chiffré
public function test_can_find_user_by_encrypted_email()
{
    $user = User::factory()->create(['email' => 'test@example.com']);
    
    $found = User::findByEncrypted('email', 'test@example.com');
    
    $this->assertEquals($user->id, $found->id);
}

// Test de recherche par ville chiffrée
public function test_can_find_employees_by_encrypted_city()
{
    Employee::factory()->create(['city' => 'Montreal']);
    Employee::factory()->create(['city' => 'Quebec']);
    
    $montreal = Employee::getAllByEncrypted('city', 'Montreal');
    
    $this->assertCount(1, $montreal);
}
```

## Résumé

| Avant | Après |
|-------|-------|
| `Model::where('field', $value)` | `Model::whereEncrypted('field', $value)` |
| `Model::where('field', 'like', $pattern)` | `Model::whereEncryptedLike('field', $pattern)` |
| `Model::where('field', $value)->first()` | `Model::findByEncrypted('field', $value)` |
| `Model::where('field', $value)->get()` | `Model::getAllByEncrypted('field', $value)` |
