Examples for presentation.
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.idea
|
||||||
|
composer.phar
|
||||||
|
vendor
|
||||||
|
.phpunit.result.cache
|
||||||
30
composer.json
Normal file
30
composer.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "kukulis/readable-example",
|
||||||
|
"description": "Example for presentation",
|
||||||
|
"type": "project",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Giedrius Tumelis"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Example\\": "sources/Example/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"psr/event-dispatcher": "^1.0",
|
||||||
|
"symfony/uid": "^5.4",
|
||||||
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
|
"ext-pdo": "*",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
2563
composer.lock
generated
Normal file
2563
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
phpunit.xml
Normal file
12
phpunit.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
defaultTestSuite="Unit" >
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Unit">
|
||||||
|
<directory suffix="Test.php">./tests/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
||||||
57
sources/Example/Bad/APIService.php
Normal file
57
sources/Example/Bad/APIService.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Bad;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
class APIService
|
||||||
|
{
|
||||||
|
private Repository $dbService;
|
||||||
|
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
public function __construct(Repository $dbService, Client $client)
|
||||||
|
{
|
||||||
|
$this->dbService = $dbService;
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApiData(string $filter) : array
|
||||||
|
{
|
||||||
|
$response = $this->client->request('GET', 'api_endpoint/products', [
|
||||||
|
'query' => ['filter'=>$filter]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$json = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
|
||||||
|
return json_decode($json, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(string $filter) : array {
|
||||||
|
|
||||||
|
$data = $this->getApiData($filter);
|
||||||
|
|
||||||
|
$skus = [];
|
||||||
|
foreach ($data as $product) {
|
||||||
|
$skus[] = $product['sku'];
|
||||||
|
}
|
||||||
|
$products = $this->dbService->getProducts($skus);
|
||||||
|
|
||||||
|
/** @var Product[] $productsMap */
|
||||||
|
$productsMap = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$productsMap[$product->sku] = $product;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($data as $apiProduct) {
|
||||||
|
$productsMap[$apiProduct['sku']]->quantity = $apiProduct['amount'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
sources/Example/Bad/CalculatorService.php
Normal file
29
sources/Example/Bad/CalculatorService.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Bad;
|
||||||
|
|
||||||
|
class CalculatorService
|
||||||
|
{
|
||||||
|
private float $vatPercent;
|
||||||
|
private APIService $apiService;
|
||||||
|
|
||||||
|
public function __construct(float $vatPercent, APIService $apiService)
|
||||||
|
{
|
||||||
|
$this->vatPercent = $vatPercent;
|
||||||
|
$this->apiService = $apiService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(string $requestData) : array {
|
||||||
|
$products = $this->apiService->getProducts($requestData);
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$product->price = $product->price * ( 1 + $this->vatPercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
sources/Example/Bad/Controller.php
Normal file
23
sources/Example/Bad/Controller.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Bad;
|
||||||
|
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class Controller
|
||||||
|
{
|
||||||
|
private CalculatorService $calculator;
|
||||||
|
|
||||||
|
public function __construct(CalculatorService $calculator)
|
||||||
|
{
|
||||||
|
$this->calculator = $calculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(RequestInterface $request) : array {
|
||||||
|
$params = $request->getBody();
|
||||||
|
return $this->calculator->getProducts($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
sources/Example/Bad/Product.php
Normal file
30
sources/Example/Bad/Product.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Bad;
|
||||||
|
|
||||||
|
class Product
|
||||||
|
{
|
||||||
|
public string $sku = '';
|
||||||
|
public int $quantity = 0;
|
||||||
|
public float $price = 0;
|
||||||
|
|
||||||
|
public function setSku(string $sku): Product
|
||||||
|
{
|
||||||
|
$this->sku = $sku;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuantity(int $quantity): Product
|
||||||
|
{
|
||||||
|
$this->quantity = $quantity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice(float $price): Product
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
29
sources/Example/Bad/Repository.php
Normal file
29
sources/Example/Bad/Repository.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Bad;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class Repository
|
||||||
|
{
|
||||||
|
private PDO $pdo;
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts($skus) : array{
|
||||||
|
$quotedSkus = [];
|
||||||
|
foreach($skus as $sku){
|
||||||
|
$quotedSkus[] = $this->pdo->quote($sku);
|
||||||
|
}
|
||||||
|
$skusStr = implode(',', $quotedSkus);
|
||||||
|
$sql = "SELECT * FROM products where id in ($skusStr)";
|
||||||
|
|
||||||
|
return $this->pdo->query($sql)->fetchAll(PDO::FETCH_CLASS, Product::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
sources/Example/Good/ApiClient.php
Normal file
38
sources/Example/Good/ApiClient.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Good;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
class ApiClient
|
||||||
|
{
|
||||||
|
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(string $filter): array
|
||||||
|
{
|
||||||
|
$response = $this->client->request('GET', 'api_endpoint/products', [
|
||||||
|
'query' => ['filter' => $filter]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$json = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
|
||||||
|
$products = [];
|
||||||
|
foreach ($data as $productArray) {
|
||||||
|
$products[] = (new Product())->setData($productArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
sources/Example/Good/Controller.php
Normal file
43
sources/Example/Good/Controller.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Good;
|
||||||
|
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class Controller
|
||||||
|
{
|
||||||
|
private ApiClient $apiClient;
|
||||||
|
private Repository $repository;
|
||||||
|
private VATCalculator $vatCalculator;
|
||||||
|
|
||||||
|
public function __construct(ApiClient $apiClient, Repository $repository, VATCalculator $vatCalculator)
|
||||||
|
{
|
||||||
|
$this->apiClient = $apiClient;
|
||||||
|
$this->repository = $repository;
|
||||||
|
$this->vatCalculator = $vatCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(RequestInterface $request) : array {
|
||||||
|
$params = $request->getBody();
|
||||||
|
|
||||||
|
$products = $this->apiClient->getProducts($params);
|
||||||
|
|
||||||
|
$skus = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$skus[] = $product->sku;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbProducts = $this->repository->getProducts($skus);
|
||||||
|
|
||||||
|
Product::copyQuantities($dbProducts, $products);
|
||||||
|
|
||||||
|
$this->vatCalculator->applyVat($dbProducts);
|
||||||
|
|
||||||
|
return $dbProducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
sources/Example/Good/Product.php
Normal file
58
sources/Example/Good/Product.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Good;
|
||||||
|
|
||||||
|
class Product
|
||||||
|
{
|
||||||
|
public string $sku = '';
|
||||||
|
public int $quantity = 0;
|
||||||
|
public float $price = 0;
|
||||||
|
|
||||||
|
public function setSku(string $sku): Product
|
||||||
|
{
|
||||||
|
$this->sku = $sku;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuantity(int $quantity): Product
|
||||||
|
{
|
||||||
|
$this->quantity = $quantity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice(float $price): Product
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instead of this better to use some DAO lib like JMSSerializer, or cuyz/valinor.
|
||||||
|
*/
|
||||||
|
public function setData(array $data): Product {
|
||||||
|
$this->sku = $data['sku'] ?? '';
|
||||||
|
$this->quantity = $data['amount'] ?? 0;
|
||||||
|
$this->price = $data['price'] ?? 0;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Product[] $destinationProducts
|
||||||
|
* @param Product[] $sourceProducts
|
||||||
|
*/
|
||||||
|
public static function copyQuantities(array $destinationProducts, array $sourceProducts ) {
|
||||||
|
$indexedBySku = [];
|
||||||
|
foreach ($destinationProducts as $destinationProduct) {
|
||||||
|
$indexedBySku[$destinationProduct->sku] = $destinationProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($sourceProducts as $sourceProduct) {
|
||||||
|
if ( !array_key_exists($sourceProduct->sku, $indexedBySku) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$indexedBySku[$sourceProduct->sku]->setQuantity($sourceProduct->quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
sources/Example/Good/Repository.php
Normal file
29
sources/Example/Good/Repository.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Good;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class Repository
|
||||||
|
{
|
||||||
|
private PDO $pdo;
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts($skus) : array{
|
||||||
|
$quotedSkus = [];
|
||||||
|
foreach($skus as $sku){
|
||||||
|
$quotedSkus[] = $this->pdo->quote($sku);
|
||||||
|
}
|
||||||
|
$skusStr = implode(',', $quotedSkus);
|
||||||
|
$sql = "SELECT * FROM products where id in ($skusStr)";
|
||||||
|
|
||||||
|
return $this->pdo->query($sql)->fetchAll(PDO::FETCH_CLASS, Product::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
sources/Example/Good/VATCalculator.php
Normal file
25
sources/Example/Good/VATCalculator.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\Good;
|
||||||
|
|
||||||
|
class VATCalculator
|
||||||
|
{
|
||||||
|
private float $vat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float $vat
|
||||||
|
*/
|
||||||
|
public function __construct(float $vat)
|
||||||
|
{
|
||||||
|
$this->vat = $vat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Product[] $products
|
||||||
|
*/
|
||||||
|
public function applyVat(array $products) : void {
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$product->setPrice( $product->price * (1+$this->vat/100) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
sources/Example/VeryGood/ApiClient.php
Normal file
30
sources/Example/VeryGood/ApiClient.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\VeryGood;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
class ApiClient
|
||||||
|
{
|
||||||
|
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts(string $filter): array {
|
||||||
|
$response = $this->client->request('GET', 'api_endpoint/products', [
|
||||||
|
'query' => ['filter'=>$filter]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$json = $response->getBody()->getContents();
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
|
||||||
|
return array_map( fn($element)=> (new Product())->setData($element), $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
sources/Example/VeryGood/Controller.php
Normal file
40
sources/Example/VeryGood/Controller.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\VeryGood;
|
||||||
|
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class Controller
|
||||||
|
{
|
||||||
|
private ApiClient $apiClient;
|
||||||
|
private Repository $repository;
|
||||||
|
|
||||||
|
private float $vat;
|
||||||
|
|
||||||
|
public function __construct(ApiClient $apiClient, Repository $repository, float $vat)
|
||||||
|
{
|
||||||
|
$this->apiClient = $apiClient;
|
||||||
|
$this->repository = $repository;
|
||||||
|
$this->vat = $vat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProducts(RequestInterface $request): array
|
||||||
|
{
|
||||||
|
$params = $request->getBody();
|
||||||
|
|
||||||
|
$apiProducts = $this->apiClient->getProducts($params);
|
||||||
|
|
||||||
|
$skus = array_map(fn($product) => $product->sku, $apiProducts);
|
||||||
|
|
||||||
|
$dbProducts = $this->repository->getProducts($skus);
|
||||||
|
|
||||||
|
Indexer::applyWhenMatch(
|
||||||
|
$dbProducts,
|
||||||
|
$apiProducts,
|
||||||
|
fn($product) => $product->getSku(),
|
||||||
|
fn($product) => $product->getSku(),
|
||||||
|
fn(Product $dbProduct, Product $apiProduct) => $dbProduct->setQuantity($apiProduct->quantity));
|
||||||
|
|
||||||
|
return array_map(fn($product) => $product->applyVat($this->vat), $dbProducts);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
sources/Example/VeryGood/Indexer.php
Normal file
34
sources/Example/VeryGood/Indexer.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\VeryGood;
|
||||||
|
class Indexer
|
||||||
|
{
|
||||||
|
public static function reindex(array $elements, callable $indexGetter)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach ($elements as $element) {
|
||||||
|
$index = call_user_func($indexGetter, $element);
|
||||||
|
$result[$index] = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function applyWhenMatch(
|
||||||
|
array $elements,
|
||||||
|
array $sourceElements,
|
||||||
|
callable $sourceKeyGetter,
|
||||||
|
callable $destinationKeyGetter,
|
||||||
|
callable $action
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$indexedSources = self::reindex($sourceElements, $sourceKeyGetter);
|
||||||
|
foreach ($elements as $element) {
|
||||||
|
$destinationKey = call_user_func($destinationKeyGetter, $element);
|
||||||
|
if (!array_key_exists($destinationKey, $indexedSources)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
call_user_func($action, $element, $indexedSources[$destinationKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
sources/Example/VeryGood/Product.php
Normal file
62
sources/Example/VeryGood/Product.php
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\VeryGood;
|
||||||
|
|
||||||
|
class Product
|
||||||
|
{
|
||||||
|
public string $sku = '';
|
||||||
|
public int $quantity = 0;
|
||||||
|
public float $price = 0;
|
||||||
|
|
||||||
|
public function setSku(string $sku): Product
|
||||||
|
{
|
||||||
|
$this->sku = $sku;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuantity(int $quantity): Product
|
||||||
|
{
|
||||||
|
$this->quantity = $quantity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice(float $price): Product
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instead of this better to use some DAO lib like JMSSerializer, or cuyz/valinor.
|
||||||
|
*/
|
||||||
|
public function setData(array $data): Product
|
||||||
|
{
|
||||||
|
$this->sku = $data['sku'] ?? '';
|
||||||
|
$this->quantity = $data['amount'] ?? 0;
|
||||||
|
$this->price = $data['price'] ?? 0;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyVat(float $vat): Product
|
||||||
|
{
|
||||||
|
$this->price = $this->price * (1 + $vat / 100);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSku(): string
|
||||||
|
{
|
||||||
|
return $this->sku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuantity(): int
|
||||||
|
{
|
||||||
|
return $this->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrice(): float
|
||||||
|
{
|
||||||
|
return $this->price;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
sources/Example/VeryGood/Repository.php
Normal file
30
sources/Example/VeryGood/Repository.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Example\VeryGood;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class Repository
|
||||||
|
{
|
||||||
|
private PDO $pdo;
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Product[]
|
||||||
|
*/
|
||||||
|
public function getProducts($skus): array
|
||||||
|
{
|
||||||
|
$quotedSkus = [];
|
||||||
|
foreach ($skus as $sku) {
|
||||||
|
$quotedSkus[] = $this->pdo->quote($sku);
|
||||||
|
}
|
||||||
|
$skusStr = implode(',', $quotedSkus);
|
||||||
|
$sql = "SELECT * FROM products where id in ($skusStr)";
|
||||||
|
|
||||||
|
return $this->pdo->query($sql)->fetchAll(PDO::FETCH_CLASS, Product::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
tests/Unit/BadTest.php
Normal file
58
tests/Unit/BadTest.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use Example\Bad\APIService;
|
||||||
|
use Example\Bad\CalculatorService;
|
||||||
|
use Example\Bad\Controller;
|
||||||
|
use Example\Bad\Repository;
|
||||||
|
use Example\Bad\Product;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use PDO;
|
||||||
|
use PDOStatement;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class BadTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testBad()
|
||||||
|
{
|
||||||
|
$guzzleClient = $this->createMock(Client::class);
|
||||||
|
|
||||||
|
// make call of controller from the guzzle client
|
||||||
|
$guzzleClient->method('request')->willReturn(
|
||||||
|
new Response(200, [],
|
||||||
|
'[
|
||||||
|
{ "sku" : "x1234", "amount": "10" },
|
||||||
|
{ "sku" : "x1235", "amount": "20" },
|
||||||
|
{ "sku" : "x4567", "amount": "5" }
|
||||||
|
]'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdo = $this->createMock(PDO::class);
|
||||||
|
$pdoResult = $this->createMock(PDOStatement::class);
|
||||||
|
$pdoResult->method('fetchAll')->willReturn([
|
||||||
|
(new Product())->setSku("x1234")->setPrice(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(30),
|
||||||
|
]);
|
||||||
|
$pdo->method('query')->willReturn($pdoResult);
|
||||||
|
|
||||||
|
$dbService = new Repository($pdo);
|
||||||
|
$apiService = new ApiService($dbService, $guzzleClient);
|
||||||
|
$calculatorService = new CalculatorService(0.20, $apiService);
|
||||||
|
$controller = new Controller($calculatorService);
|
||||||
|
|
||||||
|
$products = $controller->getProducts(new Request('get', '/api', [], 'cosmetics'));
|
||||||
|
|
||||||
|
$expectedProducts = [
|
||||||
|
(new Product())->setSku("x1234")->setPrice(12)->setQuantity(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(24)->setQuantity(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(36)->setQuantity(5),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expectedProducts, $products);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
tests/Unit/GoodTest.php
Normal file
63
tests/Unit/GoodTest.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use Example\Good\ApiClient;
|
||||||
|
use Example\Good\Controller;
|
||||||
|
use Example\Good\Product;
|
||||||
|
use Example\Good\Repository;
|
||||||
|
use Example\Good\VATCalculator;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use PDO;
|
||||||
|
use PDOStatement;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class GoodTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGood()
|
||||||
|
{
|
||||||
|
$guzzleClient = $this->createMock(Client::class);
|
||||||
|
|
||||||
|
// make call of controller from the guzzle client
|
||||||
|
$guzzleClient->method('request')->willReturnCallback(
|
||||||
|
function () {
|
||||||
|
return new Response(200, [],
|
||||||
|
'[
|
||||||
|
{ "sku" : "x1234", "amount": "10" },
|
||||||
|
{ "sku" : "x1235", "amount": "20" },
|
||||||
|
{ "sku" : "x4567", "amount": "5" }
|
||||||
|
]'
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdo = $this->createMock(PDO::class);
|
||||||
|
$pdoResult = $this->createMock(PDOStatement::class);
|
||||||
|
$pdoResult->method('fetchAll')->willReturn([
|
||||||
|
(new Product())->setSku("x1234")->setPrice(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(30),
|
||||||
|
]);
|
||||||
|
$pdo->method('query')->willReturn($pdoResult);
|
||||||
|
|
||||||
|
$dbService = new Repository($pdo);
|
||||||
|
$apiService = new ApiClient($guzzleClient);
|
||||||
|
|
||||||
|
$vatCalculator = new VATCalculator(20);
|
||||||
|
|
||||||
|
$controller = new Controller($apiService, $dbService, $vatCalculator);
|
||||||
|
|
||||||
|
$products = $controller->getProducts(new Request('get', '/api', [], 'cosmetics'));
|
||||||
|
|
||||||
|
$expectedProducts = [
|
||||||
|
(new Product())->setSku("x1234")->setPrice(12)->setQuantity(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(24)->setQuantity(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(36)->setQuantity(5),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expectedProducts, $products);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
tests/Unit/VeryGoodTest.php
Normal file
63
tests/Unit/VeryGoodTest.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use Example\VeryGood\ApiClient;
|
||||||
|
use Example\VeryGood\Controller;
|
||||||
|
use Example\VeryGood\Product;
|
||||||
|
use Example\VeryGood\Repository;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use PDO;
|
||||||
|
use PDOStatement;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
class VeryGoodTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testVeryGood2()
|
||||||
|
{
|
||||||
|
$guzzleClient = $this->createMock(Client::class);
|
||||||
|
|
||||||
|
// make call of controller from the guzzle client
|
||||||
|
$guzzleClient->method('request')->willReturnCallback(
|
||||||
|
function () {
|
||||||
|
return new Response(200, [],
|
||||||
|
'[
|
||||||
|
{ "sku" : "x1234", "amount": "10" },
|
||||||
|
{ "sku" : "x1235", "amount": "20" },
|
||||||
|
{ "sku" : "x4567", "amount": "5" }
|
||||||
|
]'
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdo = $this->createMock(PDO::class);
|
||||||
|
$pdoResult = $this->createMock(PDOStatement::class);
|
||||||
|
$pdoResult->method('fetchAll')->willReturn([
|
||||||
|
(new Product())->setSku("x1234")->setPrice(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(30),
|
||||||
|
]);
|
||||||
|
$pdo->method('query')->willReturn($pdoResult);
|
||||||
|
|
||||||
|
$dbService = new Repository($pdo);
|
||||||
|
$apiService = new ApiClient($guzzleClient);
|
||||||
|
|
||||||
|
$controller = new Controller($apiService, $dbService, 20);
|
||||||
|
|
||||||
|
$products = $controller->getProducts(new Request('get', '/api', [], 'cosmetics'));
|
||||||
|
|
||||||
|
$expectedProducts = [
|
||||||
|
(new Product())->setSku("x1234")->setPrice(12)->setQuantity(10),
|
||||||
|
(new Product())->setSku("x1235")->setPrice(24)->setQuantity(20),
|
||||||
|
(new Product())->setSku("x4567")->setPrice(36)->setQuantity(5),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expectedProducts, $products);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user