Skip to content

Виконання завдань cron

Завдання cron корисні для виконання завдань з технічного обслуговування. На відміну від воркерів, вони працюють за розкладом, протягом короткого періоду часу.

Очищування коментарів

Коментарі, позначені як спам або відхилені адміністратором, зберігаються в базі даних, оскільки адміністратор може захотіти оглянути їх пізніше. Але вони, ймовірно, мають бути видалені через деякий час. Мабуть, достатньо тримати їх протягом тижня після їх створення.

Створіть кілька корисних методів у репозиторії коментарів, щоб знайти відхилені коментарі, підрахувати їх і видалити:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
--- a/src/Repository/CommentRepository.php
+++ b/src/Repository/CommentRepository.php
@@ -6,6 +6,7 @@ use App\Entity\Comment;
 use App\Entity\Conference;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
 use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\Tools\Pagination\Paginator;

 /**
@@ -16,6 +17,8 @@ use Doctrine\ORM\Tools\Pagination\Paginator;
  */
 class CommentRepository extends ServiceEntityRepository
 {
+    private const DAYS_BEFORE_REJECTED_REMOVAL = 7;
+
     public const PAGINATOR_PER_PAGE = 2;

     public function __construct(ManagerRegistry $registry)
@@ -23,6 +26,29 @@ class CommentRepository extends ServiceEntityRepository
         parent::__construct($registry, Comment::class);
     }

+    public function countOldRejected(): int
+    {
+        return $this->getOldRejectedQueryBuilder()->select('COUNT(c.id)')->getQuery()->getSingleScalarResult();
+    }
+
+    public function deleteOldRejected(): int
+    {
+        return $this->getOldRejectedQueryBuilder()->delete()->getQuery()->execute();
+    }
+
+    private function getOldRejectedQueryBuilder(): QueryBuilder
+    {
+        return $this->createQueryBuilder('c')
+            ->andWhere('c.state = :state_rejected or c.state = :state_spam')
+            ->andWhere('c.createdAt < :date')
+            ->setParameters([
+                'state_rejected' => 'rejected',
+                'state_spam' => 'spam',
+                'date' => new \DateTimeImmutable(-self::DAYS_BEFORE_REJECTED_REMOVAL.' days'),
+            ])
+        ;
+    }
+
     public function getCommentPaginator(Conference $conference, int $offset): Paginator
     {
         $query = $this->createQueryBuilder('c')

Tip

Для складніших запитів іноді корисно поглянути на згенеровані SQL-вирази (їх можна знайти в журналах і в профілювальнику для веб-запитів).

Використання констант класу, параметрів контейнера та змінних середовища

7 днів? Ми могли б вибрати інше число, можливо, 10 або 20. Це число може змінитися з часом. Ми вирішили зберегти його як константу в класі, але у нас є можливість зберегти його як параметр у контейнері, або навіть визначити його як змінну середовища.

Ось кілька правил, щоб вирішити, яку абстракцію використовувати:

  • Якщо значення є чутливим (паролі, токени API, ...), використовуйте секретне сховище Symfony або Vault;
  • Якщо значення є динамічним, і ви можете змінити його без повторного розгортання, використовуйте змінну середовища;
  • Якщо значення може відрізнятися в різних середовищах, використовуйте параметр контейнера;
  • Для всього іншого зберігайте значення в коді, наприклад, у константі класу.

Створення команди CLI

Видалення старих коментарів є ідеальним завданням для cron. Це слід робити на регулярній основі, а невелика затримка не надає жодного істотного впливу.

Створіть команду CLI з назвою app:comment:cleanup, створивши файл src/Command/CommentCleanupCommand.php:

src/Command/CommentCleanupCommand.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
namespace App\Command;

use App\Repository\CommentRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class CommentCleanupCommand extends Command
{
    private $commentRepository;

    protected static $defaultName = 'app:comment:cleanup';

    public function __construct(CommentRepository $commentRepository)
    {
        $this->commentRepository = $commentRepository;

        parent::__construct();
    }

    protected function configure()
    {
        $this
            ->setDescription('Deletes rejected and spam comments from the database')
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Dry run')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        if ($input->getOption('dry-run')) {
            $io->note('Dry mode enabled');

            $count = $this->commentRepository->countOldRejected();
        } else {
            $count = $this->commentRepository->deleteOldRejected();
        }

        $io->success(sprintf('Deleted "%d" old rejected/spam comments.', $count));

        return 0;
    }
}

Усі команди застосунку реєструються разом із вбудованими командами Symfony, і всі вони доступні за допомогою symfony console. Оскільки кількість доступних команд може бути великою, вам слід групувати їх, використовуючи простори імен. За домовленістю, команди застосунку мають зберігатися в просторі імен app. Додайте будь-яку кількість підпросторів імен, відокремивши їх двокрапкою (:).

Команда отримує input (аргументи й параметри, передані команді), а ви можете використовувати output, щоб писати в консоль.

Очистьте базу даних, виконавши команду:

1
$ symfony console app:comment:cleanup

Налаштування cron у Platform.sh

Однією з приємних особливостей Platform.sh є те, що більша частина конфігурації зберігається в одному файлі: .platform.app.yaml. Веб-контейнер, воркери й завдання cron описані разом, щоб полегшити обслуговування:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--- a/.platform.app.yaml
+++ b/.platform.app.yaml
@@ -60,6 +60,14 @@ crons:
         spec: '50 23 * * *'
         cmd: if [ "$PLATFORM_BRANCH" = "main" ]; then croncape php-security-checker; fi

+    comment_cleanup:
+        # Cleanup every night at 11.50 pm (UTC).
+        spec: '50 23 * * *'
+        cmd: |
+            if [ "$PLATFORM_BRANCH" = "master" ]; then
+                croncape symfony console app:comment:cleanup
+            fi
+
 workers:
     messenger:
         commands:

Секція crons визначає всі завдання cron. Кожне завдання виконується за spec графіком.

Утиліта croncape відстежує виконання команди й відправляє електронний лист на адреси, визначені в змінній середовища MAILTO, якщо команда повертає будь-який код виходу, відмінний від 0.

Налаштуйте змінну середовища MAILTO:

1
$ symfony cloud:variable:create --sensitive=1 --level=project -y --name=env:MAILTO --value=ops@example.com

Зверніть увагу, що завдання cron встановлені у всіх гілках Platform.sh. Якщо ви не хочете запускати деякі з них у не-продакшн середовищах, перевірте змінну середовища $SYMFONY_BRANCH:

1
2
3
if [ "$PLATFORM_BRANCH" = "master" ]; then
    croncape symfony app:invoices:send
fi
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.
TOC
    Version