Выполнение заданий 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
@@ -5,7 +5,9 @@ namespace App\Repository;
use App\Entity\Comment;
use App\Entity\Conference;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator;
/**
@@ -18,6 +20,8 @@ use Doctrine\ORM\Tools\Pagination\Paginator;
*/
class CommentRepository extends ServiceEntityRepository
{
+ private const DAYS_BEFORE_REJECTED_REMOVAL = 7;
+
public const COMMENTS_PER_PAGE = 2;
public function __construct(ManagerRegistry $registry)
@@ -25,6 +29,27 @@ 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')
+ ->setParameter('state_rejected', 'rejected')
+ ->setParameter('state_spam', 'spam')
+ ->setParameter('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
:
Все команды приложения регистрируются вместе со встроенными командами 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
@@ -61,6 +61,14 @@ crons:
spec: '50 23 * * *'
cmd: if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then croncape php-security-checker; fi
+ comment_cleanup:
+ # Cleanup every night at 11.50 pm (UTC).
+ spec: '50 23 * * *'
+ cmd: |
+ if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; 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. Если вы не хотите запускать какие-то задания вне продакшена, проверьте переменную окружения $PLATFORM_ENVIRONMENT_TYPE
:
1 2 3
if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
croncape symfony app:invoices:send
fi