Skip to content

Commit

Permalink
Merge pull request #10 from IlyasAkbergen/feature/2-entities
Browse files Browse the repository at this point in the history
#2 add Entities, abstract ArticlesProvider and tests
  • Loading branch information
IlyasAkbergen authored Jan 11, 2025
2 parents 21139cb + dc8df9f commit 8daa166
Show file tree
Hide file tree
Showing 22 changed files with 412 additions and 7 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: PHPStan

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: PHPStan
run: make phpstan
18 changes: 18 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: PHPUnit

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Test
run: make test
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ up:
docker compose up -d

test:
docker compose run --no-deps --rm php sh -c "composer install && php /var/www/html/vendor/phpunit/phpunit/phpunit --no-configuration /var/www/html/tests/Unit"
docker compose run --no-deps --rm php sh -c "composer install && php /var/www/html/vendor/phpunit/phpunit/phpunit --no-configuration /var/www/html/tests"

phpstan:
docker compose run --no-deps --rm php sh -c "composer install && php /var/www/html/vendor/bin/phpstan"
24 changes: 24 additions & 0 deletions app/Jobs/FetchArticles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace App\Jobs;

use Domain\Service\ArticlesProvider;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class FetchArticles implements ShouldQueue
{
use Queueable;

public function __construct(
readonly private ArticlesProvider $articlesProvider,
) {
}

public function handle(): void
{
$this->articlesProvider->fetchArticles();
}
}
22 changes: 22 additions & 0 deletions app/Repository/ArticleRepositoryEloquent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Repository;

use Domain\Entity\Article;
use Domain\Repository\ArticleRepositoryInterface;

class ArticleRepositoryEloquent implements ArticleRepositoryInterface
{
public function find(int $id): ?Article
{
// TODO: Implement find() method.
return null;
}

public function save(Article ...$articles): void
{
// TODO: Implement save() method.
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
"Database\\Seeders\\": "database/seeders/",
"Domain\\": "domain/"
}
},
"autoload-dev": {
Expand Down
23 changes: 23 additions & 0 deletions domain/Entity/Article.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Domain\Entity;

use DateTimeImmutable;
use Domain\ValueObject\KeywordsCollection;

readonly class Article
{
public function __construct(
public ?int $id = null,
public string $title,
public string $content,
public Author $author,
public Source $source,
public Category $category,
public DateTimeImmutable $publishedAt,
public KeywordsCollection $keywords,
) {
}
}
16 changes: 16 additions & 0 deletions domain/Entity/Author.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Domain\Entity;

use Domain\ValueObject\FullName;

readonly class Author
{
public function __construct(
public int $id,
public FullName $fullName,
) {
}
}
14 changes: 14 additions & 0 deletions domain/Entity/Category.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Domain\Entity;

readonly class Category
{
public function __construct(
public int $id,
public string $name,
) {
}
}
17 changes: 17 additions & 0 deletions domain/Entity/Source.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Domain\Entity;

use Domain\ValueObject\Url;

readonly class Source
{
public function __construct(
public int $id,
public string $name,
public Url $url,
) {
}
}
9 changes: 9 additions & 0 deletions domain/Exception/DomainException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Domain\Exception;

class DomainException extends \Exception
{
}
14 changes: 14 additions & 0 deletions domain/Repository/ArticleRepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Domain\Repository;

use Domain\Entity\Article;

interface ArticleRepositoryInterface
{
public function find(int $id): ?Article;

public function save(Article ...$articles): void;
}
26 changes: 26 additions & 0 deletions domain/Service/ArticlesProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Domain\Service;

use Domain\Entity\Article;
use Domain\Repository\ArticleRepositoryInterface;

abstract class ArticlesProvider
{
public function __construct(
protected ArticleRepositoryInterface $articleRepository,
) {
}

public function fetchArticles(): void
{
$this->articleRepository->save(...$this->getNewArticles());
}

/**
* @return Article[]
*/
abstract protected function getNewArticles(): array;
}
28 changes: 28 additions & 0 deletions domain/ValueObject/FullName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Domain\ValueObject;

use Domain\Exception\DomainException;

readonly class FullName
{
/**
* @throws DomainException
*/
public function __construct(
public string $firstName,
public string $lastName,
) {
if (empty($this->firstName) || empty($this->lastName)) {
throw new DomainException('First name and last name cannot be empty');
}
}

public function __toString(): string
{
return $this->firstName . ' ' . $this->lastName;
}
}

21 changes: 21 additions & 0 deletions domain/ValueObject/Keyword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Domain\ValueObject;

use Domain\Exception\DomainException;

readonly class Keyword
{
/**
* @throws DomainException
*/
public function __construct(
public string $value,
) {
if (empty($this->value)) {
throw new DomainException('Keyword cannot be empty');
}
}
}
16 changes: 16 additions & 0 deletions domain/ValueObject/KeywordsCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Domain\ValueObject;

readonly class KeywordsCollection
{
public array $keywords;

public function __construct(
Keyword ...$keywords,
) {
$this->keywords = $keywords;
}
}
16 changes: 16 additions & 0 deletions domain/ValueObject/Url.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Domain\ValueObject;

readonly class Url
{
public function __construct(
public string $value,
) {
if (!filter_var($this->value, FILTER_VALIDATE_URL)) {
throw new \InvalidArgumentException('Invalid URL');
}
}
}
11 changes: 6 additions & 5 deletions routes/console.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
declare(strict_types=1);

Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote')->hourly();
use App\Jobs\FetchArticles;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Schedule;

//Schedule::job(new FetchArticles(App::get(NewsAPIArticlesProvider::class)))->everyFiveMinutes();
48 changes: 48 additions & 0 deletions tests/Unit/Domain/Service/ArticlesProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Tests\Unit\Domain\Service;

use Domain\Entity\Article;
use Domain\Repository\ArticleRepositoryInterface;
use Domain\Service\ArticlesProvider;
use PHPUnit\Framework\MockObject\Exception;
use Tests\TestCase;

class ArticlesProviderTest extends TestCase
{
/**
* @throws Exception
*/
public function testFetchArticles(): void
{
$articles = [
self::createMock(Article::class),
self::createMock(Article::class),
];
$articleRepository = self::createMock(ArticleRepositoryInterface::class);
$articleRepository->expects(self::once())
->method('save')
->with(...$articles);

$articlesProvider = new class($articles, $articleRepository) extends ArticlesProvider {
/**
* @param Article[] $articles
*/
public function __construct(
private $articles,
ArticleRepositoryInterface $articleRepository,
) {
parent::__construct($articleRepository);
}

protected function getNewArticles(): array
{
return $this->articles;
}
};

$articlesProvider->fetchArticles();
}
}
Loading

0 comments on commit 8daa166

Please sign in to comment.