diff --git a/app/config/packages/doctrine.yaml b/app/config/packages/doctrine.yaml index 37e3d2b52..ed0a96953 100644 --- a/app/config/packages/doctrine.yaml +++ b/app/config/packages/doctrine.yaml @@ -51,6 +51,11 @@ doctrine: is_bundle: false dir: '%kernel.project_dir%/../sources/AppBundle/Veille/Entity' prefix: 'AppBundle\Veille\Entity' + PlanetePHP: + type: attribute + is_bundle: false + dir: '%kernel.project_dir%/../sources/PlanetePHP' + prefix: 'PlanetePHP' when@test: doctrine: dbal: diff --git a/app/config/reference.php b/app/config/reference.php index 7193b06b3..5dab12460 100644 --- a/app/config/reference.php +++ b/app/config/reference.php @@ -1014,6 +1014,7 @@ * }>, * autoescape_service?: scalar|Param|null, // Default: null * autoescape_service_method?: scalar|Param|null, // Default: null + * base_template_class?: scalar|Param|null, // Deprecated: The child node "base_template_class" at path "twig.base_template_class" is deprecated. * cache?: scalar|Param|null, // Default: true * charset?: scalar|Param|null, // Default: "%kernel.charset%" * debug?: bool|Param, // Default: "%kernel.debug%" diff --git a/db/migrations/20260609141713_cleanup_user_id_in_planete_flux.php b/db/migrations/20260609141713_cleanup_user_id_in_planete_flux.php new file mode 100644 index 000000000..9126e64d2 --- /dev/null +++ b/db/migrations/20260609141713_cleanup_user_id_in_planete_flux.php @@ -0,0 +1,17 @@ +query(<<createForm(FeedFormType::class, $data); + $feed = new Feed(); + $feed->url = 'https://'; + $feed->feed = 'https://'; + $feed->status = FeedStatus::Active; + $form = $this->createForm(FeedFormType::class, $feed); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { try { - $this->feedRepository->insert( - $data->name, - $data->url, - $data->feed, - $data->status, - $data->userId, - ); - - $this->audit->log('Ajout du flux ' . $data->name); + $this->feedRepository->save($feed); + + $this->audit->log('Ajout du flux ' . $feed->name); $this->addFlash('notice', 'Le flux a été ajouté'); return $this->redirectToRoute('admin_planete_feed_list'); diff --git a/sources/AppBundle/Controller/Admin/Planete/FeedArticleListAction.php b/sources/AppBundle/Controller/Admin/Planete/FeedArticleListAction.php index 3c7a726bf..82799b7c7 100644 --- a/sources/AppBundle/Controller/Admin/Planete/FeedArticleListAction.php +++ b/sources/AppBundle/Controller/Admin/Planete/FeedArticleListAction.php @@ -4,7 +4,7 @@ namespace AppBundle\Controller\Admin\Planete; -use PlanetePHP\FeedArticleRepository; +use PlanetePHP\ArticleRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Twig\Environment; @@ -12,7 +12,7 @@ final readonly class FeedArticleListAction { public function __construct( - private FeedArticleRepository $feedArticleRepository, + private ArticleRepository $articleRepository, private Environment $twig, ) {} @@ -22,7 +22,7 @@ public function __invoke(Request $request): Response $direction = $request->query->getString('direction', 'asc'); return new Response($this->twig->render('admin/planete/feed_article_list.html.twig', [ - 'articles' => $this->feedArticleRepository->search($sort, $direction, 20), + 'articles' => $this->articleRepository->search($sort, $direction, 20), 'sort' => $sort, 'direction' => $direction, ])); diff --git a/sources/AppBundle/Controller/Admin/Planete/FeedDeleteAction.php b/sources/AppBundle/Controller/Admin/Planete/FeedDeleteAction.php index 342cae5eb..34bc93fd7 100644 --- a/sources/AppBundle/Controller/Admin/Planete/FeedDeleteAction.php +++ b/sources/AppBundle/Controller/Admin/Planete/FeedDeleteAction.php @@ -22,7 +22,10 @@ public function __invoke(Request $request): RedirectResponse $id = $request->query->getInt('id'); try { - $this->feedRepository->delete($id); + $feed = $this->feedRepository->find($id); + if ($feed !== null) { + $this->feedRepository->delete($feed); + } $this->audit->log('Suppression du flux ' . $id); $this->addFlash('notice', 'Le flux a été supprimé'); diff --git a/sources/AppBundle/Controller/Admin/Planete/FeedEditAction.php b/sources/AppBundle/Controller/Admin/Planete/FeedEditAction.php index dcb1effeb..7e17c9920 100644 --- a/sources/AppBundle/Controller/Admin/Planete/FeedEditAction.php +++ b/sources/AppBundle/Controller/Admin/Planete/FeedEditAction.php @@ -5,7 +5,6 @@ namespace AppBundle\Controller\Admin\Planete; use AppBundle\AuditLog\Audit; -use AppBundle\Planete\FeedFormData; use AppBundle\Planete\FeedFormType; use Exception; use PlanetePHP\FeedRepository; @@ -23,27 +22,17 @@ public function __construct( public function __invoke(Request $request): Response { $id = $request->query->getInt('id'); - $feed = $this->feedRepository->get($id); - $data = new FeedFormData(); - $data->name = $feed->name; - $data->feed = $feed->feed; - $data->url = $feed->url; - $data->userId = $feed->userId; - $data->status = $feed->status; - $form = $this->createForm(FeedFormType::class, $data); + $feed = $this->feedRepository->find($id); + if ($feed === null) { + throw $this->createNotFoundException(); + } + $form = $this->createForm(FeedFormType::class, $feed); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { try { - $this->feedRepository->update( - $id, - $data->name, - $data->url, - $data->feed, - $data->status, - $data->userId, - ); - - $this->audit->log(sprintf("Modification du flux %s (%d)", $data->name, $id)); + $this->feedRepository->save($feed); + + $this->audit->log(sprintf("Modification du flux %s (%d)", $feed->name, $id)); $this->addFlash('notice', 'Le flux a été modifié'); return $this->redirectToRoute('admin_planete_feed_list'); diff --git a/sources/AppBundle/Controller/Admin/Planete/FeedListAction.php b/sources/AppBundle/Controller/Admin/Planete/FeedListAction.php index adadbbf3a..6efc79802 100644 --- a/sources/AppBundle/Controller/Admin/Planete/FeedListAction.php +++ b/sources/AppBundle/Controller/Admin/Planete/FeedListAction.php @@ -23,7 +23,7 @@ public function __construct( public function __invoke(Request $request): Response { $testFeeds = $request->query->getBoolean('testFeeds'); - $feeds = $this->feedRepository->find(); + $feeds = $this->feedRepository->findAllOrderedByName(); return new Response($this->twig->render('admin/planete/feed_list.html.twig', [ 'feeds' => $feeds, diff --git a/sources/AppBundle/Controller/Planete/ArticlesController.php b/sources/AppBundle/Controller/Planete/ArticlesController.php index 8c01ec08d..7b517656e 100644 --- a/sources/AppBundle/Controller/Planete/ArticlesController.php +++ b/sources/AppBundle/Controller/Planete/ArticlesController.php @@ -4,14 +4,14 @@ namespace AppBundle\Controller\Planete; -use PlanetePHP\DisplayableFeedArticle; -use PlanetePHP\FeedArticleRepository; +use PlanetePHP\Article; +use PlanetePHP\ArticleRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; final readonly class ArticlesController { - public function __construct(private FeedArticleRepository $feedArticleRepository) {} + public function __construct(private ArticleRepository $articleRepository) {} public function __invoke(Request $request): Response { @@ -22,8 +22,8 @@ public function __invoke(Request $request): Response $page = 1; } - $totalCount = $this->feedArticleRepository->countRelevant(); - $articles = $this->feedArticleRepository->findLatest($page - 1, DATE_RSS, $perPage); + $totalCount = $this->articleRepository->countRelevant(); + $articles = $this->articleRepository->findLatest($page - 1, $perPage); $data = []; @@ -31,12 +31,12 @@ public function __invoke(Request $request): Response $data[] = [ 'title' => $article->title, 'url' => $this->getArticleUrl($article), - 'date' => $article->update, + 'date' => $article->updatedAt?->format(DATE_RSS), 'author' => $article->author, 'content' => $article->content, 'feed' => [ - 'name' => $article->feedName, - 'url' => $article->feedUrl, + 'name' => $article->feed?->name, + 'url' => $article->feed?->url, ], ]; } @@ -53,7 +53,7 @@ public function __invoke(Request $request): Response ); } - private function getArticleUrl(DisplayableFeedArticle $article): string + private function getArticleUrl(Article $article): string { $url = $article->url; @@ -62,7 +62,7 @@ private function getArticleUrl(DisplayableFeedArticle $article): string } if (!str_starts_with($url, 'http')) { - $feedUrl = rtrim((string) $article->feedUrl, '/'); + $feedUrl = rtrim((string) $article->feed?->url, '/'); $articleUrl = ltrim($url, '/'); return implode('/', [$feedUrl, $articleUrl]); diff --git a/sources/AppBundle/Planete/FeedFormData.php b/sources/AppBundle/Planete/FeedFormData.php deleted file mode 100644 index 3b0c996d7..000000000 --- a/sources/AppBundle/Planete/FeedFormData.php +++ /dev/null @@ -1,27 +0,0 @@ - 30, 'maxlength' => 40, ], + 'constraints' => [ + new Assert\NotBlank(), + ], ]) ->add('url', UrlType::class, [ 'label' => 'URL', @@ -40,6 +44,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'size' => 50, 'maxlength' => 200, ], + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Url(), + ], ]) ->add('feed', UrlType::class, [ 'label' => 'Flux', @@ -48,6 +56,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'size' => 50, 'maxlength' => 200, ], + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Url(), + ], ]) ->add('userId', ChoiceType::class, [ 'label' => 'Personne physique', @@ -58,6 +70,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'État', 'required' => true, 'class' => FeedStatus::class, + 'constraints' => [ + new Assert\NotNull(), + ], ]) ->add('save', SubmitType::class, ['label' => 'Ajouter']); } diff --git a/sources/PlanetePHP/Article.php b/sources/PlanetePHP/Article.php new file mode 100644 index 000000000..80628dc5a --- /dev/null +++ b/sources/PlanetePHP/Article.php @@ -0,0 +1,46 @@ + + */ +final class ArticleRepository extends EntityRepository +{ + private const string PERTINENCE_REGEX = '/php|afup|pear|pecl|symfony|copix|jelix|wampserver|simpletest|simplexml|zend|pmo|drupal|ovidentia|mvc|magento|chrome|spip|PDO|mock|cake|hiphop|CMS|Framework|typo3|photon|pattern/i'; + + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Article::class); + } + + public function countRelevant(): int + { + return $this->count([ + 'isRelevant' => true, + ]); + } + + /** + * @return Article[] + */ + public function search(string $sort, string $direction, int $limit = 20): array + { + $sorts = [ + 'title' => 'a.title', + 'content' => 'a.content', + 'status' => 'a.isRelevant', + ]; + Assert::keyExists($sorts, $sort); + Assert::inArray($direction, ['asc', 'desc']); + + return $this->createQueryBuilder('a') + ->orderBy($sorts[$sort], $direction) + ->setMaxResults($limit) + ->getQuery() + ->getResult(); + } + + /** + * @return Article[] + */ + public function findLatest(int $page = 0, int $nombre = 10): array + { + return $this->createQueryBuilder('a') + ->innerJoin('a.feed', 'f') + ->addSelect('f') + ->where('a.isRelevant = :isRelevant') + ->setParameter('isRelevant', true) + ->orderBy('a.updatedAt', 'DESC') + ->setFirstResult($page * $nombre) + ->setMaxResults($nombre) + ->getQuery() + ->getResult(); + } + + public function saveByKey(Article $article): void + { + $existing = $this->findOneBy(['key' => $article->key]); + + if ($existing === null) { + $this->save($article); + + return; + } + + $existing->feed = $article->feed; + $existing->title = $article->title; + $existing->url = $article->url; + $existing->updatedAt = $article->updatedAt; + $existing->author = $article->author; + $existing->summary = $article->summary; + $existing->content = $article->content; + $existing->isRelevant = $article->isRelevant; + + $this->save($existing); + } + + public function isRelevant(?string $content): bool + { + $content = strip_tags((string) $content); + + return preg_match(self::PERTINENCE_REGEX, $content) === 1; + } +} diff --git a/sources/PlanetePHP/DisplayableFeedArticle.php b/sources/PlanetePHP/DisplayableFeedArticle.php deleted file mode 100644 index d5fb9bdf3..000000000 --- a/sources/PlanetePHP/DisplayableFeedArticle.php +++ /dev/null @@ -1,18 +0,0 @@ -pertinenceRegex = '/' . self::PERTINENCE_LIST . '/i'; - } - - public function count(): int - { - $query = $this->connection->prepare('SELECT COUNT(b.id) FROM afup_planete_billet b'); - - return intval($query->executeQuery()->fetchOne()); - } - - public function countRelevant(): int - { - $query = $this->connection->prepare('SELECT COUNT(b.id) FROM afup_planete_billet b WHERE b.etat = :status'); - $query->bindValue('status', self::RELEVANT); - - return intval($query->executeQuery()->fetchOne()); - } - - /** - * @return FeedArticle[] - */ - public function search(string $sort, string $direction, int $limit = 20): array - { - $sorts = [ - 'title' => 'b.titre', - 'content' => 'b.contenu', - 'status' => 'b.etat', - ]; - Assert::keyExists($sorts, $sort); - Assert::inArray($direction, ['asc', 'desc']); - $qb = $this->connection->createQueryBuilder(); - $qb->from('afup_planete_billet', 'b') - ->select('b.*') - ->orderBy($sorts[$sort], $direction) - ->setMaxResults($limit); - - return $this->hydrate($qb->executeQuery()->fetchAllAssociative()); - } - - public function save(FeedArticle $billet) - { - $id = $this->findIdByKey($billet->key); - if (null !== $id) { - return $this->update($billet, $id); - } - - return $this->insert($billet); - } - - /** - * @return array - */ - public function findLatest($page = 0, $format = DATE_ATOM, $nombre = 10): array - { - $query = $this->connection->prepare('SELECT b.titre, b.url, b.maj, b.auteur, b.contenu, f.nom feed_name, f.url feed_url - FROM afup_planete_billet b - INNER JOIN afup_planete_flux f on b.afup_planete_flux_id = f.id - WHERE b.etat = :status - ORDER BY b.maj DESC - LIMIT :start, :length - '); - $query->bindValue('status', self::RELEVANT); - $query->bindValue('start', $page * $nombre, ParameterType::INTEGER); - $query->bindValue('length', $nombre, ParameterType::INTEGER); - - return $this->hydrateDisplayable($query->executeQuery()->fetchAllAssociative(), $format); - } - - public function isRelevant($content): int - { - $content = strip_tags((string) $content); - $relevant = self::IRRELEVANT; - if (preg_match($this->pertinenceRegex, $content)) { - $relevant = self::RELEVANT; - } - - return $relevant; - } - - private function findIdByKey($key) - { - $query = $this->connection->prepare('SELECT id FROM afup_planete_billet WHERE clef = :key'); - $query->bindValue('key', $key); - $row = $query->executeQuery()->fetchAssociative(); - - return is_array($row) ? $row['id'] : null; - } - - private function update(FeedArticle $billet, $id = null) - { - $statement = $this->connection->prepare('UPDATE afup_planete_billet - SET afup_planete_flux_id = :feedId, - clef= :key, - titre = :title, - url = :url, - maj = :update, - auteur = :author, - resume = :summary, - contenu = :content, - etat = :status - WHERE id = :id'); - - $statement->bindValue('feedId', $billet->feedId); - $statement->bindValue('key', $billet->key); - $statement->bindValue('title', $billet->title); - $statement->bindValue('url', $billet->url); - $statement->bindValue('update', $billet->update); - $statement->bindValue('author', $billet->author); - $statement->bindValue('summary', $billet->summary); - $statement->bindValue('content', $billet->content); - $statement->bindValue('status', $billet->status); - - $statement->bindValue('id', $id ?: $billet->id); - - return $statement->executeStatement(); - } - - private function insert(FeedArticle $billet) - { - $statement = $this->connection->prepare('INSERT INTO afup_planete_billet - (afup_planete_flux_id, clef, titre, url, maj, auteur, resume, contenu, etat) - VALUES (:feedId, :key, :title, :url, :update, :author, :summary, :content, :status)'); - - $statement->bindValue('feedId', $billet->feedId); - $statement->bindValue('key', $billet->key); - $statement->bindValue('title', $billet->title); - $statement->bindValue('url', $billet->url); - $statement->bindValue('update', $billet->update); - $statement->bindValue('author', $billet->author); - $statement->bindValue('summary', $billet->summary); - $statement->bindValue('content', $billet->content); - $statement->bindValue('status', $billet->status); - - return $statement->executeStatement(); - } - - private function hydrate(array $rows): array - { - return array_map(static fn(array $row): FeedArticle => new FeedArticle( - $row['id'], - $row['afup_planete_flux_id'], - $row['clef'], - $row['titre'], - $row['url'], - $row['maj'], - $row['auteur'], - $row['resume'], - $row['contenu'], - $row['etat'], - ), $rows); - } - - /** - * @return array - */ - private function hydrateDisplayable(array $rows, $format = DATE_ATOM): array - { - return array_map(static fn(array $row): DisplayableFeedArticle => new DisplayableFeedArticle( - $row['titre'], - $row['url'], - date($format, (int) $row['maj']), - $row['auteur'], - $row['contenu'], - $row['feed_name'], - $row['feed_url'], - ), $rows); - } -} diff --git a/sources/PlanetePHP/FeedCrawler.php b/sources/PlanetePHP/FeedCrawler.php index 16e0d3b88..bc6dc344b 100644 --- a/sources/PlanetePHP/FeedCrawler.php +++ b/sources/PlanetePHP/FeedCrawler.php @@ -17,7 +17,7 @@ public function __construct( private ClockInterface $clock, private SymfonyFeedClient $httpClient, private FeedRepository $feedRepository, - private FeedArticleRepository $feedArticleRepository, + private ArticleRepository $articleRepository, private LoggerInterface $logger, ) { Reader::setHttpClient($this->httpClient); @@ -62,20 +62,23 @@ public function crawl(): CrawlingResult $author = $author['name'] ?? null; } - $article = new FeedArticle( - null, - $feed->id, - $item->getId(), - $item->getTitle(), - $item->getLink(), - $date->getTimestamp(), - $author, - $item->getDescription(), - $item->getContent(), - $this->feedArticleRepository->isRelevant($item->getTitle() . ' ' . $item->getContent()), - ); - - $this->feedArticleRepository->save($article); + // On normalise la date dans le fuseau horaire de l'application + // pour que l'affichage reste cohérent quel que soit le fuseau du flux + $updatedAt = \DateTime::createFromInterface($date); + $updatedAt->setTimezone(new \DateTimeZone(date_default_timezone_get())); + + $article = new Article(); + $article->feed = $feed; + $article->key = $item->getId(); + $article->title = $item->getTitle(); + $article->url = $item->getLink(); + $article->updatedAt = $updatedAt; + $article->author = $author; + $article->summary = $item->getDescription(); + $article->content = $item->getContent(); + $article->isRelevant = $this->articleRepository->isRelevant($item->getTitle() . ' ' . $item->getContent()); + + $this->articleRepository->saveByKey($article); $saved++; } diff --git a/sources/PlanetePHP/FeedRepository.php b/sources/PlanetePHP/FeedRepository.php index 7fa3563ed..c023dc2c2 100644 --- a/sources/PlanetePHP/FeedRepository.php +++ b/sources/PlanetePHP/FeedRepository.php @@ -4,94 +4,40 @@ namespace PlanetePHP; -use Doctrine\DBAL\Connection; +use AppBundle\Doctrine\EntityRepository; +use Doctrine\Persistence\ManagerRegistry; -final readonly class FeedRepository +/** + * @extends EntityRepository + */ +final class FeedRepository extends EntityRepository { - public function __construct(private Connection $connection) {} + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Feed::class); + } /** * @return Feed[] */ public function findActive(): array { - $query = $this->connection->prepare('SELECT id, nom, url, feed, etat, id_personne_physique - FROM afup_planete_flux f WHERE f.etat = :status ORDER BY f.nom'); - $query->bindValue('status', FeedStatus::Active->value); - - return $this->hydrateAll($query->executeQuery()->fetchAllAssociative()); + return $this->createQueryBuilder('f') + ->where('f.status = :status') + ->setParameter('status', FeedStatus::Active) + ->orderBy('f.name', 'ASC') + ->getQuery() + ->getResult(); } /** * @return Feed[] */ - public function find(): array - { - $qb = $this->connection->createQueryBuilder() - ->select('f.id', 'f.nom', 'f.url', 'f.feed', 'f.etat', 'f.id_personne_physique') - ->from('afup_planete_flux', 'f') - ->orderBy('f.nom', 'asc'); - - return $this->hydrateAll($qb->executeQuery()->fetchAllAssociative()); - } - - public function get(int $id): Feed - { - $query = $this->connection->prepare('SELECT id, nom, url, feed, etat, id_personne_physique - FROM afup_planete_flux f WHERE f.id = :id'); - $query->bindValue('id', $id); - - return $this->hydrate($query->executeQuery()->fetchAssociative()); - } - - public function insert(string $name, string $url, string $feed, FeedStatus $status, ?int $userId = 0): void - { - $statement = $this->connection->prepare('INSERT INTO afup_planete_flux (nom, url, feed, etat, id_personne_physique) VALUES (:name, :url, :feed, :status, :userId)'); - - $statement->bindValue('name', $name); - $statement->bindValue('url', $url); - $statement->bindValue('feed', $feed); - $statement->bindValue('status', $status->value); - $statement->bindValue('userId', (int) $userId); - - $statement->executeStatement(); - } - - public function update(int $id, string $name, string $url, string $feed, FeedStatus $status, ?int $userId = 0): void - { - $statement = $this->connection->prepare('UPDATE afup_planete_flux - SET nom = :name, url = :url, feed = :feed, etat = :status, id_personne_physique = :userId - WHERE id = :id'); - - $statement->bindValue('name', $name); - $statement->bindValue('url', $url); - $statement->bindValue('feed', $feed); - $statement->bindValue('status', $status->value); - $statement->bindValue('userId', (int) $userId); - $statement->bindValue('id', $id); - - $statement->executeStatement(); - } - - public function delete(int $id): void - { - $this->connection->delete('afup_planete_flux', ['id' => $id]); - } - - private function hydrateAll(array $rows): array - { - return array_map($this->hydrate(...), $rows); - } - - private function hydrate(array $row): Feed + public function findAllOrderedByName(): array { - return new Feed( - $row['id'], - $row['nom'], - $row['url'], - $row['feed'], - FeedStatus::from($row['etat']), - $row['id_personne_physique'], - ); + return $this->createQueryBuilder('f') + ->orderBy('f.name', 'ASC') + ->getQuery() + ->getResult(); } } diff --git a/templates/admin/planete/feed_article_list.html.twig b/templates/admin/planete/feed_article_list.html.twig index b1b94b9a5..22fcab344 100644 --- a/templates/admin/planete/feed_article_list.html.twig +++ b/templates/admin/planete/feed_article_list.html.twig @@ -26,10 +26,10 @@ {% for article in articles %} - + {{ article.title }} {{ article.content|striptags }} - {{ article.status == 1 ? 'Actif' : 'Inactif (non lié à PHP)' }} + {{ article.isRelevant ? 'Actif' : 'Inactif (non lié à PHP)' }} {% else %} diff --git a/templates/admin/planete/feed_list.html.twig b/templates/admin/planete/feed_list.html.twig index d64788760..9f0f83d39 100644 --- a/templates/admin/planete/feed_list.html.twig +++ b/templates/admin/planete/feed_list.html.twig @@ -32,7 +32,7 @@ {% for feed in feeds %} - + {{ feed.name }} get(FeedArticleRepository::class); + $articlesRepository = self::getContainer()->get(ArticleRepository::class); $feedRepository = self::getContainer()->get(FeedRepository::class); // L'auto-incrément démarre à 148 dans le script de création de la bdd - $feedRepository->insert('fake rss', 'https://rss.fake', 'https://rss.fake', FeedStatus::Active); - $feedRepository->insert('fake atom', 'https://atom.fake', 'https://atom.fake', FeedStatus::Active); - $feedRepository->insert('fake invalid', 'https://invalid.fake', 'https://invalid.fake', FeedStatus::Active); - $feedRepository->insert('fake html', 'https://html.fake', 'https://html.fake', FeedStatus::Active); - $feedRepository->insert('fake error 500', 'https://error500.fake', 'https://error500.fake', FeedStatus::Active); - $feedRepository->insert('fake error 404', 'https://error404.fake', 'https://error404.fake', FeedStatus::Active); - $feedRepository->insert('fake empty', 'https://empty.fake', 'https://empty.fake', FeedStatus::Active); + $this->createFeed($feedRepository, 'fake rss', 'https://rss.fake'); + $this->createFeed($feedRepository, 'fake atom', 'https://atom.fake'); + $this->createFeed($feedRepository, 'fake invalid', 'https://invalid.fake'); + $this->createFeed($feedRepository, 'fake html', 'https://html.fake'); + $this->createFeed($feedRepository, 'fake error 500', 'https://error500.fake'); + $this->createFeed($feedRepository, 'fake error 404', 'https://error404.fake'); + $this->createFeed($feedRepository, 'fake empty', 'https://empty.fake'); $client = new MockHttpClient(function (string $method, string $url): MockResponse { if ($url === 'https://atom.fake/') { @@ -81,28 +82,38 @@ public function testCrawlFeed(): void self::assertCount(3, $articles); - self::assertInstanceOf(DisplayableFeedArticle::class, $articles[0]); - self::assertEquals('fake rss', $articles[0]->feedName); + self::assertInstanceOf(Article::class, $articles[0]); + self::assertEquals('fake rss', $articles[0]->feed->name); self::assertEquals("Lorem Ipsum 🐘", $articles[0]->title); self::assertEquals("https://rss.fake/news/123-lorem-ipsum", $articles[0]->url); self::assertNull($articles[0]->author); - self::assertEquals('2025-03-17T07:15:12+01:00', $articles[0]->update); + self::assertEquals('2025-03-17T07:15:12+01:00', $articles[0]->updatedAt->format(DATE_ATOM)); self::assertEquals("

Test php

\n

salut

", $articles[0]->content); - self::assertInstanceOf(DisplayableFeedArticle::class, $articles[1]); - self::assertEquals('fake atom', $articles[1]->feedName); + self::assertInstanceOf(Article::class, $articles[1]); + self::assertEquals('fake atom', $articles[1]->feed->name); self::assertEquals("Lorem Ipsum", $articles[1]->title); self::assertEquals("https://atom.fake/lorem-ipsum", $articles[1]->url); self::assertEquals('Ada Lovelace', $articles[1]->author); - self::assertEquals('2025-03-16T14:15:00+01:00', $articles[1]->update); + self::assertEquals('2025-03-16T14:15:00+01:00', $articles[1]->updatedAt->format(DATE_ATOM)); self::assertEquals('

Contenu PHP

', $articles[1]->content); - self::assertInstanceOf(DisplayableFeedArticle::class, $articles[2]); - self::assertEquals('fake rss', $articles[2]->feedName); + self::assertInstanceOf(Article::class, $articles[2]); + self::assertEquals('fake rss', $articles[2]->feed->name); self::assertEquals("Autre article", $articles[2]->title); self::assertEquals("https://rss.fake/news/456-autre-article", $articles[2]->url); self::assertNull($articles[2]->author); - self::assertEquals('2025-03-14T22:26:02+01:00', $articles[2]->update); + self::assertEquals('2025-03-14T22:26:02+01:00', $articles[2]->updatedAt->format(DATE_ATOM)); self::assertEquals('

contenu php

', $articles[2]->content); } + + private function createFeed(FeedRepository $feedRepository, string $name, string $url): void + { + $feed = new Feed(); + $feed->name = $name; + $feed->url = $url; + $feed->feed = $url; + $feed->status = FeedStatus::Active; + $feedRepository->save($feed); + } }