> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Planification de tâches

> Utilisez le planificateur de Laravel pour orchestrer élégamment vos tâches périodiques.

## Qu'est-ce que la planification

Traditionnellement, il fallait écrire manuellement une entrée cron par tâche périodique.
Cette approche externalise les définitions du code : aucune versioning, et un SSH est nécessaire pour modifier.

Avec le planificateur Laravel, vous définissez les planifications **directement dans l'application** de manière fluide.
Une seule entrée cron sur le serveur, et les définitions vivent avec votre code, sous contrôle de version.

Convention : définir les planifications dans `routes/console.php`.

```php theme={null}
<?php

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
```

<Info>
  `schedule:list` liste les tâches et affiche leur prochaine exécution.

  ```shell theme={null}
  php artisan schedule:list
  ```
</Info>

### Flux d'exécution

```mermaid theme={null}
flowchart TD
    A["* * * * * artisan schedule:run<br>(cron chaque minute)"] --> B["Kernel Console"]
    B --> C["Appel de schedule()"]
    C --> D["Évaluation des tâches"]
    D --> E{"L'expression cron<br>correspond ?"}
    E -->|"Non"| F["Ignorer"]
    E -->|"Oui"| G{"Conditions when / skip /<br>environments"}
    G -->|"Non"| F
    G -->|"Oui"| H{"En mode maintenance ?"}
    H -->|"Normal"| I["Exécuter<br>(hooks avant / corps / hooks après)"]
    H -->|"Maintenance"| J{"evenInMaintenanceMode() ?"}
    J -->|"Oui"| I
    J -->|"Non"| F
```

## Définir des planifications

Écrivez dans `routes/console.php`. Vous pouvez aussi passer par `withSchedule` dans `bootstrap/app.php`.

```php theme={null}
// bootstrap/app.php
use Illuminate\Console\Scheduling\Schedule;

->withSchedule(function (Schedule $schedule) {
    $schedule->call(new DeleteRecentUsers)->daily();
})
```

## Types de tâches planifiables

### Commandes Artisan

Utilisez `command`. Nom de commande ou classe.

```php theme={null}
use App\Console\Commands\SendEmailsCommand;
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send Taylor --force')->daily();

Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
```

Commandes définies par closure : chaînez directement.

```php theme={null}
Artisan::command('delete:recent-users', function () {
    DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
```

### Jobs de queue

`job` planifie un [job de queue](/fr/queues), sans closure explicite.

```php theme={null}
use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;

Schedule::job(new Heartbeat)->everyFiveMinutes();
```

Nom de queue et connexion :

```php theme={null}
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
```

### Commandes shell

`exec` exécute une commande OS.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::exec('node /home/forge/script.js')->daily();
```

### Closures

`call` planifie une closure.

```php theme={null}
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
```

Objet avec `__invoke` :

```php theme={null}
Schedule::call(new DeleteRecentUsers)->daily();
```

## Fréquences

### Méthodes principales

| Méthode                      | Description            |
| ---------------------------- | ---------------------- |
| `->everySecond()`            | Toutes les secondes    |
| `->everyMinute()`            | Chaque minute          |
| `->everyFiveMinutes()`       | Toutes les 5 min       |
| `->everyFifteenMinutes()`    | Toutes les 15 min      |
| `->everyThirtyMinutes()`     | Toutes les 30 min      |
| `->hourly()`                 | Chaque heure           |
| `->hourlyAt(17)`             | Chaque heure à HH:17   |
| `->daily()`                  | À 0 h                  |
| `->dailyAt('13:00')`         | À 13 h                 |
| `->twiceDaily(1, 13)`        | 1 h et 13 h            |
| `->weekly()`                 | Dimanche à 0 h         |
| `->weeklyOn(1, '8:00')`      | Lundi à 8 h            |
| `->monthly()`                | 1er du mois à 0 h      |
| `->monthlyOn(4, '15:00')`    | Le 4 à 15 h            |
| `->quarterly()`              | 1er du trimestre à 0 h |
| `->yearly()`                 | 1er janvier à 0 h      |
| `->timezone('Europe/Paris')` | Spécifier le fuseau    |

### Expression cron directe

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->cron('0 9 * * *'); // 9 h chaque jour
```

### Combinaisons

```php theme={null}
use Illuminate\Support\Facades\Schedule;

// Lundi à 13 h
Schedule::call(function () {
    // ...
})->weekly()->mondays()->at('13:00');

// Jours ouvrés, 8 h → 17 h, chaque heure
Schedule::command('foo')
    ->weekdays()
    ->hourly()
    ->timezone('Europe/Paris')
    ->between('8:00', '17:00');
```

### Fuseau horaire

`timezone` sur une tâche :

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('report:generate')
    ->timezone('Europe/Paris')
    ->at('9:00');
```

Ou par défaut pour tout via `config/app.php`.

```php theme={null}
// config/app.php
'schedule_timezone' => 'Europe/Paris',
```

<Warning>
  Les fuseaux avec heure d'été peuvent exécuter une tâche deux fois ou pas du tout au moment du changement. Préférez UTC dans la mesure du possible.
</Warning>

## Contraintes

### `when` / `skip`

Exécute si `when` retourne `true`, ignore si `skip` retourne `true`.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily()->when(function () {
    return true;
});

Schedule::command('emails:send')->daily()->skip(function () {
    return true;
});
```

### `environments`

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->environments(['staging', 'production']);
```

### Contraintes horaires

```php theme={null}
Schedule::command('emails:send')
    ->hourly()
    ->between('7:00', '22:00');

Schedule::command('emails:send')
    ->hourly()
    ->unlessBetween('23:00', '4:00');
```

### Jours

| Méthode                       | Description                  |
| ----------------------------- | ---------------------------- |
| `->weekdays()`                | Jours ouvrés                 |
| `->weekends()`                | Week-end                     |
| `->mondays()` à `->sundays()` | Un jour précis               |
| `->days([0, 3])`              | Dimanche (0) et mercredi (3) |

```php theme={null}
use Illuminate\Support\Facades;
use Illuminate\Console\Scheduling\Schedule;

Facades\Schedule::command('emails:send')
    ->hourly()
    ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
```

## Éviter la superposition

Par défaut, une tâche peut commencer même si la précédente tourne encore. `withoutOverlapping` attend la fin.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->withoutOverlapping();
```

TTL du verrou (minutes) ; 24 h par défaut.

```php theme={null}
Schedule::command('emails:send')->withoutOverlapping(10);
```

<Info>
  Le verrou passe par le cache. Si la tâche reste bloquée, libérez-le avec `schedule:clear-cache`.

  ```shell theme={null}
  php artisan schedule:clear-cache
  ```
</Info>

## Multi-serveurs

Avec `onOneServer`, une seule instance de serveur exécute la tâche parmi toutes.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('report:generate')
    ->fridays()
    ->at('17:00')
    ->onOneServer();
```

<Warning>
  Nécessite un driver de cache `database`, `memcached`, `dynamodb` ou `redis`, partagé entre tous les serveurs.
</Warning>

### Flux distribué avec `onOneServer()`

```mermaid theme={null}
flowchart TD
    A["Plusieurs serveurs<br>exécutent schedule:run"] --> B["Serveur 1"]
    A --> C["Serveur 2"]
    A --> D["Serveur 3"]

    B --> E["Tentative d'acquisition<br>d'un verrou atomique partagé"]
    C --> E
    D --> E

    E -->|"Succès (1er serveur)"| F["Exécute la tâche"]
    E -->|"Échec (autres)"| G["Skip"]

    F --> H["Libère le verrou"]
```

## Grouper des tâches

Appliquez des réglages communs avec `group`.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::daily()
    ->onOneServer()
    ->timezone('Europe/Paris')
    ->group(function () {
        Schedule::command('emails:send --force');
        Schedule::command('emails:prune');
    });
```

## Exécution en arrière-plan

Les tâches simultanées s'exécutent par défaut dans l'ordre de définition. `runInBackground` lance en parallèle.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('analytics:report')
    ->daily()
    ->runInBackground();
```

<Warning>
  Disponible uniquement avec `command` et `exec`.
</Warning>

## Mode maintenance

En mode maintenance, les tâches planifiées ne s'exécutent pas. `evenInMaintenanceMode` force l'exécution.

```php theme={null}
Schedule::command('emails:send')->evenInMaintenanceMode();
```

### Flux

```mermaid theme={null}
flowchart TD
    A["artisan schedule:run"] --> B{"Mode maintenance ?<br>(artisan down)"}
    B -->|"Normal"| C["Exécute"]
    B -->|"Maintenance"| D{"evenInMaintenanceMode() ?"}
    D -->|"Oui"| C
    D -->|"Non"| E["Skip"]
```

## Pause du planificateur

Suspendez sans modifier le code.

```shell theme={null}
# Pause
php artisan schedule:pause

# Reprise
php artisan schedule:continue
```

Pour continuer certaines tâches malgré la pause : `evenWhenPaused`. Utile pour la supervision.

```php theme={null}
Schedule::command('emails:send')->evenWhenPaused();
```

## Gestion des sorties

### Fichiers

`sendOutputTo` écrit dans un fichier.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')
    ->daily()
    ->sendOutputTo(storage_path('logs/emails-send.log'));
```

`appendOutputTo` ajoute au fichier existant.

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->appendOutputTo(storage_path('logs/emails-send.log'));
```

### Par e-mail

`emailOutputTo` envoie la sortie par e-mail. Nécessite la [configuration mail](/fr/mail).

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->sendOutputTo($filePath)
    ->emailOutputTo('admin@example.com');
```

Uniquement en cas d'échec : `emailOutputOnFailure`.

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->emailOutputOnFailure('admin@example.com');
```

## Hooks

`before` / `after` avant et après l'exécution.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')
    ->daily()
    ->before(function () {
        // Avant
    })
    ->after(function () {
        // Après
    });
```

Succès / échec via `onSuccess` / `onFailure`.

```php theme={null}
use Illuminate\Support\Stringable;

Schedule::command('emails:send')
    ->daily()
    ->onSuccess(function (Stringable $output) {
        // OK
    })
    ->onFailure(function (Stringable $output) {
        // KO
    });
```

## Déploiement sur serveur

<Steps>
  <Step title="Ajouter l'entrée cron">
    Ajoutez à `crontab -e` :

    ```shell theme={null}
    * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
    ```
  </Step>

  <Step title="Vérifier">
    Liste des tâches et prochaines exécutions :

    ```shell theme={null}
    php artisan schedule:list
    ```
  </Step>
</Steps>

<Tip>
  Avec [Laravel Cloud](https://cloud.laravel.com), la planification est gérée sans configuration cron.
</Tip>

### En local

`schedule:work` maintient un planificateur en foreground.

```shell theme={null}
php artisan schedule:work
```

Il tourne jusqu'à `Ctrl+C`.

### Sous-minute

Cron ne descend pas sous la minute. Laravel offre `everySecond()`.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->everySecond();
```

Lorsque des tâches sous-minute existent, `schedule:run` tourne jusqu'à la fin de la minute.

<Tip>
  Déléguez le travail à des jobs / commandes en arrière-plan : sinon, une tâche longue bloque les suivantes.
</Tip>

Pour interrompre un `schedule:run` pendant un déploiement :

```shell theme={null}
php artisan schedule:interrupt
```

## Commandes utiles

```shell theme={null}
# Lister
php artisan schedule:list

# Exécuter (appelé par cron)
php artisan schedule:run

# Local, en boucle
php artisan schedule:work

# Pause
php artisan schedule:pause

# Reprise
php artisan schedule:continue

# Libérer les verrous
php artisan schedule:clear-cache

# Interrompre les sous-minutes
php artisan schedule:interrupt
```


## Related topics

- [Tutoriel - Laravel Console Starter](/fr/packages/laravel-console-starter/tutorial.md)
- [FAQ sur la nouvelle structure d'application Laravel 11+](/fr/advanced/app-structure-faq.md)
- [Console Artisan](/fr/artisan.md)
- [Labeler](/fr/packages/laravel-bluesky/labeler.md)
- [Introduction à Laravel Nightwatch](/fr/blog/nightwatch-introduction.md)
