Psalm fixes. Introduced PHP Standard library
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ApiPlatform\Core\DataPersister
|
||||
{
|
||||
/** @template T */
|
||||
interface DataPersisterInterface
|
||||
{
|
||||
/**
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function supports($data): bool;
|
||||
|
||||
/**
|
||||
* @param T $data
|
||||
* @return T|void
|
||||
*/
|
||||
public function persist($data);
|
||||
|
||||
/**
|
||||
* @param T $data
|
||||
* @return void
|
||||
*/
|
||||
public function remove($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template-extends DataPersisterInterface<T>
|
||||
*/
|
||||
interface ContextAwareDataPersisterInterface extends DataPersisterInterface { }
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM
|
||||
{
|
||||
/** @template T */
|
||||
abstract class AbstractQuery
|
||||
{
|
||||
/** @psalm-return EntityManagerInterface<T> */
|
||||
public function getEntityManager() { }
|
||||
|
||||
/**
|
||||
* @psalm-param string|1|2|3|4|5|null $hydrationMode
|
||||
* @psalm-return T[]
|
||||
*/
|
||||
public function getResult($hydrationMode = self::HYDRATE_OBJECT) { }
|
||||
|
||||
/**
|
||||
* @psalm-param string|1|2|3|4|5|null $hydrationMode
|
||||
* @psalm-return ?T
|
||||
* @throws NonUniqueResultException
|
||||
*/
|
||||
public function getOneOrNullResult($hydrationMode = null) { }
|
||||
|
||||
/**
|
||||
* @psalm-param string|1|2|3|4|5|null $hydrationMode
|
||||
* @psalm-return T
|
||||
* @throws NonUniqueResultException
|
||||
* @throws NoResultException
|
||||
*/
|
||||
public function getSingleResult($hydrationMode = null) { }
|
||||
|
||||
/**
|
||||
* @psalm-param ArrayCollection|array|null $parameters
|
||||
* @psalm-param string|1|2|3|4|5|null $hydrationMode
|
||||
* @psalm-return \Doctrine\ORM\Internal\Hydration\IterableResult<T>
|
||||
*/
|
||||
public function iterate($parameters = null, $hydrationMode = null) { }
|
||||
|
||||
/**
|
||||
* @psalm-param ArrayCollection|array|null $parameters
|
||||
* @psalm-param string|1|2|3|4|5|null $hydrationMode
|
||||
* @psalm-return T|T[]|null
|
||||
*/
|
||||
public function execute($parameters = null, $hydrationMode = null) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template-extends AbstractQuery<T>
|
||||
*/
|
||||
final class Query extends AbstractQuery {}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
/** @psalm-return \Doctrine\ORM\Query<T> */
|
||||
public function getQuery() { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration
|
||||
{
|
||||
/**
|
||||
* @template T
|
||||
* @template-implements \Iterator<T>
|
||||
*/
|
||||
class IterableResult implements \Iterator { }
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Functional;
|
||||
|
||||
/**
|
||||
* @template A
|
||||
*
|
||||
* @psalm-param \Traversable<A>|list<A> $collection
|
||||
* @psalm-param callable(A, mixed=, A[]): bool $callback
|
||||
* @psalm-return list<A>
|
||||
*/
|
||||
function select($collection, callable $callback) {}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
*
|
||||
* @psalm-param \Traversable<B>|list<B> $collection
|
||||
* @psalm-param callable(B, mixed=, B[]): bool $callback
|
||||
* @psalm-return B|null
|
||||
*/
|
||||
function head($collection, callable $callback = null) {}
|
||||
|
||||
/**
|
||||
* @template C
|
||||
* @template D
|
||||
*
|
||||
* @psalm-param \Traversable<C>|list<C> $collection
|
||||
* @psalm-param callable(C, array-key=, C[]=): D $callback
|
||||
* @psalm-return list<D>
|
||||
*/
|
||||
function map($collection, callable $callback) {}
|
||||
|
||||
/**
|
||||
* @template E
|
||||
*
|
||||
* @psalm-param \Traversable<E>|list<C> $collection
|
||||
* @psalm-param callable(E, array-key=, E[]=): void $callback
|
||||
* @psalm-return null
|
||||
* @no-named-arguments
|
||||
*/
|
||||
function each($collection, callable $callback) {}
|
||||
@@ -1,162 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Predis
|
||||
{
|
||||
/**
|
||||
* @template T
|
||||
* @implements \Iterator<T>
|
||||
* @method int del(array $keys)
|
||||
* @method string dump($key)
|
||||
* @method int exists($key)
|
||||
* @method int expire($key, $seconds)
|
||||
* @method int expireat($key, $timestamp)
|
||||
* @method array keys($pattern)
|
||||
* @method int move($key, $db)
|
||||
* @method mixed object($subcommand, $key)
|
||||
* @method int persist($key)
|
||||
* @method int pexpire($key, $milliseconds)
|
||||
* @method int pexpireat($key, $timestamp)
|
||||
* @method int pttl($key)
|
||||
* @method string randomkey()
|
||||
* @method mixed rename($key, $target)
|
||||
* @method int renamenx($key, $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort($key, array $options = null)
|
||||
* @method int ttl($key)
|
||||
* @method mixed type($key)
|
||||
* @method int append($key, $value)
|
||||
* @method int bitcount($key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method int decr($key)
|
||||
* @method int decrby($key, $decrement)
|
||||
* @method string get($key)
|
||||
* @method int getbit($key, $offset)
|
||||
* @method string getrange($key, $start, $end)
|
||||
* @method string getset($key, $value)
|
||||
* @method int incr($key)
|
||||
* @method int incrby($key, $increment)
|
||||
* @method string incrbyfloat($key, $increment)
|
||||
* @method array mget(array $keys)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method mixed psetex($key, $milliseconds, $value)
|
||||
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit($key, $offset, $value)
|
||||
* @method int setex($key, $seconds, $value)
|
||||
* @method int setnx($key, $value)
|
||||
* @method int setrange($key, $offset, $value)
|
||||
* @method int strlen($key)
|
||||
* @method int hdel($key, array $fields)
|
||||
* @method int hexists($key, $field)
|
||||
* @method string hget($key, $field)
|
||||
* @method array hgetall($key)
|
||||
* @method int hincrby($key, $field, $increment)
|
||||
* @method string hincrbyfloat($key, $field, $increment)
|
||||
* @method array hkeys($key)
|
||||
* @method int hlen($key)
|
||||
* @method array hmget($key, array $fields)
|
||||
* @method mixed hmset($key, array $dictionary)
|
||||
* @method array hscan($key, $cursor, array $options = null)
|
||||
* @method int hset($key, $field, $value)
|
||||
* @method int hsetnx($key, $field, $value)
|
||||
* @method array hvals($key)
|
||||
* @method int hstrlen($key, $field)
|
||||
* @method array blpop(array $keys, $timeout)
|
||||
* @method array brpop(array $keys, $timeout)
|
||||
* @method array brpoplpush($source, $destination, $timeout)
|
||||
* @method string lindex($key, $index)
|
||||
* @method int linsert($key, $whence, $pivot, $value)
|
||||
* @method int llen($key)
|
||||
* @method string lpop($key)
|
||||
* @method int lpush($key, array $values)
|
||||
* @method int lpushx($key, $value)
|
||||
* @method array lrange($key, $start, $stop)
|
||||
* @method int lrem($key, $count, $value)
|
||||
* @method mixed lset($key, $index, $value)
|
||||
* @method mixed ltrim($key, $start, $stop)
|
||||
* @method string rpop($key)
|
||||
* @method string rpoplpush($source, $destination)
|
||||
* @method int rpush($key, array $values)
|
||||
* @method int rpushx($key, $value)
|
||||
* @method int sadd($key, array $members)
|
||||
* @method int scard($key)
|
||||
* @method array sdiff(array $keys)
|
||||
* @method int sdiffstore($destination, array $keys)
|
||||
* @method array sinter(array $keys)
|
||||
* @method int sinterstore($destination, array $keys)
|
||||
* @method int sismember($key, $member)
|
||||
* @method array smembers($key)
|
||||
* @method int smove($source, $destination, $member)
|
||||
* @method string spop($key, $count = null)
|
||||
* @method string srandmember($key, $count = null)
|
||||
* @method int srem($key, $member)
|
||||
* @method array sscan($key, $cursor, array $options = null)
|
||||
* @method array sunion(array $keys)
|
||||
* @method int sunionstore($destination, array $keys)
|
||||
* @method int zadd($key, array $membersAndScoresDictionary)
|
||||
* @method int zcard($key)
|
||||
* @method string zcount($key, $min, $max)
|
||||
* @method string zincrby($key, $increment, $member)
|
||||
* @method int zinterstore($destination, array $keys, array $options = null)
|
||||
* @method array zrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrank($key, $member)
|
||||
* @method int zrem($key, $member)
|
||||
* @method int zremrangebyrank($key, $start, $stop)
|
||||
* @method int zremrangebyscore($key, $min, $max)
|
||||
* @method array zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method int zrevrank($key, $member)
|
||||
* @method int zunionstore($destination, array $keys, array $options = null)
|
||||
* @method string zscore($key, $member)
|
||||
* @method array zscan($key, $cursor, array $options = null)
|
||||
* @method array zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method int zremrangebylex($key, $min, $max)
|
||||
* @method int zlexcount($key, $min, $max)
|
||||
* @method int pfadd($key, array $elements)
|
||||
* @method mixed pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method int pfcount(array $keys)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch($key)
|
||||
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth($password)
|
||||
* @method string echo($message)
|
||||
* @method mixed ping($message = null)
|
||||
* @method mixed select($database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof($host, $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd($key, $longitude, $latitude, $member)
|
||||
* @method array geohash($key, array $members)
|
||||
* @method array geopos($key, array $members)
|
||||
* @method string geodist($key, $member1, $member2, $unit = null)
|
||||
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Symfony\Component\Messenger\Stamp
|
||||
{
|
||||
/** @template T */
|
||||
final class HandledStamp
|
||||
{
|
||||
/** @psam-return T */
|
||||
public function getResult() { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Messenger
|
||||
{
|
||||
trait HandleTrait
|
||||
{
|
||||
/**
|
||||
* @template T
|
||||
* @psalm-param object|Envelope $message
|
||||
* @psalm-return T|Envelope
|
||||
*/
|
||||
private function handle($message) { }
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Symfony\Component\Validator
|
||||
{
|
||||
class GroupSequence
|
||||
{
|
||||
}
|
||||
|
||||
interface ConstraintViolationInterface
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends \Traversable<int, ConstraintViolationInterface>
|
||||
* @extends \ArrayAccess<int, ConstraintViolationInterface>
|
||||
*/
|
||||
interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"azjezz/psl": "^2.0",
|
||||
"beberlei/assert": "^3.2",
|
||||
"doctrine/annotations": "^1.0",
|
||||
"doctrine/doctrine-bundle": "^2.4",
|
||||
@@ -53,7 +54,11 @@
|
||||
"fakerphp/faker": "^1.20",
|
||||
"keyvanakbary/mimic": "^1.0",
|
||||
"mockery/mockery": "^1.5",
|
||||
"php-standard-library/psalm-plugin": "^2.0",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"psalm/plugin-mockery": "^0.9.1",
|
||||
"psalm/plugin-phpunit": "^0.17.0",
|
||||
"psalm/plugin-symfony": "^3.1",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"symfony/browser-kit": "6.1.*",
|
||||
"symfony/css-selector": "6.1.*",
|
||||
@@ -63,7 +68,9 @@
|
||||
"symfony/phpunit-bridge": "^6.1",
|
||||
"symfony/stopwatch": "6.1.*",
|
||||
"symfony/web-profiler-bundle": "6.1.*",
|
||||
"theofidry/psysh-bundle": "^4.3"
|
||||
"theofidry/psysh-bundle": "^4.3",
|
||||
"vimeo/psalm": "^4.26",
|
||||
"weirdan/doctrine-psalm-plugin": "^2.3"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": {
|
||||
|
||||
1316
composer.lock
generated
1316
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -18,11 +18,11 @@ services:
|
||||
MYSQL_USER: user
|
||||
MYSQL_PASSWORD: pass
|
||||
MYSQL_DATABASE: db
|
||||
app:
|
||||
build: .docker/php
|
||||
init: true
|
||||
env_file: .env.docker.dist
|
||||
ports:
|
||||
- "8000:80"
|
||||
volumes:
|
||||
- ./:/var/www/html
|
||||
# app:
|
||||
# build: .docker/php
|
||||
# init: true
|
||||
# env_file: .env.docker.dist
|
||||
# ports:
|
||||
# - "8000:80"
|
||||
# volumes:
|
||||
# - ./:/var/www/html
|
||||
24
psalm.xml
24
psalm.xml
@@ -1,26 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="4"
|
||||
resolveFromConfigFile="true"
|
||||
errorLevel="1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<directory name="src"/>
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
<directory name="src/CheeperSpaghetti" />
|
||||
<directory name="src/CheeperCommandHandlers" />
|
||||
<directory name="src/CheeperLayered" />
|
||||
<directory name="vendor"/>
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
|
||||
<stubs>
|
||||
<file name=".psalm-stubs/api-platform.phpstub"/>
|
||||
<file name=".psalm-stubs/doctrine-orm.phpstub"/>
|
||||
<file name=".psalm-stubs/symfony-messenger.phpstub"/>
|
||||
<file name=".psalm-stubs/symfony-validator.phpstub"/>
|
||||
<file name=".psalm-stubs/lstrojny-functional-php.phpstub"/>
|
||||
</stubs>
|
||||
<plugins>
|
||||
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin"/>
|
||||
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
|
||||
<pluginClass class="Weirdan\DoctrinePsalmPlugin\Plugin"/>
|
||||
<pluginClass class="Psl\Psalm\Plugin"/>
|
||||
<pluginClass class="Psalm\MockeryPlugin\Plugin"/>
|
||||
</plugins>
|
||||
</psalm>
|
||||
|
||||
@@ -16,7 +16,8 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use function Safe\json_decode;
|
||||
use Psl\Type;
|
||||
use Psl\Json;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
final class PostAuthorController extends AbstractController
|
||||
@@ -95,7 +96,7 @@ final class PostAuthorController extends AbstractController
|
||||
'birth_date' => new Assert\Optional(new Assert\DateTime(format: "Y-m-d"))
|
||||
]);
|
||||
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$data = Json\typed($request->getContent(), Type\dict(Type\string(), Type\string()));
|
||||
$violations = $this->validator->validate($data, $constraint);
|
||||
|
||||
if (count($violations) > 0) {
|
||||
|
||||
@@ -3,7 +3,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Dto\AuthorDto;
|
||||
use App\Dto\CheepDto;
|
||||
use Cheeper\Application\CheepApplicationService;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
@@ -12,11 +11,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use function Safe\json_decode;
|
||||
use Psl\Json;
|
||||
use Psl\Type;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
final class PostCheepController extends AbstractController
|
||||
@@ -85,7 +84,7 @@ final class PostCheepController extends AbstractController
|
||||
'message' => new Assert\NotBlank()
|
||||
]);
|
||||
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$data = Json\typed($request->getContent(), Type\dict(Type\string(), Type\string()));
|
||||
|
||||
$violations = $this->validator->validate(
|
||||
$data,
|
||||
|
||||
@@ -4,18 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Dto\CheepDto;
|
||||
use Cheeper\Application\FollowApplicationService;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use function Safe\json_decode;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psl\Json;
|
||||
use Psl\Type;
|
||||
|
||||
final class PostFollowersController extends AbstractController
|
||||
{
|
||||
@@ -80,7 +79,7 @@ final class PostFollowersController extends AbstractController
|
||||
'to_author_id' => [new Assert\NotBlank(), new Assert\Uuid()],
|
||||
]);
|
||||
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$data = Json\typed($request->getContent(), Type\dict(Type\string(), Type\string()));
|
||||
$violations = $this->validator->validate($data, $constraints);
|
||||
|
||||
if (count($violations) > 0) {
|
||||
|
||||
@@ -28,6 +28,7 @@ final class AppFixtures extends Fixture
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
/** @return Author[] */
|
||||
private function makeAuthorFixtures(ObjectManager $manager): array
|
||||
{
|
||||
$carlos = Author::signUp(
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Dto;
|
||||
|
||||
use Cheeper\DomainModel\Cheep\Cheep;
|
||||
use DateTimeInterface;
|
||||
use Safe\DateTimeImmutable;
|
||||
|
||||
final class CheepDto
|
||||
{
|
||||
@@ -24,7 +25,7 @@ final class CheepDto
|
||||
$cheep->cheepId()->toString(),
|
||||
$cheep->authorId()->toString(),
|
||||
$cheep->cheepMessage()->message(),
|
||||
\DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $cheep->cheepDate()->date())->format(DateTimeInterface::ATOM)
|
||||
DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $cheep->cheepDate()->date())->format(DateTimeInterface::ATOM)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,9 @@ use Cheeper\DomainModel\Follow\Follow;
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class Author
|
||||
{
|
||||
private function __construct(
|
||||
@@ -35,8 +38,8 @@ class Author
|
||||
?string $location = null,
|
||||
?Website $website = null,
|
||||
?BirthDate $birthDate = null
|
||||
): static {
|
||||
return new static(
|
||||
): self {
|
||||
return new self(
|
||||
$authorId->toString(),
|
||||
$userName->userName(),
|
||||
$email->value(),
|
||||
|
||||
@@ -10,11 +10,11 @@ use DateTimeInterface;
|
||||
interface CheepRepository
|
||||
{
|
||||
public function add(Cheep $cheep): void;
|
||||
public function ofId(CheepId $cheepId): ?Cheep;
|
||||
public function ofId(CheepId $cheepId): Cheep|null;
|
||||
|
||||
/** @return Cheep[] */
|
||||
/** @return list<Cheep> */
|
||||
public function all(): array;
|
||||
|
||||
/** @return Cheep[] */
|
||||
/** @return list<Cheep> */
|
||||
public function ofFollowingPeopleOf(Author $author, int $offset, int $size): array;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\DomainModel\Clock;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
final class DateCollectionClockStrategy implements ClockStrategy
|
||||
{
|
||||
private int $iterator;
|
||||
|
||||
public function __construct(
|
||||
private readonly array $collection = []
|
||||
) {
|
||||
$this->iterator = 0;
|
||||
}
|
||||
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
if (empty($this->collection)) {
|
||||
throw new \InvalidArgumentException('Date collection is empty');
|
||||
}
|
||||
|
||||
$currentDate = $this->collection[$this->iterator];
|
||||
$this->iterator = ($this->iterator + 1) % count($this->collection);
|
||||
|
||||
return $currentDate;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use Cheeper\DomainModel\Cheep\Cheep;
|
||||
use Cheeper\DomainModel\Cheep\CheepId;
|
||||
use Cheeper\DomainModel\Cheep\CheepRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
final class DoctrineOrmCheepRepository implements CheepRepository
|
||||
@@ -45,12 +46,14 @@ WHERE f.fromAuthorId = :fromAuthorId
|
||||
ORDER BY c.cheepDate.date DESC
|
||||
DQL;
|
||||
|
||||
/** @psalm-var Query<Cheep> $query */
|
||||
$query = $this->em->createQuery($dql);
|
||||
$query->setFirstResult($offset);
|
||||
$query->setMaxResults($size);
|
||||
|
||||
return $query->execute([
|
||||
$query->setParameters([
|
||||
'fromAuthorId' => $author->authorId()
|
||||
]);
|
||||
|
||||
return $query->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Infrastructure\Persistence;
|
||||
|
||||
use Cheeper\DomainModel\Author\Author;
|
||||
use Cheeper\DomainModel\Cheep\Cheep;
|
||||
use Cheeper\DomainModel\Cheep\CheepId;
|
||||
use Cheeper\DomainModel\Cheep\CheepRepository;
|
||||
use Psl\Iter;
|
||||
|
||||
final class InMemoryCheepRepository implements CheepRepository
|
||||
{
|
||||
/** @var list<Cheep> */
|
||||
private array $cheeps = [];
|
||||
|
||||
public function add(Cheep $cheep): void
|
||||
{
|
||||
$this->cheeps[] = $cheep;
|
||||
}
|
||||
|
||||
public function ofId(CheepId $cheepId): Cheep|null
|
||||
{
|
||||
return Iter\search($this->cheeps, fn(Cheep $c) => $c->cheepId()->equals($cheepId));
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->cheeps;
|
||||
}
|
||||
|
||||
public function ofFollowingPeopleOf(Author $author, int $offset, int $size): array
|
||||
{
|
||||
return $this->all();
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,11 @@ final class PostCheepControllerTest extends ApiTestCase
|
||||
$autorData = $this->createAuthorWithRandomizedData($client);
|
||||
$cheepData = $this->makeRandomizedCheep($client, $autorData['userName']);
|
||||
|
||||
$client->request(Request::METHOD_GET, "/api/cheeps/${cheepData['id']}");
|
||||
$response = $client->request(Request::METHOD_GET, "/api/cheeps/${cheepData['id']}");
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$data = $client->getResponse()->toArray();
|
||||
$data = $response->toArray();
|
||||
|
||||
$this->assertEquals($cheepData, $data);
|
||||
}
|
||||
|
||||
@@ -5,21 +5,16 @@ declare(strict_types=1);
|
||||
namespace Cheeper\Tests\Application;
|
||||
|
||||
use Cheeper\Application\CheepApplicationService;
|
||||
use Cheeper\DomainModel\Author\Author;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\DomainModel\Author\AuthorId;
|
||||
use Cheeper\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\DomainModel\Author\EmailAddress;
|
||||
use Cheeper\DomainModel\Author\UserName;
|
||||
use Cheeper\DomainModel\Cheep\Cheep;
|
||||
use Cheeper\DomainModel\Cheep\CheepId;
|
||||
use Cheeper\DomainModel\Cheep\CheepMessage;
|
||||
use Cheeper\DomainModel\Cheep\CheepRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryAuthorRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryCheepRepository;
|
||||
use Cheeper\Tests\DomainModel\Author\AuthorTestDataBuilder;
|
||||
use Cheeper\Tests\DomainModel\Cheep\CheepTestDataBuilder;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psl\Iter;
|
||||
|
||||
final class CheepApplicationServiceTest extends TestCase
|
||||
{
|
||||
@@ -29,7 +24,7 @@ final class CheepApplicationServiceTest extends TestCase
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->cheepRepository = Mockery::mock(CheepRepository::class);
|
||||
$this->cheepRepository = new InMemoryCheepRepository();
|
||||
$this->authorRepository = new InMemoryAuthorRepository();
|
||||
$this->cheepService = new CheepApplicationService($this->authorRepository, $this->cheepRepository);
|
||||
}
|
||||
@@ -49,11 +44,8 @@ final class CheepApplicationServiceTest extends TestCase
|
||||
AuthorTestDataBuilder::anAuthor()->build()
|
||||
);
|
||||
|
||||
$this->cheepRepository->expects('add');
|
||||
|
||||
$cheep = $this->cheepService->postCheep('irrelevant', 'message');
|
||||
|
||||
$this->assertNotNull($cheep);
|
||||
$this->assertNotNull($cheep->authorId());
|
||||
$this->assertEquals('message', $cheep->cheepMessage()->message());
|
||||
}
|
||||
@@ -74,12 +66,12 @@ final class CheepApplicationServiceTest extends TestCase
|
||||
$this->authorRepository->add($author);
|
||||
|
||||
$cheeps = [
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test1"),
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test2"),
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test3"),
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test1")->build(),
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test2")->build(),
|
||||
CheepTestDataBuilder::aCheep()->withAMessage("test3")->build(),
|
||||
];
|
||||
|
||||
$this->cheepRepository->allows()->ofFollowingPeopleOf($author, 0, 10)->andReturn($cheeps);
|
||||
Iter\apply($cheeps, fn(Cheep $c) => $this->cheepRepository->add($c));
|
||||
|
||||
$this->assertCount(
|
||||
count($cheeps),
|
||||
|
||||
Reference in New Issue
Block a user