Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.18% covered (danger)
43.18%
19 / 44
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ArticleFetcher
43.18% covered (danger)
43.18%
19 / 44
20.00% covered (danger)
20.00%
1 / 5
33.19
0.00% covered (danger)
0.00%
0 / 1
 getArticlesFromFeed
44.44% covered (danger)
44.44%
4 / 9
0.00% covered (danger)
0.00%
0 / 1
4.54
 getArticlesFromRssFeed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getArticlesFromWebsiteFeed
38.89% covered (danger)
38.89%
7 / 18
0.00% covered (danger)
0.00%
0 / 1
5.05
 fetchArticleData
77.78% covered (warning)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
2.04
 saveArticle
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Services\Article;
4
5use App\Models\Article;
6use App\Models\Feed;
7use App\Services\Http\HttpFetcher;
8use App\Services\Factories\ArticleParserFactory;
9use App\Services\Factories\HomepageParserFactory;
10use App\Services\Log\LogSaver;
11use Exception;
12use Illuminate\Support\Collection;
13
14class ArticleFetcher
15{
16    /**
17     * @return Collection<int, Article>
18     */
19    public static function getArticlesFromFeed(Feed $feed): Collection
20    {
21        if ($feed->type === 'rss') {
22            return self::getArticlesFromRssFeed($feed);
23        } elseif ($feed->type === 'website') {
24            return self::getArticlesFromWebsiteFeed($feed);
25        }
26
27        LogSaver::warning("Unsupported feed type", null, [
28            'feed_id' => $feed->id,
29            'feed_type' => $feed->type
30        ]);
31
32        return collect();
33    }
34
35    /**
36     * @return Collection<int, Article>
37     */
38    private static function getArticlesFromRssFeed(Feed $feed): Collection
39    {
40        // TODO: Implement RSS feed parsing
41        // For now, return empty collection
42        return collect();
43    }
44
45    /**
46     * @return Collection<int, Article>
47     */
48    private static function getArticlesFromWebsiteFeed(Feed $feed): Collection
49    {
50        try {
51            // Try to get parser for this feed
52            $parser = HomepageParserFactory::getParserForFeed($feed);
53
54            if (! $parser) {
55                LogSaver::warning("No parser available for feed URL", null, [
56                    'feed_id' => $feed->id,
57                    'feed_url' => $feed->url
58                ]);
59
60                return collect();
61            }
62
63            $html = HttpFetcher::fetchHtml($feed->url);
64            $urls = $parser->extractArticleUrls($html);
65
66            return collect($urls)
67                ->map(fn (string $url) => self::saveArticle($url, $feed->id));
68
69        } catch (Exception $e) {
70            LogSaver::error("Failed to fetch articles from website feed", null, [
71                'feed_id' => $feed->id,
72                'feed_url' => $feed->url,
73                'error' => $e->getMessage()
74            ]);
75
76            return collect();
77        }
78    }
79
80    /**
81     * @return array<string, mixed>
82     */
83    public static function fetchArticleData(Article $article): array
84    {
85        try {
86            $html = HttpFetcher::fetchHtml($article->url);
87            $parser = ArticleParserFactory::getParser($article->url);
88
89            return $parser->extractData($html);
90        } catch (Exception $e) {
91            LogSaver::error('Exception while fetching article data', null, [
92                'url' => $article->url,
93                'error' => $e->getMessage()
94            ]);
95
96            return [];
97        }
98    }
99
100    private static function saveArticle(string $url, ?int $feedId = null): Article
101    {
102        $existingArticle = Article::where('url', $url)->first();
103
104        if ($existingArticle) {
105            return $existingArticle;
106        }
107
108        return Article::create([
109            'url' => $url,
110            'feed_id' => $feedId
111        ]);
112    }
113}