Compare commits
28 commits
master
...
feature_po
Author | SHA1 | Date | |
---|---|---|---|
49d4097a47 | |||
3f31705536 | |||
6b5a60b2a5 | |||
b0a6fbfb7f | |||
a12bf9d9a2 | |||
dbc1c060f8 | |||
b1d047941c | |||
4d7549bddd | |||
ac0d905ce4 | |||
16489b509e | |||
728464005e | |||
4ce5fe0ccb | |||
300dbcb466 | |||
d528b45436 | |||
f2b423ad8c | |||
4707b41f27 | |||
05aaa1d4e1 | |||
26ee4522fc | |||
63b27dc312 | |||
f5a4e5b896 | |||
4eb7b418db | |||
5897555302 | |||
7902f9b3ea | |||
f8dfe4e103 | |||
46b327b455 | |||
31d49eb270 | |||
5d2ce0fe42 | |||
e82ff0d2a1 |
|
@ -19,6 +19,7 @@ class AppKernel extends Kernel
|
||||||
new Symfony\Bundle\WebServerBundle\WebServerBundle(),
|
new Symfony\Bundle\WebServerBundle\WebServerBundle(),
|
||||||
new JMS\SerializerBundle\JMSSerializerBundle(),
|
new JMS\SerializerBundle\JMSSerializerBundle(),
|
||||||
new Csa\Bundle\GuzzleBundle\CsaGuzzleBundle(),
|
new Csa\Bundle\GuzzleBundle\CsaGuzzleBundle(),
|
||||||
|
new Leezy\PheanstalkBundle\LeezyPheanstalkBundle(),
|
||||||
new Ob\HighchartsBundle\ObHighchartsBundle(),
|
new Ob\HighchartsBundle\ObHighchartsBundle(),
|
||||||
new Knp\Bundle\MarkdownBundle\KnpMarkdownBundle(),
|
new Knp\Bundle\MarkdownBundle\KnpMarkdownBundle(),
|
||||||
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
|
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
|
||||||
|
|
51
app/DoctrineMigrations/Version20180427143940.php
Normal file
51
app/DoctrineMigrations/Version20180427143940.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Application\Migrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issue #44 - Post and Comment schema refactoring.
|
||||||
|
* - Post subscription status added.
|
||||||
|
* - Comments parent-child relations removed.
|
||||||
|
* - User login index
|
||||||
|
* - Other adjustments
|
||||||
|
*/
|
||||||
|
class Version20180427143940 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(Schema $schema)
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||||
|
|
||||||
|
$this->addSql('ALTER TABLE posts.posts ADD is_subscribed BOOLEAN DEFAULT FALSE NOT NULL');
|
||||||
|
// Removing parent_id constraint and index
|
||||||
|
$this->addSql('ALTER TABLE posts.comments DROP CONSTRAINT fk_62899975727aca70');
|
||||||
|
$this->addSql('DROP INDEX posts.idx_62899975727aca70');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments DROP parent_id');
|
||||||
|
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ADD to_number INT');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ALTER number TYPE INT');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ALTER number DROP DEFAULT');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_6289997596901F54 ON posts.comments (number)');
|
||||||
|
|
||||||
|
$this->addSql('CREATE INDEX idx_user_login ON users.users (login)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema)
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||||
|
|
||||||
|
$this->addSql('ALTER TABLE posts.posts DROP is_subscribed');
|
||||||
|
$this->addSql('DROP INDEX posts.UNIQ_6289997596901F54');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ADD parent_id INT DEFAULT NULL');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments DROP to_number');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ALTER number TYPE SMALLINT');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ALTER number DROP DEFAULT');
|
||||||
|
$this->addSql('ALTER TABLE posts.comments ADD CONSTRAINT fk_62899975727aca70 FOREIGN KEY (parent_id) REFERENCES posts.comments (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('CREATE INDEX idx_62899975727aca70 ON posts.comments (parent_id)');
|
||||||
|
$this->addSql('DROP INDEX users.idx_user_login');
|
||||||
|
}
|
||||||
|
}
|
28
app/DoctrineMigrations/Version20190227233244.php
Normal file
28
app/DoctrineMigrations/Version20190227233244.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Application\Migrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20190227233244 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(Schema $schema) : void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||||
|
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX unique_post_id_comment_number ON posts.comments (post_id, number)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema) : void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||||
|
|
||||||
|
$this->addSql('DROP INDEX posts.unique_post_id_comment_number');
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,6 +71,13 @@ doctrine_migrations:
|
||||||
table_name: migration_versions
|
table_name: migration_versions
|
||||||
name: Application Migrations
|
name: Application Migrations
|
||||||
|
|
||||||
|
leezy_pheanstalk:
|
||||||
|
pheanstalks:
|
||||||
|
primary:
|
||||||
|
server: "%beanstalkd_host%"
|
||||||
|
port: "%beanstalkd_port%"
|
||||||
|
default: true
|
||||||
|
|
||||||
# Swiftmailer Configuration
|
# Swiftmailer Configuration
|
||||||
swiftmailer:
|
swiftmailer:
|
||||||
transport: "%mailer_transport%"
|
transport: "%mailer_transport%"
|
||||||
|
|
|
@ -6,6 +6,11 @@ parameters:
|
||||||
database_user: point
|
database_user: point
|
||||||
database_password: ~
|
database_password: ~
|
||||||
|
|
||||||
|
# Message Queue settings
|
||||||
|
beanstalkd_host: 'localhost'
|
||||||
|
beanstalkd_port: 11300
|
||||||
|
beanstalkd_ws_updates_tube: 'point-websocket-updates'
|
||||||
|
|
||||||
mailer_transport: smtp
|
mailer_transport: smtp
|
||||||
mailer_host: 127.0.0.1
|
mailer_host: 127.0.0.1
|
||||||
mailer_user: ~
|
mailer_user: ~
|
||||||
|
|
|
@ -9,4 +9,4 @@ security:
|
||||||
security: false
|
security: false
|
||||||
|
|
||||||
default:
|
default:
|
||||||
anonymous: true
|
anonymous: ~
|
||||||
|
|
|
@ -101,6 +101,13 @@ services:
|
||||||
# Send message
|
# Send message
|
||||||
Skobkin\Bundle\PointToolsBundle\Command\TelegramSendMessageCommand:
|
Skobkin\Bundle\PointToolsBundle\Command\TelegramSendMessageCommand:
|
||||||
tags: [{ name: console.command }]
|
tags: [{ name: console.command }]
|
||||||
|
# WebSocket MQ processing
|
||||||
|
Skobkin\Bundle\PointToolsBundle\Command\ProcessWebsocketUpdatesCommand:
|
||||||
|
arguments:
|
||||||
|
$bsClient: '@leezy.pheanstalk.primary'
|
||||||
|
$bsTubeName: '%beanstalkd_ws_updates_tube%'
|
||||||
|
tags:
|
||||||
|
- { name: console.command }
|
||||||
|
|
||||||
|
|
||||||
# Entity repositories as services
|
# Entity repositories as services
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1.0",
|
"php": ">=7.1.0",
|
||||||
"ext-json": "*",
|
|
||||||
"symfony/symfony": "^3.4",
|
"symfony/symfony": "^3.4",
|
||||||
"doctrine/orm": "^2.5",
|
"doctrine/orm": "^2.5",
|
||||||
"doctrine/annotations": "^1.3.0",
|
"doctrine/annotations": "^1.3.0",
|
||||||
|
@ -34,7 +33,8 @@
|
||||||
"unreal4u/telegram-api": "^2.2",
|
"unreal4u/telegram-api": "^2.2",
|
||||||
"csa/guzzle-bundle": "^3",
|
"csa/guzzle-bundle": "^3",
|
||||||
"symfony/web-server-bundle": "^3.3",
|
"symfony/web-server-bundle": "^3.3",
|
||||||
"sentry/sentry-symfony": "^2.2"
|
"sentry/sentry-symfony": "^2.2",
|
||||||
|
"leezy/pheanstalk-bundle": "^3.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/phpunit-bridge": "^3.0",
|
"symfony/phpunit-bridge": "^3.0",
|
||||||
|
|
1904
composer.lock
generated
1904
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,114 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\Command;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use JMS\Serializer\Serializer;
|
||||||
|
use Leezy\PheanstalkBundle\Proxy\PheanstalkProxy;
|
||||||
|
use Pheanstalk\Job;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Exception\WebSocket\UnsupportedTypeException;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Service\WebSocket\WebSocketMessageProcessor;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\{InputInterface, InputOption};
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command processes WebSocket updates MQ and stores new content in the DB
|
||||||
|
*/
|
||||||
|
class ProcessWebsocketUpdatesCommand extends Command
|
||||||
|
{
|
||||||
|
/** @var EntityManagerInterface */
|
||||||
|
private $em;
|
||||||
|
|
||||||
|
/** @var PheanstalkProxy */
|
||||||
|
private $bsClient;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $bsTubeName;
|
||||||
|
|
||||||
|
/** @var Serializer */
|
||||||
|
private $serializer;
|
||||||
|
|
||||||
|
/** @var WebSocketMessageProcessor */
|
||||||
|
private $messageProcessor;
|
||||||
|
|
||||||
|
/** @var \Raven_Client */
|
||||||
|
private $sentryClient;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EntityManagerInterface $em,
|
||||||
|
\Raven_Client $raven,
|
||||||
|
PheanstalkProxy $bsClient,
|
||||||
|
string $bsTubeName,
|
||||||
|
Serializer $serializer,
|
||||||
|
WebSocketMessageProcessor $processor
|
||||||
|
) {
|
||||||
|
$this->em = $em;
|
||||||
|
$this->sentryClient = $raven;
|
||||||
|
$this->serializer = $serializer;
|
||||||
|
$this->messageProcessor = $processor;
|
||||||
|
$this->bsClient = $bsClient;
|
||||||
|
$this->bsTubeName = $bsTubeName;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('point:update:websocket-messages')
|
||||||
|
->setDescription('Reads and processes updates from Beanstalkd queue pipe')
|
||||||
|
->addOption('keep-jobs', 'k', InputOption::VALUE_NONE, 'Don\'t delete jobs from queue after processing')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$keepJobs = (bool) $input->getOption('keep-jobs');
|
||||||
|
|
||||||
|
/** @var Job $job */
|
||||||
|
while ($job = $this->bsClient->reserveFromTube($this->bsTubeName, 0)) {
|
||||||
|
try {
|
||||||
|
/** @var Message $message */
|
||||||
|
$message = $this->serializer->deserialize($job->getData(), Message::class, 'json');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln(sprintf(
|
||||||
|
'Error while deserializing #%d data: \'%s\'',
|
||||||
|
$job->getId(),
|
||||||
|
$job->getData()
|
||||||
|
));
|
||||||
|
$this->sentryClient->captureException($e);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->writeln('Processing job #'.$job->getId().' ('.$message->getA().')');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($this->messageProcessor->processMessage($message)) {
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
if (!$keepJobs) {
|
||||||
|
$this->bsClient->delete($job);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->em->clear();
|
||||||
|
}
|
||||||
|
} catch (UnsupportedTypeException $e) {
|
||||||
|
$output->writeln(' Unsupported message type: '.$message->getA());
|
||||||
|
$this->sentryClient->captureException($e);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln(' Message processing error: '.$e->getMessage());
|
||||||
|
$this->sentryClient->captureException($e);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,19 +12,11 @@ class PostController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ParamConverter("post", class="SkobkinPointToolsBundle:Blogs\Post")
|
* @ParamConverter("post", class="SkobkinPointToolsBundle:Blogs\Post")
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function showAction(Post $post, PostRepository $postRepository): Response
|
public function showAction(Post $post, PostRepository $postRepository): Response
|
||||||
{
|
{
|
||||||
if ((!$post->getAuthor()->isPublic()) || $post->getAuthor()->isWhitelistOnly()) {
|
|
||||||
/**
|
|
||||||
* Throwing 404 instead of 403 because of
|
|
||||||
* @see \Symfony\Component\Security\Http\Firewall\ExceptionListener::handleAccessDeniedException()
|
|
||||||
* starts to replace 403 by 401 exceptions for anonymous users and tries to authenticate them.
|
|
||||||
*/
|
|
||||||
throw $this->createNotFoundException('Author\'s blog is private.');
|
|
||||||
//throw $this->createAccessDeniedException('Author\'s blog is private.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->render('SkobkinPointToolsBundle:Post:show.html.twig', [
|
return $this->render('SkobkinPointToolsBundle:Post:show.html.twig', [
|
||||||
'post' => $postRepository->getPostWithComments($post->getId()),
|
'post' => $postRepository->getPostWithComments($post->getId()),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -4,39 +4,22 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
||||||
|
|
||||||
class Comment implements ValidableInterface
|
class Comment implements ValidableInterface
|
||||||
{
|
{
|
||||||
/**
|
/** @var int|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $postId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $number;
|
private $number;
|
||||||
|
|
||||||
/**
|
/** @var int|null */
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $toCommentId;
|
private $toCommentId;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $created;
|
private $created;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $text;
|
private $text;
|
||||||
|
|
||||||
/**
|
/** @var User|null */
|
||||||
* @var User|null
|
|
||||||
*/
|
|
||||||
private $author;
|
private $author;
|
||||||
|
|
||||||
/**
|
/** @var bool|null */
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
private $isRec;
|
private $isRec;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,27 +4,17 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
||||||
|
|
||||||
class MetaPost implements ValidableInterface
|
class MetaPost implements ValidableInterface
|
||||||
{
|
{
|
||||||
/**
|
/** @var Post|null */
|
||||||
* @var Post|null
|
|
||||||
*/
|
|
||||||
private $post;
|
private $post;
|
||||||
|
|
||||||
/**
|
/** @var Comment[]|null */
|
||||||
* @var Comment[]|null
|
|
||||||
*/
|
|
||||||
private $comments;
|
private $comments;
|
||||||
|
|
||||||
|
|
||||||
public function getPost(): ?Post
|
public function getPost(): ?Post
|
||||||
{
|
{
|
||||||
return $this->post;
|
return $this->post;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPost(?Post $post): void
|
|
||||||
{
|
|
||||||
$this->post = $post;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Comment[]|null
|
* @return Comment[]|null
|
||||||
*/
|
*/
|
||||||
|
@ -33,20 +23,8 @@ class MetaPost implements ValidableInterface
|
||||||
return $this->comments;
|
return $this->comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Comment[]|null $comments
|
|
||||||
*/
|
|
||||||
public function setComments(?array $comments): void
|
|
||||||
{
|
|
||||||
$this->comments = $comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isValid(): bool
|
public function isValid(): bool
|
||||||
{
|
{
|
||||||
if (null !== $this->post && $this->post->isValid()) {
|
return (null !== $this->post && $this->post->isValid());
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,44 +4,28 @@ namespace Skobkin\Bundle\PointToolsBundle\DTO\Api;
|
||||||
|
|
||||||
class Post implements ValidableInterface
|
class Post implements ValidableInterface
|
||||||
{
|
{
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
/**
|
/** @var string[]|null */
|
||||||
* @var string[]|null
|
|
||||||
*/
|
|
||||||
private $tags;
|
private $tags;
|
||||||
|
|
||||||
/**
|
/** @var string[]|null */
|
||||||
* @var string[]|null
|
|
||||||
*/
|
|
||||||
private $files;
|
private $files;
|
||||||
|
|
||||||
/**
|
/** @var User|null */
|
||||||
* @var User|null
|
|
||||||
*/
|
|
||||||
private $author;
|
private $author;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $text;
|
private $text;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $created;
|
private $created;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
/**
|
/** @var bool|null */
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
private $private;
|
private $private;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,330 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket;
|
||||||
|
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\ValidableInterface;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Exception\WebSocket\UnsupportedTypeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket update message
|
||||||
|
*/
|
||||||
|
class Message implements ValidableInterface
|
||||||
|
{
|
||||||
|
public const TYPE_COMMENT = 'comment';
|
||||||
|
public const TYPE_COMMENT_EDITED = 'comment_edited';
|
||||||
|
public const TYPE_POST = 'post';
|
||||||
|
public const TYPE_POST_EDITED = 'post_edited';
|
||||||
|
public const TYPE_POST_COMMENT_RECOMMENDATION = 'rec';
|
||||||
|
public const TYPE_RECOMMENDATION_WITH_COMMENT = 'ok';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event type. @see Message::TYPE_* constants
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login of the user
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $author;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $authorId;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $authorName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of the comment in the thread
|
||||||
|
*
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
private $commentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ???
|
||||||
|
*
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private $cut;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of file paths
|
||||||
|
*
|
||||||
|
* @var string[]|null
|
||||||
|
*/
|
||||||
|
private $files;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Link in the Post::type=feed posts
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
private $postAuthorId;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $postId;
|
||||||
|
|
||||||
|
/** @var bool|null */
|
||||||
|
private $private;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of the comment in the thread for recommendation with text
|
||||||
|
*
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
private $rcid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of tags
|
||||||
|
*
|
||||||
|
* @var string[]|null
|
||||||
|
*/
|
||||||
|
private $tags;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated ???
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of the comment to which this comment is answering
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $toCommentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text quotation of the comment to which this comment is answering
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $toText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of logins of users to which post is addressed
|
||||||
|
*
|
||||||
|
* @var string[]|null
|
||||||
|
*/
|
||||||
|
private $toUsers;
|
||||||
|
|
||||||
|
public function isPost(): bool
|
||||||
|
{
|
||||||
|
return self::TYPE_POST === $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isComment(): bool
|
||||||
|
{
|
||||||
|
return self::TYPE_COMMENT === $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCommentRecommendation(): bool
|
||||||
|
{
|
||||||
|
return self::TYPE_RECOMMENDATION_WITH_COMMENT === $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPostRecommendation(): bool
|
||||||
|
{
|
||||||
|
return self::TYPE_POST_COMMENT_RECOMMENDATION === $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \RuntimeException
|
||||||
|
* @throws UnsupportedTypeException
|
||||||
|
*/
|
||||||
|
public function isValid(): bool
|
||||||
|
{
|
||||||
|
switch ($this->a) {
|
||||||
|
case self::TYPE_POST:
|
||||||
|
return $this->isValidPost();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_POST_EDITED:
|
||||||
|
return $this->isValidPostEdited();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_COMMENT;
|
||||||
|
return $this->isValidComment();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_COMMENT_EDITED;
|
||||||
|
return $this->isValidCommentEdited();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_RECOMMENDATION_WITH_COMMENT;
|
||||||
|
return $this->isValidRecommendationWithComment();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_POST_COMMENT_RECOMMENDATION;
|
||||||
|
return $this->isValidPostCommentRecommendation();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case null:
|
||||||
|
throw new \RuntimeException('Message has NULL type.');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnsupportedTypeException(sprintf('Type \'%s\' is not supported.', $this->a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getA(): string
|
||||||
|
{
|
||||||
|
return $this->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthor(): string
|
||||||
|
{
|
||||||
|
return $this->author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthorId(): int
|
||||||
|
{
|
||||||
|
return $this->authorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthorName(): ?string
|
||||||
|
{
|
||||||
|
return $this->authorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommentId(): ?int
|
||||||
|
{
|
||||||
|
return $this->commentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCut(): ?bool
|
||||||
|
{
|
||||||
|
return $this->cut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFiles(): ?array
|
||||||
|
{
|
||||||
|
return $this->files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHtml(): ?string
|
||||||
|
{
|
||||||
|
return $this->html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLink(): ?string
|
||||||
|
{
|
||||||
|
return $this->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPostId(): string
|
||||||
|
{
|
||||||
|
return $this->postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPostAuthorId(): ?int
|
||||||
|
{
|
||||||
|
return $this->postAuthorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivate(): ?bool
|
||||||
|
{
|
||||||
|
return $this->private;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRcid(): ?int
|
||||||
|
{
|
||||||
|
return $this->rcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTags(): ?array
|
||||||
|
{
|
||||||
|
return $this->tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getText(): string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): ?string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToCommentId(): ?string
|
||||||
|
{
|
||||||
|
return $this->toCommentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToText(): ?string
|
||||||
|
{
|
||||||
|
return $this->toText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToUsers(): ?array
|
||||||
|
{
|
||||||
|
return $this->toUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasCommonMandatoryData(): bool
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
null !== $this->author &&
|
||||||
|
null !== $this->authorId &&
|
||||||
|
null !== $this->postId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidPost(): bool
|
||||||
|
{
|
||||||
|
return $this->hasCommonMandatoryData() && (
|
||||||
|
// Text can be empty ("") though
|
||||||
|
null !== $this->text &&
|
||||||
|
null !== $this->tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidPostEdited(): bool
|
||||||
|
{
|
||||||
|
return $this->isValidPost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidComment(): bool
|
||||||
|
{
|
||||||
|
return $this->hasCommonMandatoryData() && (
|
||||||
|
null !== $this->commentId &&
|
||||||
|
null !== $this->html &&
|
||||||
|
null !== $this->text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidCommentEdited(): bool
|
||||||
|
{
|
||||||
|
return $this->hasCommonMandatoryData() && (null !== $this->commentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidRecommendationWithComment(): bool
|
||||||
|
{
|
||||||
|
return $this->hasCommonMandatoryData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isValidPostCommentRecommendation(): bool
|
||||||
|
{
|
||||||
|
return $this->hasCommonMandatoryData() && (null !== $this->postAuthorId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
|
|
||||||
class LoadCommentsData extends AbstractFixture implements OrderedFixtureInterface
|
class LoadCommentsData extends AbstractFixture implements OrderedFixtureInterface
|
||||||
{
|
{
|
||||||
public function load(ObjectManager $om)
|
public function load(ObjectManager $om): void
|
||||||
{
|
{
|
||||||
/** @var Post $post */
|
/** @var Post $post */
|
||||||
$post = $this->getReference('test_post_longpost');
|
$post = $this->getReference('test_post_longpost');
|
||||||
|
@ -25,29 +25,28 @@ class LoadCommentsData extends AbstractFixture implements OrderedFixtureInterfac
|
||||||
$this->getReference('test_user_99995'),
|
$this->getReference('test_user_99995'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$comments = [];
|
$text = 'Some text with [link to @skobkin-ru site](https://skobk.in/) and `code block`'.PHP_EOL.
|
||||||
|
|
||||||
foreach (range(1, 10000) as $num) {
|
|
||||||
$comment = (new Comment())
|
|
||||||
->setNumber($num)
|
|
||||||
->setDeleted(mt_rand(0, 15) ? false : true)
|
|
||||||
->setCreatedAt(new \DateTime())
|
|
||||||
->setAuthor($users[array_rand($users)])
|
|
||||||
->setRec(false)
|
|
||||||
->setText(
|
|
||||||
'Some text with [link to @skobkin-ru site](https://skobk.in/) and `code block`'.PHP_EOL.
|
|
||||||
'and some quotation:'.PHP_EOL.
|
'and some quotation:'.PHP_EOL.
|
||||||
'> test test quote'.PHP_EOL.
|
'> test test quote'.PHP_EOL.
|
||||||
'and some text after'
|
'and some text after';
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
if (count($comments) > 0 && mt_rand(0, 1)) {
|
foreach (range(1, 10000) as $num) {
|
||||||
$comment->setParent($comments[mt_rand(0, count($comments) - 1)]);
|
$comment = new Comment(
|
||||||
|
$text,
|
||||||
|
new \DateTime(),
|
||||||
|
false,
|
||||||
|
$post,
|
||||||
|
$num,
|
||||||
|
($num > 1 && !random_int(0, 4)) ? random_int(1, $num - 1) : null,
|
||||||
|
$users[array_rand($users)],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!random_int(0, 15)) {
|
||||||
|
$comment->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
$post->addComment($comment);
|
$post->addComment($comment);
|
||||||
$comments[] = $comment;
|
|
||||||
|
|
||||||
$om->persist($comment);
|
$om->persist($comment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,64 +2,33 @@
|
||||||
|
|
||||||
namespace Skobkin\Bundle\PointToolsBundle\DataFixtures\ORM;
|
namespace Skobkin\Bundle\PointToolsBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
use Doctrine\Common\DataFixtures\{AbstractFixture, OrderedFixtureInterface};
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
use Doctrine\Common\Persistence\ObjectManager;
|
use Doctrine\Common\Persistence\ObjectManager;
|
||||||
use Skobkin\Bundle\PointToolsBundle\Entity\{Blogs\Post, User};
|
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
|
|
||||||
class LoadPostData extends AbstractFixture implements OrderedFixtureInterface
|
class LoadPostData extends AbstractFixture implements OrderedFixtureInterface
|
||||||
{
|
{
|
||||||
public const POST_ID_LONG = 'longpost';
|
|
||||||
public const POST_ID_SHORT = 'shortpost';
|
|
||||||
public const POST_ID_PR_USER = 'prusrpst';
|
|
||||||
public const POST_ID_WL_USER = 'wlusrpst';
|
|
||||||
public const POST_ID_PR_WL_USER = 'prwlusrpst';
|
|
||||||
|
|
||||||
public function load(ObjectManager $om)
|
public function load(ObjectManager $om)
|
||||||
{
|
{
|
||||||
/** @var User $mainUser */
|
/** @var User $testUser */
|
||||||
$mainUser = $this->getReference('test_user_'.LoadUserData::USER_MAIN_ID);
|
$testUser = $this->getReference('test_user_99999');
|
||||||
/** @var User $privateUser */
|
|
||||||
$privateUser = $this->getReference('test_user_'.LoadUserData::USER_PRIV_ID);
|
|
||||||
/** @var User $wlUser */
|
|
||||||
$wlUser = $this->getReference('test_user_'.LoadUserData::USER_WLON_ID);
|
|
||||||
/** @var User $prWlUser */
|
|
||||||
$prWlUser = $this->getReference('test_user_'.LoadUserData::USER_PRWL_ID);
|
|
||||||
|
|
||||||
$longPost = (new Post(self::POST_ID_LONG, $mainUser, new \DateTime(), Post::TYPE_POST))
|
$longPost = (new Post('longpost', $testUser, new \DateTime(), Post::TYPE_POST))
|
||||||
->setText('Test post with many comments')
|
->setText('Test post with many comments')
|
||||||
->setPrivate(false)
|
->setPrivate(false)
|
||||||
->setDeleted(false)
|
->setDeleted(false)
|
||||||
;
|
;
|
||||||
|
|
||||||
$shortPost = (new Post(self::POST_ID_SHORT, $mainUser, new \DateTime(), Post::TYPE_POST))
|
$shortPost = (new Post('shortpost', $testUser, new \DateTime(), Post::TYPE_POST))
|
||||||
->setText('Test short post')
|
->setText('Test short post')
|
||||||
->setPrivate(false)
|
->setPrivate(false)
|
||||||
->setDeleted(false)
|
->setDeleted(false)
|
||||||
;
|
;
|
||||||
|
|
||||||
$privateUserPost = (new Post(self::POST_ID_PR_USER, $privateUser, new \DateTime(), Post::TYPE_POST))
|
|
||||||
->setText('Post from private user. Should not be visible in the public feed.')
|
|
||||||
->setPrivate(false)
|
|
||||||
->setDeleted(false)
|
|
||||||
;
|
|
||||||
|
|
||||||
$wlUserPost = (new Post(self::POST_ID_WL_USER, $wlUser, new \DateTime(), Post::TYPE_POST))
|
|
||||||
->setText('Post from whitelist-only user. Should only be visible for whitelisted users.')
|
|
||||||
->setPrivate(false)
|
|
||||||
->setDeleted(false)
|
|
||||||
;
|
|
||||||
|
|
||||||
$privateWlUserPost = (new Post(self::POST_ID_PR_WL_USER, $prWlUser, new \DateTime(), Post::TYPE_POST))
|
|
||||||
->setText('Post from private AND whitelist-only user. Should not be visible in the public feed.')
|
|
||||||
->setPrivate(false)
|
|
||||||
->setDeleted(false)
|
|
||||||
;
|
|
||||||
|
|
||||||
$om->persist($longPost);
|
$om->persist($longPost);
|
||||||
$om->persist($shortPost);
|
$om->persist($shortPost);
|
||||||
$om->persist($privateUserPost);
|
|
||||||
$om->persist($wlUserPost);
|
|
||||||
$om->persist($privateWlUserPost);
|
|
||||||
$om->flush();
|
$om->flush();
|
||||||
|
|
||||||
$this->addReference('test_post_longpost', $longPost);
|
$this->addReference('test_post_longpost', $longPost);
|
||||||
|
|
|
@ -9,27 +9,25 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
|
|
||||||
class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
|
class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
|
||||||
{
|
{
|
||||||
public const USER_MAIN_ID = 99999;
|
|
||||||
public const USER_SCND_ID = 99998;
|
|
||||||
public const USER_PRIV_ID = 99997;
|
|
||||||
public const USER_WLON_ID = 99996;
|
|
||||||
public const USER_PRWL_ID = 99995;
|
|
||||||
public const USER_UNNM_ID = 99994;
|
|
||||||
|
|
||||||
private $users = [
|
private $users = [
|
||||||
['id' => self::USER_MAIN_ID, 'login' => 'testuser', 'name' => 'Test User 1', 'private' => false, 'whitelist-only' => false],
|
// 99999
|
||||||
['id' => self::USER_SCND_ID, 'login' => 'testuser2', 'name' => 'Test User 2 for autocomplete test', 'private' => false, 'whitelist-only' => false],
|
['login' => 'testuser', 'name' => 'Test User 1'],
|
||||||
['id' => self::USER_PRIV_ID, 'login' => 'private_user', 'name' => 'Test User 3', 'private' => true, 'whitelist-only' => false],
|
// 99998
|
||||||
['id' => self::USER_WLON_ID, 'login' => 'whitelist_only_user', 'name' => 'Test User 4', 'private' => false, 'whitelist-only' => true],
|
['login' => 'testuser2', 'name' => 'Test User 2'],
|
||||||
['id' => self::USER_PRWL_ID, 'login' => 'private_whitelist_only_user', 'name' => 'Test User 4', 'private' => false, 'whitelist-only' => true],
|
// 99997
|
||||||
['id' => self::USER_UNNM_ID, 'login' => 'unnamed_user', 'name' => null, 'private' => false, 'whitelist-only' => false],
|
['login' => 'testuser3', 'name' => 'Test User 3'],
|
||||||
|
// 99996
|
||||||
|
['login' => 'testuser4', 'name' => 'Test User 4'],
|
||||||
|
//99995
|
||||||
|
['login' => 'testuser5', 'name' => null],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function load(ObjectManager $om)
|
public function load(ObjectManager $om)
|
||||||
{
|
{
|
||||||
|
$userId = 99999;
|
||||||
|
|
||||||
foreach ($this->users as $userData) {
|
foreach ($this->users as $userData) {
|
||||||
$user = new User($userData['id'], new \DateTime(), $userData['login'], $userData['name']);
|
$user = new User($userId--, new \DateTime(), $userData['login'], $userData['name']);
|
||||||
$user->updatePrivacy(!$userData['private'], $userData['whitelist-only']);
|
|
||||||
|
|
||||||
$om->persist($user);
|
$om->persist($user);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
/**
|
/**
|
||||||
* @ORM\Table(name="comments", schema="posts", indexes={
|
* @ORM\Table(name="comments", schema="posts", indexes={
|
||||||
* @ORM\Index(name="idx_comment_created_at", columns={"created_at"})
|
* @ORM\Index(name="idx_comment_created_at", columns={"created_at"})
|
||||||
|
* }, uniqueConstraints={
|
||||||
|
* @ORM\UniqueConstraint(name="unique_post_id_comment_number", columns={"post_id", "number"})
|
||||||
* })
|
* })
|
||||||
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Blogs\CommentRepository")
|
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Blogs\CommentRepository")
|
||||||
*/
|
*/
|
||||||
|
@ -62,10 +64,17 @@ class Comment
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="number", type="smallint")
|
* @ORM\Column(name="number", type="integer", unique=true)
|
||||||
*/
|
*/
|
||||||
private $number;
|
private $number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int|null
|
||||||
|
*
|
||||||
|
* @ORM\Column(name="to_number", type="integer", nullable=true)
|
||||||
|
*/
|
||||||
|
private $toNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var User
|
* @var User
|
||||||
*
|
*
|
||||||
|
@ -85,26 +94,36 @@ class Comment
|
||||||
*/
|
*/
|
||||||
private $files;
|
private $files;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Comment|null
|
|
||||||
*
|
|
||||||
* @ORM\ManyToOne(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment", inversedBy="children")
|
|
||||||
* @ORM\JoinColumn(name="parent_id", nullable=true)
|
|
||||||
*/
|
|
||||||
private $parent;
|
|
||||||
|
|
||||||
/**
|
public function __construct(
|
||||||
* @var Comment[]|ArrayCollection
|
string $text,
|
||||||
*
|
\DateTime $createdAt,
|
||||||
* @ORM\OneToMany(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment", fetch="EXTRA_LAZY", mappedBy="parent")
|
bool $rec,
|
||||||
*/
|
Post $post,
|
||||||
private $children;
|
int $number,
|
||||||
|
?int $toNumber,
|
||||||
|
User $author,
|
||||||
|
array $files
|
||||||
|
) {
|
||||||
|
$this->text = $text;
|
||||||
|
$this->createdAt = $createdAt;
|
||||||
|
$this->rec = $rec;
|
||||||
|
$this->post = $post;
|
||||||
|
$this->number = $number;
|
||||||
|
$this->toNumber = $toNumber;
|
||||||
|
$this->author = $author;
|
||||||
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->files = new ArrayCollection();
|
$this->files = new ArrayCollection();
|
||||||
$this->children = new ArrayCollection();
|
foreach ($files as $file) {
|
||||||
|
if (!($file instanceof File)) {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'$files array must contain only \'%s\' objects. %s given.',
|
||||||
|
\is_object($file) ? \get_class($file) : \gettype($file)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->files->add($file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): int
|
public function getId(): int
|
||||||
|
@ -112,95 +131,41 @@ class Comment
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCreatedAt(\DateTime $createdAt): self
|
|
||||||
{
|
|
||||||
$this->createdAt = $createdAt;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCreatedAt(): \DateTime
|
public function getCreatedAt(): \DateTime
|
||||||
{
|
{
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setText(string $text): self
|
|
||||||
{
|
|
||||||
$this->text = $text;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getText(): string
|
public function getText(): string
|
||||||
{
|
{
|
||||||
return $this->text;
|
return $this->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRec(bool $rec): self
|
|
||||||
{
|
|
||||||
$this->rec = $rec;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isRec(): bool
|
public function isRec(): bool
|
||||||
{
|
{
|
||||||
return $this->rec;
|
return $this->rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRec(): bool
|
|
||||||
{
|
|
||||||
return $this->rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPost(): Post
|
public function getPost(): Post
|
||||||
{
|
{
|
||||||
return $this->post;
|
return $this->post;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPost(Post $post): self
|
|
||||||
{
|
|
||||||
$this->post = $post;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setNumber(int $number): self
|
|
||||||
{
|
|
||||||
$this->number = $number;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNumber(): int
|
public function getNumber(): int
|
||||||
{
|
{
|
||||||
return $this->number;
|
return $this->number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getToNumber(): ?int
|
||||||
|
{
|
||||||
|
return $this->toNumber;
|
||||||
|
}
|
||||||
|
|
||||||
public function getAuthor(): User
|
public function getAuthor(): User
|
||||||
{
|
{
|
||||||
return $this->author;
|
return $this->author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAuthor(User $author): self
|
|
||||||
{
|
|
||||||
$this->author = $author;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addFile(File $files): self
|
|
||||||
{
|
|
||||||
$this->files[] = $files;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeFile(File $files): void
|
|
||||||
{
|
|
||||||
$this->files->removeElement($files);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return File[]|ArrayCollection
|
* @return File[]|ArrayCollection
|
||||||
*/
|
*/
|
||||||
|
@ -209,52 +174,22 @@ class Comment
|
||||||
return $this->files;
|
return $this->files;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getParent(): ?Comment
|
public function delete(): self
|
||||||
{
|
{
|
||||||
return $this->parent;
|
$this->deleted = true;
|
||||||
}
|
|
||||||
|
|
||||||
public function setParent(Comment $parent): self
|
|
||||||
{
|
|
||||||
$this->parent = $parent;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDeleted(bool $deleted): self
|
public function restore(): self
|
||||||
{
|
{
|
||||||
$this->deleted = $deleted;
|
$this->deleted = false;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDeleted(): bool
|
|
||||||
{
|
|
||||||
return $this->deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isDeleted(): bool
|
public function isDeleted(): bool
|
||||||
{
|
{
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addChild(Comment $children): self
|
|
||||||
{
|
|
||||||
$this->children[] = $children;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeChild(Comment $children): void
|
|
||||||
{
|
|
||||||
$this->children->removeElement($children);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Comment[]|ArrayCollection
|
|
||||||
*/
|
|
||||||
public function getChildren(): iterable
|
|
||||||
{
|
|
||||||
return $this->children;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,13 @@ class Post
|
||||||
*/
|
*/
|
||||||
private $deleted = false;
|
private $deleted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool Status of point-tools subscription to the post (to receive WS updates)
|
||||||
|
*
|
||||||
|
* @ORM\Column(name="is_subscribed", type="boolean", options={"default": false})
|
||||||
|
*/
|
||||||
|
private $subscribed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var User
|
* @var User
|
||||||
*
|
*
|
||||||
|
@ -103,7 +110,7 @@ class Post
|
||||||
private $comments;
|
private $comments;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(string $id, User $author, \DateTime $createdAt, string $type)
|
public function __construct(string $id, User $author, \DateTime $createdAt, string $type = self::TYPE_POST)
|
||||||
{
|
{
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
$this->author = $author;
|
$this->author = $author;
|
||||||
|
@ -217,6 +224,25 @@ class Post
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isSubscribed(): bool
|
||||||
|
{
|
||||||
|
return $this->subscribed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subscribe(): self
|
||||||
|
{
|
||||||
|
$this->subscribed = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsubscribe(): self
|
||||||
|
{
|
||||||
|
$this->subscribed = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setPrivate(bool $private): self
|
public function setPrivate(bool $private): self
|
||||||
{
|
{
|
||||||
$this->private = $private;
|
$this->private = $private;
|
||||||
|
@ -237,7 +263,6 @@ class Post
|
||||||
public function addComment(Comment $comment): self
|
public function addComment(Comment $comment): self
|
||||||
{
|
{
|
||||||
$this->comments[] = $comment;
|
$this->comments[] = $comment;
|
||||||
$comment->setPost($this);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Table(name="telegram_accounts", schema="users", indexes={
|
* @ORM\Table(name="telegram_accounts", schema="users", indexes={
|
||||||
* @ORM\Index(name="subscriber_notification_idx", columns={"subscriber_notification"}, options={"where": "subscriber_notification = TRUE"}),
|
* @ORM\Index(name="subscriber_notification_idx", columns={"subscriber_notification"}, options={"where": "(subscriber_notification = true)"}),
|
||||||
* @ORM\Index(name="rename_notification_idx", columns={"rename_notification"}, options={"where": "rename_notification = TRUE"}),
|
* @ORM\Index(name="rename_notification_idx", columns={"rename_notification"}, options={"where": "(rename_notification = true)"}),
|
||||||
* })
|
* })
|
||||||
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository")
|
* @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Repository\Telegram\AccountRepository")
|
||||||
* @ORM\HasLifecycleCallbacks()
|
* @ORM\HasLifecycleCallbacks()
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Table(name="users", schema="users", indexes={
|
* @ORM\Table(name="users", schema="users", indexes={
|
||||||
|
* @ORM\Index(name="idx_user_login", columns={"login"}),
|
||||||
* @ORM\Index(name="idx_user_public", columns={"public"}),
|
* @ORM\Index(name="idx_user_public", columns={"public"}),
|
||||||
* @ORM\Index(name="idx_user_removed", columns={"is_removed"})
|
* @ORM\Index(name="idx_user_removed", columns={"is_removed"})
|
||||||
* })
|
* })
|
||||||
|
@ -190,15 +191,24 @@ class User
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateCreatedAt(\DateTime $date): self
|
||||||
|
{
|
||||||
|
$this->createdAt = $date;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getUpdatedAt(): ?\DateTime
|
public function getUpdatedAt(): ?\DateTime
|
||||||
{
|
{
|
||||||
return $this->updatedAt;
|
return $this->updatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updatePrivacy(?bool $public, ?bool $whitelistOnly): void
|
public function updatePrivacy(bool $public, bool $whitelistOnly): self
|
||||||
{
|
{
|
||||||
$this->public = $public;
|
$this->public = $public;
|
||||||
$this->whitelistOnly = $whitelistOnly;
|
$this->whitelistOnly = $whitelistOnly;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPublic(): ?bool
|
public function isPublic(): ?bool
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
|
||||||
|
|
||||||
|
class PostNotFoundException extends NotFoundException
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function __construct(string $id, \Exception $previous)
|
||||||
|
{
|
||||||
|
parent::__construct('Post not found', 0, $previous);
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): string
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,16 +4,11 @@ namespace Skobkin\Bundle\PointToolsBundle\Exception\Api;
|
||||||
|
|
||||||
class UserNotFoundException extends NotFoundException
|
class UserNotFoundException extends NotFoundException
|
||||||
{
|
{
|
||||||
/**
|
/** @var int */
|
||||||
* @var int
|
private $userId;
|
||||||
*/
|
|
||||||
protected $userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $login;
|
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $login;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\Exception\WebSocket;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class UnsupportedTypeException extends RuntimeException
|
||||||
|
{
|
||||||
|
}
|
|
@ -2,10 +2,6 @@ Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment:
|
||||||
exclusion_policy: none
|
exclusion_policy: none
|
||||||
access_type: public_method
|
access_type: public_method
|
||||||
properties:
|
properties:
|
||||||
postId:
|
|
||||||
serialized_name: 'post_id'
|
|
||||||
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
|
|
||||||
max_depth: 2
|
|
||||||
number:
|
number:
|
||||||
serialized_name: 'id'
|
serialized_name: 'id'
|
||||||
type: integer
|
type: integer
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost:
|
Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost:
|
||||||
exclusion_policy: none
|
exclusion_policy: none
|
||||||
access_type: public_method
|
|
||||||
properties:
|
properties:
|
||||||
post:
|
post:
|
||||||
|
serialized_name: 'post'
|
||||||
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
|
type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post'
|
||||||
max_depth: 2
|
|
||||||
comments:
|
comments:
|
||||||
|
serialized_name: 'comments'
|
||||||
type: 'array<Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment>'
|
type: 'array<Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment>'
|
||||||
max_depth: 2
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message:
|
||||||
|
exclusion_policy: none
|
||||||
|
properties:
|
||||||
|
a:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'a'
|
||||||
|
author:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'author'
|
||||||
|
authorId:
|
||||||
|
type: int
|
||||||
|
serialized_name: 'author_id'
|
||||||
|
authorName:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'author_name'
|
||||||
|
commentId:
|
||||||
|
type: int
|
||||||
|
serialized_name: 'comment_id'
|
||||||
|
cut:
|
||||||
|
type: bool
|
||||||
|
serialized_name: 'cut'
|
||||||
|
files:
|
||||||
|
type: array<string>
|
||||||
|
serialized_name: 'files'
|
||||||
|
html:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'html'
|
||||||
|
link:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'link'
|
||||||
|
postId:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'post_id'
|
||||||
|
postAuthorId:
|
||||||
|
type: int
|
||||||
|
serialized_name: 'post_author_id'
|
||||||
|
private:
|
||||||
|
type: bool
|
||||||
|
serialized_name: 'private'
|
||||||
|
rcid:
|
||||||
|
type: int
|
||||||
|
serialized_name: 'rcid'
|
||||||
|
tags:
|
||||||
|
type: array<string>
|
||||||
|
serialized_name: 'tags'
|
||||||
|
text:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'text'
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'title'
|
||||||
|
toCommentId:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'to_comment_id'
|
||||||
|
toText:
|
||||||
|
type: string
|
||||||
|
serialized_name: 'to_text'
|
||||||
|
toUsers:
|
||||||
|
type: array<string>
|
||||||
|
serialized_name: 'to_users'
|
|
@ -5,6 +5,9 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
|
||||||
use GuzzleHttp\ClientInterface;
|
use GuzzleHttp\ClientInterface;
|
||||||
use JMS\Serializer\SerializerInterface;
|
use JMS\Serializer\SerializerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Exception\Api\{NotFoundException, PostNotFoundException};
|
||||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +15,8 @@ use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
||||||
*/
|
*/
|
||||||
class PostApi extends AbstractApi
|
class PostApi extends AbstractApi
|
||||||
{
|
{
|
||||||
|
private const PREFIX = '/api/post/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PostFactory
|
* @var PostFactory
|
||||||
*/
|
*/
|
||||||
|
@ -24,4 +29,24 @@ class PostApi extends AbstractApi
|
||||||
|
|
||||||
$this->postFactory = $postFactory;
|
$this->postFactory = $postFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws PostNotFoundException
|
||||||
|
*/
|
||||||
|
public function getById(string $id): Post
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$postData = $this->getGetJsonData(
|
||||||
|
self::PREFIX.$id,
|
||||||
|
[],
|
||||||
|
MetaPost::class
|
||||||
|
);
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
throw new PostNotFoundException($id, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not catching ForbiddenException right now
|
||||||
|
|
||||||
|
return $this->postFactory->findOrCreateFromDtoWithContent($postData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
|
namespace Skobkin\Bundle\PointToolsBundle\Service\Api;
|
||||||
|
|
||||||
use GuzzleHttp\ClientInterface;
|
use GuzzleHttp\ClientInterface;
|
||||||
use JMS\Serializer\{
|
use JMS\Serializer\{DeserializationContext, SerializerInterface};
|
||||||
DeserializationContext, SerializerInterface
|
|
||||||
};
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{Auth, User as UserDTO};
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{Auth, User as UserDTO};
|
||||||
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
use Skobkin\Bundle\PointToolsBundle\Entity\User;
|
||||||
|
|
|
@ -6,6 +6,8 @@ use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
abstract class AbstractFactory
|
abstract class AbstractFactory
|
||||||
{
|
{
|
||||||
|
public const DATE_FORMAT = 'Y-m-d_H:i:s';
|
||||||
|
|
||||||
/** @var LoggerInterface */
|
/** @var LoggerInterface */
|
||||||
protected $logger;
|
protected $logger;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
|
namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment;
|
||||||
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\{CommentRepository, PostRepository};
|
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\{CommentRepository, PostRepository};
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Service\Api\PostApi;
|
||||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
||||||
|
|
||||||
class CommentFactory extends AbstractFactory
|
class CommentFactory extends AbstractFactory
|
||||||
|
@ -17,12 +20,45 @@ class CommentFactory extends AbstractFactory
|
||||||
/** @var UserFactory */
|
/** @var UserFactory */
|
||||||
private $userFactory;
|
private $userFactory;
|
||||||
|
|
||||||
|
/** @var PostApi */
|
||||||
|
private $postApi;
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger, CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory)
|
|
||||||
|
public function __construct(LoggerInterface $logger, CommentRepository $commentRepository, PostRepository $postRepository, UserFactory $userFactory, PostApi $postApi)
|
||||||
{
|
{
|
||||||
parent::__construct($logger);
|
parent::__construct($logger);
|
||||||
$this->userFactory = $userFactory;
|
$this->userFactory = $userFactory;
|
||||||
$this->commentRepository = $commentRepository;
|
$this->commentRepository = $commentRepository;
|
||||||
$this->postRepository = $postRepository;
|
$this->postRepository = $postRepository;
|
||||||
|
$this->postApi = $postApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOrCreateFromWebsocketMessage(Message $message): Comment
|
||||||
|
{
|
||||||
|
if ($message->isValid()) {
|
||||||
|
throw new \InvalidArgumentException('Comment is invalid');
|
||||||
|
}
|
||||||
|
if ($message->isComment()) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'Invalid Message object provided. %s expected, %s given',
|
||||||
|
Message::TYPE_COMMENT,
|
||||||
|
$message->getA()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $comment = $this->commentRepository->findOneBy(['post' => $post, 'number' => $message->getCommentId()])) {
|
||||||
|
$author = $this->userFactory->findOrCreateFromIdLoginAndName(
|
||||||
|
$message->getAuthorId(),
|
||||||
|
$message->getAuthor(),
|
||||||
|
$message->getAuthorName()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (null === $post = $this->postRepository->find($message->getPostId())) {
|
||||||
|
$post = $this->postApi->getById($message->getPostId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//$comment = new Comment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,10 +4,11 @@ namespace Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{MetaPost, Post as PostDTO, PostsPage};
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\{MetaPost, Post as ApiPost, PostsPage};
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message as WebsocketMessage;
|
||||||
use Skobkin\Bundle\PointToolsBundle\Entity\{Blogs\Post, Blogs\PostTag, User};
|
use Skobkin\Bundle\PointToolsBundle\Entity\{Blogs\Post, Blogs\PostTag, User};
|
||||||
use Skobkin\Bundle\PointToolsBundle\Exception\{Api\InvalidResponseException, Factory\Blog\InvalidDataException};
|
use Skobkin\Bundle\PointToolsBundle\Exception\{Api\InvalidResponseException, Factory\Blog\InvalidDataException};
|
||||||
use Skobkin\Bundle\PointToolsBundle\Repository\Blogs\PostRepository;
|
use Skobkin\Bundle\PointToolsBundle\Repository\{Blogs\PostRepository, UserRepository};
|
||||||
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
use Skobkin\Bundle\PointToolsBundle\Service\Factory\{AbstractFactory, UserFactory};
|
||||||
|
|
||||||
class PostFactory extends AbstractFactory
|
class PostFactory extends AbstractFactory
|
||||||
|
@ -18,6 +19,9 @@ class PostFactory extends AbstractFactory
|
||||||
/** @var PostRepository */
|
/** @var PostRepository */
|
||||||
private $postRepository;
|
private $postRepository;
|
||||||
|
|
||||||
|
/** @var UserRepository */
|
||||||
|
private $userRepository;
|
||||||
|
|
||||||
/** @var UserFactory */
|
/** @var UserFactory */
|
||||||
private $userFactory;
|
private $userFactory;
|
||||||
|
|
||||||
|
@ -35,6 +39,7 @@ class PostFactory extends AbstractFactory
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $em,
|
||||||
PostRepository $postRepository,
|
PostRepository $postRepository,
|
||||||
|
UserRepository $userRepository,
|
||||||
UserFactory $userFactory,
|
UserFactory $userFactory,
|
||||||
FileFactory $fileFactory,
|
FileFactory $fileFactory,
|
||||||
CommentFactory $commentFactory,
|
CommentFactory $commentFactory,
|
||||||
|
@ -43,6 +48,7 @@ class PostFactory extends AbstractFactory
|
||||||
parent::__construct($logger);
|
parent::__construct($logger);
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
$this->postRepository = $postRepository;
|
$this->postRepository = $postRepository;
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
$this->userFactory = $userFactory;
|
$this->userFactory = $userFactory;
|
||||||
$this->fileFactory = $fileFactory;
|
$this->fileFactory = $fileFactory;
|
||||||
$this->commentFactory = $commentFactory;
|
$this->commentFactory = $commentFactory;
|
||||||
|
@ -92,8 +98,6 @@ class PostFactory extends AbstractFactory
|
||||||
/**
|
/**
|
||||||
* Create full post with tags, files and comments
|
* Create full post with tags, files and comments
|
||||||
*
|
*
|
||||||
* @todo Implement comments
|
|
||||||
*
|
|
||||||
* @throws InvalidDataException
|
* @throws InvalidDataException
|
||||||
*/
|
*/
|
||||||
public function findOrCreateFromDtoWithContent(MetaPost $metaPost): Post
|
public function findOrCreateFromDtoWithContent(MetaPost $metaPost): Post
|
||||||
|
@ -111,7 +115,7 @@ class PostFactory extends AbstractFactory
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$post = $this->findOrCreateFromDto($postData, $author);
|
$post = $this->findOrCreateFromApiDto($postData, $author);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->updatePostTags($post, $postData->getTags() ?: []);
|
$this->updatePostTags($post, $postData->getTags() ?: []);
|
||||||
|
@ -127,10 +131,62 @@ class PostFactory extends AbstractFactory
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TODO implement comments
|
||||||
|
|
||||||
return $post;
|
return $post;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findOrCreateFromDto(PostDTO $postData, User $author): Post
|
public function findOrCreateFromWebsocketDto(WebsocketMessage $message): Post
|
||||||
|
{
|
||||||
|
if (!$message->isValid()) {
|
||||||
|
throw new InvalidDataException('Invalid post data');
|
||||||
|
}
|
||||||
|
if (!$message->isPost()) {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
'Incorrect message type received. \'post\' expected \'%s\' given',
|
||||||
|
$message->getA()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $post = $this->postRepository->find($message->getPostId())) {
|
||||||
|
$author = $this->userFactory->findOrCreateFromIdLoginAndName(
|
||||||
|
$message->getAuthorId(),
|
||||||
|
$message->getAuthor(),
|
||||||
|
$message->getAuthorName()
|
||||||
|
);
|
||||||
|
|
||||||
|
$post = new Post(
|
||||||
|
$message->getPostId(),
|
||||||
|
$author,
|
||||||
|
new \DateTime(),
|
||||||
|
Post::TYPE_POST
|
||||||
|
);
|
||||||
|
$this->postRepository->add($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
$post
|
||||||
|
->setText($message->getText())
|
||||||
|
->setPrivate((bool) $message->getPrivate())
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->updatePostTags($post, $message->getTags() ?: []);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error('Error while updating post tags');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->updatePostFiles($post, $message->getFiles() ?: []);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error('Error while updating post files');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findOrCreateFromApiDto(ApiPost $postData, User $author): Post
|
||||||
{
|
{
|
||||||
if (null === ($post = $this->postRepository->find($postData->getId()))) {
|
if (null === ($post = $this->postRepository->find($postData->getId()))) {
|
||||||
// Creating new post
|
// Creating new post
|
||||||
|
@ -145,7 +201,7 @@ class PostFactory extends AbstractFactory
|
||||||
|
|
||||||
$post
|
$post
|
||||||
->setText($postData->getText())
|
->setText($postData->getText())
|
||||||
->setPrivate($postData->getPrivate())
|
->setPrivate((bool) $postData->getPrivate())
|
||||||
;
|
;
|
||||||
|
|
||||||
return $post;
|
return $post;
|
||||||
|
|
|
@ -10,12 +10,9 @@ use Skobkin\Bundle\PointToolsBundle\Exception\Factory\InvalidUserDataException;
|
||||||
|
|
||||||
class UserFactory extends AbstractFactory
|
class UserFactory extends AbstractFactory
|
||||||
{
|
{
|
||||||
public const DATE_FORMAT = 'Y-m-d_H:i:s';
|
|
||||||
|
|
||||||
/** @var UserRepository */
|
/** @var UserRepository */
|
||||||
private $userRepository;
|
private $userRepository;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(LoggerInterface $logger, UserRepository $userRepository)
|
public function __construct(LoggerInterface $logger, UserRepository $userRepository)
|
||||||
{
|
{
|
||||||
parent::__construct($logger);
|
parent::__construct($logger);
|
||||||
|
@ -23,27 +20,25 @@ class UserFactory extends AbstractFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param UserDTO $userData
|
|
||||||
*
|
|
||||||
* @return User
|
|
||||||
*
|
|
||||||
* @throws InvalidUserDataException
|
* @throws InvalidUserDataException
|
||||||
*/
|
*/
|
||||||
public function findOrCreateFromDTO(UserDTO $userData): User
|
public function findOrCreateFromDTO(UserDTO $userData): User
|
||||||
{
|
{
|
||||||
// @todo LOG
|
|
||||||
|
|
||||||
if (!$userData->isValid()) {
|
if (!$userData->isValid()) {
|
||||||
throw new InvalidUserDataException('Invalid user data', $userData);
|
throw new InvalidUserDataException('Invalid user data', $userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$createdAt = \DateTime::createFromFormat(self::DATE_FORMAT, $userData->getCreated()) ?: new \DateTime();
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
if (null === ($user = $this->userRepository->find($userData->getId()))) {
|
if (null === ($user = $this->userRepository->find($userData->getId()))) {
|
||||||
$user = new User(
|
$user = new User(
|
||||||
$userData->getId(),
|
$userData->getId(),
|
||||||
\DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime()
|
$createdAt
|
||||||
);
|
);
|
||||||
$this->userRepository->add($user);
|
$this->userRepository->add($user);
|
||||||
|
} else {
|
||||||
|
$user->updateCreatedAt($createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->updateLoginAndName($userData->getLogin(), $userData->getName());
|
$user->updateLoginAndName($userData->getLogin(), $userData->getName());
|
||||||
|
@ -55,6 +50,21 @@ class UserFactory extends AbstractFactory
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findOrCreateFromIdLoginAndName(int $id, string $login, ?string $name): User
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
if (null === $user = $this->userRepository->find($id)) {
|
||||||
|
// We're using current date now but next time when we'll be updating user from API it'll be fixed
|
||||||
|
$user = new User($id, new \DateTime(), $login, $name);
|
||||||
|
$this->userRepository->add($user);
|
||||||
|
} else {
|
||||||
|
// @todo update login?
|
||||||
|
// Probably don't because no name in the WS message (or maybe after PR to point?)
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return User[]
|
* @return User[]
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\Service\WebSocket;
|
||||||
|
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\DTO\Api\WebSocket\Message;
|
||||||
|
use Skobkin\Bundle\PointToolsBundle\Service\Factory\Blogs\{CommentFactory, PostFactory};
|
||||||
|
|
||||||
|
class WebSocketMessageProcessor
|
||||||
|
{
|
||||||
|
/** @var PostFactory */
|
||||||
|
private $postFactory;
|
||||||
|
|
||||||
|
/** @var CommentFactory */
|
||||||
|
private $commentFactory;
|
||||||
|
|
||||||
|
public function __construct(PostFactory $postFactory, CommentFactory $commentFactory)
|
||||||
|
{
|
||||||
|
$this->postFactory = $postFactory;
|
||||||
|
$this->commentFactory = $commentFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true on success (all data saved)
|
||||||
|
*/
|
||||||
|
public function processMessage(Message $message): bool
|
||||||
|
{
|
||||||
|
if (!$message->isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $message->isPost():
|
||||||
|
return $this->processPost($message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case $message->isComment():
|
||||||
|
return $this->processComment($message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case $message->isCommentRecommendation():
|
||||||
|
return $this->processRecommendation($message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processPost(Message $postData): bool
|
||||||
|
{
|
||||||
|
$this->postFactory->findOrCreateFromWebsocketDto($postData);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processComment(Message $commentData): bool
|
||||||
|
{
|
||||||
|
// Not done yet
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->commentFactory->findOrCreateFromWebsocketMessage($commentData);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processRecommendation(Message $recommendData): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Client;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class MainControllerTest extends WebTestCase
|
class MainControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
public function testUserSearch(): void
|
public function testUserSearch()
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
$crawler = $client->request('GET', '/');
|
$crawler = $client->request('GET', '/');
|
||||||
|
@ -20,7 +19,7 @@ class MainControllerTest extends WebTestCase
|
||||||
$this->assertTrue($client->getResponse()->isRedirect('/user/testuser'), 'Redirect to testuser\'s page didn\'t happen');
|
$this->assertTrue($client->getResponse()->isRedirect('/user/testuser'), 'Redirect to testuser\'s page didn\'t happen');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNonExistingUserSearch(): void
|
public function testNonExistingUserSearch()
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
$crawler = $client->request('GET', '/');
|
$crawler = $client->request('GET', '/');
|
||||||
|
@ -50,7 +49,7 @@ class MainControllerTest extends WebTestCase
|
||||||
$this->assertEquals(' Login not found', $firstError->text(), 'Incorrect error text');
|
$this->assertEquals(' Login not found', $firstError->text(), 'Incorrect error text');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserStats(): void
|
public function testUserStats()
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
$crawler = $client->request('GET', '/');
|
$crawler = $client->request('GET', '/');
|
||||||
|
@ -76,10 +75,14 @@ class MainControllerTest extends WebTestCase
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests AJAX user search autocomplete and returns JSON response string
|
* Tests AJAX user search autocomplete and returns JSON response string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function testAjaxUserAutoComplete(): string
|
public function testAjaxUserAutoComplete()
|
||||||
{
|
{
|
||||||
$client = $this->createClientForAjaxUserSearchByLogin('testuser');
|
$client = static::createClient();
|
||||||
|
// We need to search all test user with 'testuser5' included which will test the code against null-string problem in User#getName()
|
||||||
|
$client->request('GET', '/ajax/users/search/testuser');
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/json'), 'Response has "Content-Type" = "application/json"');
|
$this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/json'), 'Response has "Content-Type" = "application/json"');
|
||||||
|
|
||||||
|
@ -88,22 +91,26 @@ class MainControllerTest extends WebTestCase
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testAjaxUserAutoComplete
|
* @depends testAjaxUserAutoComplete
|
||||||
|
*
|
||||||
|
* @param $json
|
||||||
*/
|
*/
|
||||||
public function testAjaxUserAutoCompleteHasOptions(string $json): array
|
public function testAjaxUserAutoCompleteHasOptions($json)
|
||||||
{
|
{
|
||||||
$data = json_decode($json, true);
|
$data = json_decode($json);
|
||||||
|
|
||||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
||||||
$this->assertTrue(is_array($data), 'JSON data is array');
|
$this->assertTrue(is_array($data), 'JSON data is array');
|
||||||
$this->assertCount(2, $data, 'Array has 2 elements');
|
$this->assertCount(5, $data, 'Array has 5 elements');
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testAjaxUserAutoCompleteHasOptions
|
* @depends testAjaxUserAutoCompleteHasOptions
|
||||||
|
*
|
||||||
|
* @param array $users
|
||||||
*/
|
*/
|
||||||
public function testAjaxUserAutoCompleteHasValidUserObjects(array $users): void
|
public function testAjaxUserAutoCompleteHasValidUserObjects(array $users)
|
||||||
{
|
{
|
||||||
foreach ($users as $key => $user) {
|
foreach ($users as $key => $user) {
|
||||||
$this->assertTrue(array_key_exists('login', $user), sprintf('%d row of result has \'login\' field', $key));
|
$this->assertTrue(array_key_exists('login', $user), sprintf('%d row of result has \'login\' field', $key));
|
||||||
|
@ -111,43 +118,7 @@ class MainControllerTest extends WebTestCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testAjaxUserAutoCompleteForNonExistingUser()
|
||||||
* Tests AJAX user search autocomplete for unnamed user and returns JSON response string
|
|
||||||
*/
|
|
||||||
public function testAjaxUserAutoCompleteForUnnamedUser(): string
|
|
||||||
{
|
|
||||||
$client = $this->createClientForAjaxUserSearchByLogin('unnamed_user');
|
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->headers->contains('Content-Type', 'application/json'), 'Response has "Content-Type" = "application/json"');
|
|
||||||
|
|
||||||
return $client->getResponse()->getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @depends testAjaxUserAutoCompleteForUnnamedUser
|
|
||||||
*/
|
|
||||||
public function testAjaxUserAutoCompleteHasOptionsForUnnamedUser(string $json): array
|
|
||||||
{
|
|
||||||
$data = json_decode($json, true);
|
|
||||||
|
|
||||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
|
||||||
$this->assertInternalType('array', $data, 'JSON data is array');
|
|
||||||
$this->assertCount(1, $data, 'Array has 1 elements');
|
|
||||||
|
|
||||||
return reset($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @depends testAjaxUserAutoCompleteHasOptionsForUnnamedUser
|
|
||||||
*/
|
|
||||||
public function testAjaxUserAutoCompleteHasValidUserObjectsForUnnamedUser(array $user): void
|
|
||||||
{
|
|
||||||
$this->assertTrue(array_key_exists('login', $user), 'Result has \'login\' field');
|
|
||||||
$this->assertTrue(array_key_exists('name', $user), 'Result has \'name\' field');
|
|
||||||
$this->assertEquals(true, ('' === $user['name'] || null === $user['name']), 'User name is empty string or null');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAjaxUserAutoCompleteIsEmptyForNonExistingUser(): void
|
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
$client->request('GET', '/ajax/users/search/aksdjhaskdjhqwhdgqkjwhdgkjah');
|
$client->request('GET', '/ajax/users/search/aksdjhaskdjhqwhdgqkjwhdgkjah');
|
||||||
|
@ -157,15 +128,7 @@ class MainControllerTest extends WebTestCase
|
||||||
$data = json_decode($client->getResponse()->getContent());
|
$data = json_decode($client->getResponse()->getContent());
|
||||||
|
|
||||||
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
$this->assertNotNull($data, 'JSON data successfully decoded and not empty');
|
||||||
$this->assertInternalType('array', $data, 'JSON data is array');
|
$this->assertTrue(is_array($data), 'JSON data is array');
|
||||||
$this->assertCount(0, $data, 'Array has no elements');
|
$this->assertEquals(0, count($data), 'Array has no elements');
|
||||||
}
|
|
||||||
|
|
||||||
private function createClientForAjaxUserSearchByLogin(string $login): Client
|
|
||||||
{
|
|
||||||
$client = static::createClient();
|
|
||||||
$client->request('GET', '/ajax/users/search/'.$login);
|
|
||||||
|
|
||||||
return $client;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
namespace Tests\Skobkin\PointToolsBundle\Controller;
|
||||||
|
|
||||||
use Skobkin\Bundle\PointToolsBundle\DataFixtures\ORM\LoadPostData;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
use Symfony\Bundle\FrameworkBundle\{Client, Test\WebTestCase};
|
|
||||||
use Symfony\Component\DomCrawler\Crawler;
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
|
||||||
class PostControllerTest extends WebTestCase
|
class PostControllerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
public function testNonExistingPostPage()
|
public function testNonExistingPostPage()
|
||||||
{
|
{
|
||||||
$client = $this->createClientForPostId('nonexistingpost');
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/nonexistingpost');
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for non-existing post');
|
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for non-existing post');
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ class PostControllerTest extends WebTestCase
|
||||||
*/
|
*/
|
||||||
public function testShortPostPageIsOk()
|
public function testShortPostPageIsOk()
|
||||||
{
|
{
|
||||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_SHORT);
|
$client = static::createClient();
|
||||||
|
$crawler = $client->request('GET', '/shortpost');
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isOk(), '200 response code for existing post');
|
$this->assertTrue($client->getResponse()->isOk(), '200 response code for existing post');
|
||||||
|
|
||||||
return $client->getCrawler();
|
return $crawler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,33 +58,4 @@ class PostControllerTest extends WebTestCase
|
||||||
$this->assertEquals(1, $p->count(), '.post-text has zero or more than one paragraphs');
|
$this->assertEquals(1, $p->count(), '.post-text has zero or more than one paragraphs');
|
||||||
$this->assertEquals('Test short post', $p->text(), '.post-text has no correct post text');
|
$this->assertEquals('Test short post', $p->text(), '.post-text has no correct post text');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPrivateUserPostForbidden()
|
|
||||||
{
|
|
||||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_PR_USER);
|
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for private user\'s post');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testWhitelistOnlyUserPostForbidden()
|
|
||||||
{
|
|
||||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_WL_USER);
|
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for whitelist-only user\'s post');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPrivateWhitelistOnlyUserPostForbidden()
|
|
||||||
{
|
|
||||||
$client = $this->createClientForPostId(LoadPostData::POST_ID_PR_WL_USER);
|
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isNotFound(), '404 response code for private whitelist-only user\'s post');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createClientForPostId(string $id): Client
|
|
||||||
{
|
|
||||||
$client = static::createClient();
|
|
||||||
$client->request('GET', '/'.$id);
|
|
||||||
|
|
||||||
return $client;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class UserRepositoryTest extends KernelTestCase
|
||||||
{
|
{
|
||||||
$users = $this->userRepo->findAll();
|
$users = $this->userRepo->findAll();
|
||||||
|
|
||||||
$this->assertCount(6, $users, 'Not exactly 6 users in the databas');
|
$this->assertCount(5, $users, 'Not exactly 5 users in the databas');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFindOneByLogin()
|
public function testFindOneByLogin()
|
||||||
|
@ -58,14 +58,14 @@ class UserRepositoryTest extends KernelTestCase
|
||||||
// Searching LIKE %stus% (testuserX)
|
// Searching LIKE %stus% (testuserX)
|
||||||
$users = $this->userRepo->findUsersLikeLogin('stus');
|
$users = $this->userRepo->findUsersLikeLogin('stus');
|
||||||
|
|
||||||
$this->assertCount(2, $users, 'Repository found not exactly 5 users');
|
$this->assertCount(5, $users, 'Repository found not exactly 5 users');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetUsersCount()
|
public function testGetUsersCount()
|
||||||
{
|
{
|
||||||
$count = $this->userRepo->getUsersCount();
|
$count = $this->userRepo->getUsersCount();
|
||||||
|
|
||||||
$this->assertEquals(6, $count, 'Counted not exactly 5 users');
|
$this->assertEquals(5, $count, 'Counted not exactly 5 users');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFindUserSubscribersById()
|
public function testFindUserSubscribersById()
|
||||||
|
|
Loading…
Reference in a new issue