Compare commits
20 commits
690b849ca6
...
351bffb298
Author | SHA1 | Date | |
---|---|---|---|
351bffb298 | |||
312c1617e1 | |||
c908a22d15 | |||
53028d2577 | |||
1393e2d53f | |||
77fa05f457 | |||
40db2edc38 | |||
e56cae9568 | |||
fc9026d13f | |||
0c48b57710 | |||
a83b783de4 | |||
430632c8e4 | |||
f4e92a8f31 | |||
8614bd0f26 | |||
a409fbae8d | |||
0822aea5ec | |||
c7256f5a50 | |||
c151acd30b | |||
b4bdda26e3 | |||
0a05264406 |
|
@ -12,6 +12,7 @@
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.2",
|
"doctrine/doctrine-migrations-bundle": "^3.2",
|
||||||
"doctrine/orm": "^2.14",
|
"doctrine/orm": "^2.14",
|
||||||
"jms/serializer-bundle": "^5.2",
|
"jms/serializer-bundle": "^5.2",
|
||||||
|
"knplabs/knp-paginator-bundle": "^6.2",
|
||||||
"phpdocumentor/reflection-docblock": "^5.3",
|
"phpdocumentor/reflection-docblock": "^5.3",
|
||||||
"phpstan/phpdoc-parser": "^1.16",
|
"phpstan/phpdoc-parser": "^1.16",
|
||||||
"sensio/framework-extra-bundle": "^6.1",
|
"sensio/framework-extra-bundle": "^6.1",
|
||||||
|
|
162
composer.lock
generated
162
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "3e4ae473183073d1dd231517f5734c3e",
|
"content-hash": "adf20c9a601a7f685ece9da4a4ac9e47",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "doctrine/annotations",
|
"name": "doctrine/annotations",
|
||||||
|
@ -2013,6 +2013,166 @@
|
||||||
],
|
],
|
||||||
"time": "2023-02-05T11:03:45+00:00"
|
"time": "2023-02-05T11:03:45+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "knplabs/knp-components",
|
||||||
|
"version": "v4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/KnpLabs/knp-components.git",
|
||||||
|
"reference": "6b6efa81ee894e325744bf785d50dc962937b1f2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/KnpLabs/knp-components/zipball/6b6efa81ee894e325744bf785d50dc962937b1f2",
|
||||||
|
"reference": "6b6efa81ee894e325744bf785d50dc962937b1f2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0",
|
||||||
|
"symfony/event-dispatcher-contracts": "^3.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"doctrine/dbal": "<2.10"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/mongodb-odm": "^2.4",
|
||||||
|
"doctrine/orm": "^2.12",
|
||||||
|
"doctrine/phpcr-odm": "^1.6",
|
||||||
|
"ext-pdo_sqlite": "*",
|
||||||
|
"jackalope/jackalope-doctrine-dbal": "^1.8",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"propel/propel1": "^1.7",
|
||||||
|
"ruflin/elastica": "^7.0",
|
||||||
|
"solarium/solarium": "^6.0",
|
||||||
|
"symfony/http-foundation": "^5.4 || ^6.0",
|
||||||
|
"symfony/http-kernel": "^5.4 || ^6.0",
|
||||||
|
"symfony/property-access": "^5.4 || ^6.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"doctrine/common": "to allow usage pagination with Doctrine ArrayCollection",
|
||||||
|
"doctrine/mongodb-odm": "to allow usage pagination with Doctrine ODM MongoDB",
|
||||||
|
"doctrine/orm": "to allow usage pagination with Doctrine ORM",
|
||||||
|
"doctrine/phpcr-odm": "to allow usage pagination with Doctrine ODM PHPCR",
|
||||||
|
"propel/propel1": "to allow usage pagination with Propel ORM",
|
||||||
|
"ruflin/elastica": "to allow usage pagination with ElasticSearch Client",
|
||||||
|
"solarium/solarium": "to allow usage pagination with Solarium Client",
|
||||||
|
"symfony/http-foundation": "to retrieve arguments from Request",
|
||||||
|
"symfony/property-access": "to allow sorting arrays"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Knp\\Component\\": "src/Knp/Component"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "KnpLabs Team",
|
||||||
|
"homepage": "https://knplabs.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://github.com/KnpLabs/knp-components/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Knplabs component library",
|
||||||
|
"homepage": "http://github.com/KnpLabs/knp-components",
|
||||||
|
"keywords": [
|
||||||
|
"components",
|
||||||
|
"knp",
|
||||||
|
"knplabs",
|
||||||
|
"pager",
|
||||||
|
"paginator"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/KnpLabs/knp-components/issues",
|
||||||
|
"source": "https://github.com/KnpLabs/knp-components/tree/v4.1.0"
|
||||||
|
},
|
||||||
|
"time": "2022-12-19T09:36:54+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "knplabs/knp-paginator-bundle",
|
||||||
|
"version": "v6.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/KnpLabs/KnpPaginatorBundle.git",
|
||||||
|
"reference": "8da698f0856b1d9c9c02dcacbfc382c5c9440c80"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/KnpLabs/KnpPaginatorBundle/zipball/8da698f0856b1d9c9c02dcacbfc382c5c9440c80",
|
||||||
|
"reference": "8da698f0856b1d9c9c02dcacbfc382c5c9440c80",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"knplabs/knp-components": "^4.1",
|
||||||
|
"php": "^8.0",
|
||||||
|
"symfony/config": "^6.0",
|
||||||
|
"symfony/dependency-injection": "^6.0",
|
||||||
|
"symfony/event-dispatcher": "^6.0",
|
||||||
|
"symfony/http-foundation": "^6.0",
|
||||||
|
"symfony/http-kernel": "^6.0",
|
||||||
|
"symfony/routing": "^6.0",
|
||||||
|
"symfony/translation": "^6.0",
|
||||||
|
"twig/twig": "^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"symfony/expression-language": "^6.0",
|
||||||
|
"symfony/templating": "^6.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Knp\\Bundle\\PaginatorBundle\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "KnpLabs Team",
|
||||||
|
"homepage": "https://knplabs.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://github.com/KnpLabs/KnpPaginatorBundle/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Paginator bundle for Symfony to automate pagination and simplify sorting and other features",
|
||||||
|
"homepage": "https://github.com/KnpLabs/KnpPaginatorBundle",
|
||||||
|
"keywords": [
|
||||||
|
"bundle",
|
||||||
|
"knp",
|
||||||
|
"knplabs",
|
||||||
|
"pager",
|
||||||
|
"pagination",
|
||||||
|
"paginator",
|
||||||
|
"symfony"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/KnpLabs/KnpPaginatorBundle/issues",
|
||||||
|
"source": "https://github.com/KnpLabs/KnpPaginatorBundle/tree/v6.2.0"
|
||||||
|
},
|
||||||
|
"time": "2023-03-25T06:51:40+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||||
|
@ -13,4 +14,5 @@ return [
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
||||||
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
|
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
|
||||||
|
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
4
config/packages/knp_paginator.yaml
Normal file
4
config/packages/knp_paginator.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# https://github.com/KnpLabs/KnpPaginatorBundle#yaml
|
||||||
|
knp_paginator:
|
||||||
|
template:
|
||||||
|
pagination: 'KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig'
|
|
@ -12,14 +12,20 @@ services:
|
||||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
||||||
bind:
|
bind:
|
||||||
# TODO: fix retrieval
|
# TODO: fix retrieval
|
||||||
|
# Point Tools
|
||||||
|
$appUserId: 435
|
||||||
|
$appUserLogin: 'point-tools'
|
||||||
# Telegram Bot API
|
# Telegram Bot API
|
||||||
$telegramToken: ''
|
$telegramToken: ''
|
||||||
|
$debugEnabled: '%kernel.debug%'
|
||||||
# Point API
|
# Point API
|
||||||
$pointDomain: 'point.im'
|
$pointDomain: 'point.im'
|
||||||
$pointScheme: 'https'
|
$pointScheme: 'https'
|
||||||
$pointApiDelay: ''
|
$pointApiDelay: 5000
|
||||||
$pointAppUserId: ''
|
$pointAppUserId: ''
|
||||||
$pointApiClient: '@app.point.http_client'
|
$pointApiClient: '@app.point.http_client'
|
||||||
|
# Crawler API
|
||||||
|
$crawlerToken: ''
|
||||||
|
|
||||||
# makes classes in src/ available to be used as services
|
# makes classes in src/ available to be used as services
|
||||||
# this creates a service per class whose id is the fully-qualified class name
|
# this creates a service per class whose id is the fully-qualified class name
|
||||||
|
@ -31,8 +37,8 @@ services:
|
||||||
- '../src/Kernel.php'
|
- '../src/Kernel.php'
|
||||||
|
|
||||||
# HTTP client for Point API
|
# HTTP client for Point API
|
||||||
Symfony\Component\HttpClient\HttpClient:
|
app.point.http_client:
|
||||||
alias: 'app.point.http_client'
|
class: Symfony\Component\HttpClient\HttpClient
|
||||||
factory: [null, 'create']
|
factory: [null, 'create']
|
||||||
arguments:
|
arguments:
|
||||||
base_uri: '%point_base_url%'
|
base_uri: '%point_base_url%'
|
||||||
|
|
|
@ -83,10 +83,6 @@ knp_markdown:
|
||||||
parser:
|
parser:
|
||||||
service: app.point.markdown_parser
|
service: app.point.markdown_parser
|
||||||
|
|
||||||
knp_paginator:
|
|
||||||
template:
|
|
||||||
pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig
|
|
||||||
|
|
||||||
csa_guzzle:
|
csa_guzzle:
|
||||||
profiler: '%kernel.debug%'
|
profiler: '%kernel.debug%'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Exception\Factory\Blog;
|
|
||||||
|
|
||||||
class FactoryException extends \Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Exception\Factory\Blog;
|
|
||||||
|
|
||||||
use src\PointToolsBundle\Exception\Factory\Blog\FactoryException;
|
|
||||||
|
|
||||||
class InvalidDataException extends FactoryException
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Exception\Factory\Blog;
|
|
||||||
|
|
||||||
use src\PointToolsBundle\DTO\Api\Post;
|
|
||||||
use src\PointToolsBundle\Exception\Factory\Blog\InvalidDataException;
|
|
||||||
|
|
||||||
class InvalidPostDataException extends InvalidDataException
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Post
|
|
||||||
*/
|
|
||||||
private $post;
|
|
||||||
|
|
||||||
public function __construct($message = '', Post $post, $code = 0, \Exception $previous = null)
|
|
||||||
{
|
|
||||||
$this->post = $post;
|
|
||||||
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPost(): Post
|
|
||||||
{
|
|
||||||
return $this->post;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Exception\Factory;
|
|
||||||
|
|
||||||
use src\PointToolsBundle\DTO\Api\User as UserDTO;
|
|
||||||
|
|
||||||
class InvalidUserDataException extends \Exception
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var UserDTO
|
|
||||||
*/
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
public function __construct($message = "", UserDTO $user, $code = 0, \Exception $previous = null)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUser(): UserDTO
|
|
||||||
{
|
|
||||||
return $this->user;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace src\PointToolsBundle;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
|
||||||
|
|
||||||
class SkobkinPointToolsBundle extends Bundle
|
|
||||||
{
|
|
||||||
}
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -81,7 +81,7 @@ class UpdateSubscriptionsCommand extends Command
|
||||||
$progress->start(count($usersForUpdate));
|
$progress->start(count($usersForUpdate));
|
||||||
|
|
||||||
foreach ($usersForUpdate as $user) {
|
foreach ($usersForUpdate as $user) {
|
||||||
usleep($this->pointApiDelay);
|
\usleep($this->pointApiDelay);
|
||||||
|
|
||||||
$progress->advance();
|
$progress->advance();
|
||||||
$this->logger->info('Processing @'.$user->getLogin());
|
$this->logger->info('Processing @'.$user->getLogin());
|
||||||
|
|
|
@ -66,7 +66,7 @@ class UpdateUsersPrivacyCommand extends Command
|
||||||
$progress->start(count($usersForUpdate));
|
$progress->start(count($usersForUpdate));
|
||||||
|
|
||||||
foreach ($usersForUpdate as $user) {
|
foreach ($usersForUpdate as $user) {
|
||||||
usleep($this->pointApiDelay);
|
\usleep($this->pointApiDelay);
|
||||||
|
|
||||||
$progress->advance();
|
$progress->advance();
|
||||||
$this->logger->info('Processing @'.$user->getLogin());
|
$this->logger->info('Processing @'.$user->getLogin());
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller\Api;
|
namespace App\Controller\Api;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
@ -15,9 +16,6 @@ class AbstractApiController extends AbstractController
|
||||||
], $code);
|
], $code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function createErrorResponse(string $message, int $code = 400): Response
|
protected function createErrorResponse(string $message, int $code = 400): Response
|
||||||
{
|
{
|
||||||
return $this->json([
|
return $this->json([
|
|
@ -1,22 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller\Api;
|
namespace App\Controller\Api;
|
||||||
|
|
||||||
|
use App\DTO\Api\PostsPage;
|
||||||
|
use App\Factory\Blog\PostFactory;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use JMS\Serializer\SerializerInterface;
|
use JMS\Serializer\SerializerInterface;
|
||||||
use src\PointToolsBundle\DTO\Api\PostsPage;
|
|
||||||
use src\PointToolsBundle\Service\Factory\Blogs\PostFactory;
|
|
||||||
use Symfony\Component\HttpFoundation\{Request, Response};
|
use Symfony\Component\HttpFoundation\{Request, Response};
|
||||||
use src\PointToolsBundle\Controller\Api\AbstractApiController;
|
|
||||||
|
|
||||||
class CrawlerController extends AbstractApiController
|
class CrawlerController extends AbstractApiController
|
||||||
{
|
{
|
||||||
/** @var string */
|
public function __construct(
|
||||||
private $crawlerToken;
|
private readonly string $crawlerToken,
|
||||||
|
) {
|
||||||
public function __construct(string $crawlerToken)
|
|
||||||
{
|
|
||||||
$this->crawlerToken = $crawlerToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function receiveAllPageAction(Request $request, SerializerInterface $serializer, PostFactory $postFactory, EntityManagerInterface $em): Response
|
public function receiveAllPageAction(Request $request, SerializerInterface $serializer, PostFactory $postFactory, EntityManagerInterface $em): Response
|
||||||
|
@ -24,7 +21,10 @@ class CrawlerController extends AbstractApiController
|
||||||
$remoteToken = $request->request->get('token');
|
$remoteToken = $request->request->get('token');
|
||||||
|
|
||||||
if (!$this->crawlerToken || ($this->crawlerToken !== $remoteToken)) {
|
if (!$this->crawlerToken || ($this->crawlerToken !== $remoteToken)) {
|
||||||
return $this->createErrorResponse('Token error. Please check it in crawler and API parameters.', Response::HTTP_FORBIDDEN);
|
return $this->createErrorResponse(
|
||||||
|
'Token error. Please check it in crawler and API parameters.',
|
||||||
|
Response::HTTP_FORBIDDEN,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$json = $request->request->get('json');
|
$json = $request->request->get('json');
|
|
@ -1,13 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
use src\PointToolsBundle\Entity\User;
|
use App\Entity\{SubscriptionEvent, User};
|
||||||
use src\PointToolsBundle\Entity\{SubscriptionEvent};
|
use App\Repository\SubscriptionEventRepository;
|
||||||
use src\PointToolsBundle\Repository\SubscriptionEventRepository;
|
use Symfony\Component\HttpFoundation\{JsonResponse, Response};
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
class ApiController
|
class ApiController
|
||||||
{
|
{
|
|
@ -1,9 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
use src\PointToolsBundle\Repository\SubscriptionEventRepository;
|
use App\Repository\SubscriptionEventRepository;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\{Request, Response};
|
use Symfony\Component\HttpFoundation\{Request, Response};
|
||||||
|
|
|
@ -1,30 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use App\Form\UserSearchType;
|
||||||
use src\PointToolsBundle\Form\UserSearchType;
|
use App\Repository\{SubscriptionEventRepository, SubscriptionRepository, UserRepository};
|
||||||
use src\PointToolsBundle\Repository\SubscriptionRepository;
|
|
||||||
use src\PointToolsBundle\Repository\UserRepository;
|
|
||||||
use src\PointToolsBundle\Repository\{SubscriptionEventRepository};
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\FormError;
|
use Symfony\Component\Form\FormError;
|
||||||
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
|
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
|
||||||
|
|
||||||
class MainController extends AbstractController
|
class MainController extends AbstractController
|
||||||
{
|
{
|
||||||
const AJAX_AUTOCOMPLETE_SIZE = 10;
|
private const AJAX_AUTOCOMPLETE_SIZE = 10;
|
||||||
|
|
||||||
/** @var int */
|
public function __construct(
|
||||||
private $appUserId;
|
private readonly int $appUserId,
|
||||||
|
private readonly string $appUserLogin,
|
||||||
/** @var string */
|
) {
|
||||||
private $appUserLogin;
|
|
||||||
|
|
||||||
public function __construct(int $appUserId, string $appUserLogin)
|
|
||||||
{
|
|
||||||
$this->appUserId = $appUserId;
|
|
||||||
$this->appUserLogin = $appUserLogin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function indexAction(
|
public function indexAction(
|
||||||
|
@ -33,9 +25,6 @@ class MainController extends AbstractController
|
||||||
SubscriptionRepository $subscriptionRepository,
|
SubscriptionRepository $subscriptionRepository,
|
||||||
SubscriptionEventRepository $subscriptionEventRepository
|
SubscriptionEventRepository $subscriptionEventRepository
|
||||||
): Response {
|
): Response {
|
||||||
/** @var EntityManager $em */
|
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
$form = $this->createForm(
|
$form = $this->createForm(
|
||||||
UserSearchType::class,
|
UserSearchType::class,
|
||||||
null,
|
null,
|
||||||
|
@ -66,17 +55,9 @@ class MainController extends AbstractController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns user search autocomplete data in JSON */
|
||||||
* Returns user search autocomplete data in JSON
|
public function searchUserAjaxAction(string $login, UserRepository $userRepository): JsonResponse
|
||||||
*
|
|
||||||
* @param string $login
|
|
||||||
*
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function searchUserAjaxAction(string $login, UserRepository $userRepository): Response
|
|
||||||
{
|
{
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
foreach ($userRepository->findUsersLikeLogin($login, self::AJAX_AUTOCOMPLETE_SIZE) as $user) {
|
foreach ($userRepository->findUsersLikeLogin($login, self::AJAX_AUTOCOMPLETE_SIZE) as $user) {
|
|
@ -1,11 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use src\PointToolsBundle\Entity\Blogs\Post;
|
|
||||||
use src\PointToolsBundle\Repository\Blogs\PostRepository;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
|
use App\Entity\Blog\Post;
|
||||||
|
use App\Repository\Blog\PostRepository;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class PostController extends AbstractController
|
class PostController extends AbstractController
|
||||||
|
@ -29,5 +30,4 @@ class PostController extends AbstractController
|
||||||
'post' => $postRepository->getPostWithComments($post->getId()),
|
'post' => $postRepository->getPostWithComments($post->getId()),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,17 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
use src\PointToolsBundle\Repository\Blogs\PostRepository;
|
use App\Repository\Blog\PostRepository;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\{Request, Response};
|
||||||
|
|
||||||
class PublicFeedController extends AbstractController
|
class PublicFeedController extends AbstractController
|
||||||
{
|
{
|
||||||
private const POSTS_PER_PAGE = 20;
|
private const POSTS_PER_PAGE = 20;
|
||||||
|
|
||||||
public function indexAction(Request $request, PostRepository $postRepository, PaginatorInterface $paginator)
|
public function indexAction(Request $request, PostRepository $postRepository, PaginatorInterface $paginator): Response
|
||||||
{
|
{
|
||||||
$postsPagination = $paginator->paginate(
|
$postsPagination = $paginator->paginate(
|
||||||
$postRepository->createPublicFeedPostsQuery(),
|
$postRepository->createPublicFeedPostsQuery(),
|
|
@ -1,28 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller\Telegram;
|
namespace App\Controller\Telegram;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use src\PointToolsBundle\Service\Telegram\IncomingUpdateDispatcher;
|
use src\PointToolsBundle\Service\Telegram\IncomingUpdateDispatcher;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
|
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
|
||||||
use unreal4u\TelegramAPI\Telegram\Types\Update;
|
use unreal4u\Telegram\Types\Update;
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
class WebHookController extends AbstractController
|
class WebHookController extends AbstractController
|
||||||
{
|
{
|
||||||
/** @var string */
|
public function __construct(
|
||||||
private $telegramToken;
|
private readonly string $telegramToken,
|
||||||
|
private readonly bool $debugEnabled,
|
||||||
/** @var bool */
|
) {
|
||||||
private $debug;
|
|
||||||
|
|
||||||
public function __construct(string $telegramToken, bool $debug)
|
|
||||||
{
|
|
||||||
$this->telegramToken = $telegramToken;
|
|
||||||
$this->debug = $debug;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function receiveUpdateAction(Request $request, string $token, IncomingUpdateDispatcher $updateDispatcher, LoggerInterface $logger): Response
|
public function receiveUpdateAction(Request $request, string $token, IncomingUpdateDispatcher $updateDispatcher, LoggerInterface $logger): Response
|
||||||
|
@ -31,21 +23,20 @@ class WebHookController extends AbstractController
|
||||||
throw $this->createNotFoundException();
|
throw $this->createNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = json_decode($request->getContent(), true);
|
$content = \json_decode($request->getContent(), flags: JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
$update = new Update(
|
$update = new Update(
|
||||||
$content,
|
$content,
|
||||||
$logger
|
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$updateDispatcher->process($update);
|
$updateDispatcher->process($update);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($this->debug) {
|
if ($this->debugEnabled) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$logger->addError('Telegram bot error', [
|
$logger->error('Telegram bot error', [
|
||||||
'exception' => get_class($e),
|
'exception' => get_class($e),
|
||||||
'file' => $e->getFile(),
|
'file' => $e->getFile(),
|
||||||
'line' => $e->getLine(),
|
'line' => $e->getLine(),
|
|
@ -1,32 +1,24 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\DTO\{TopUserDTO, DailyEventsDTO};
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
use src\PointToolsBundle\DTO\{TopUserDTO};
|
|
||||||
use src\PointToolsBundle\DTO\DailyEvents;
|
|
||||||
use src\PointToolsBundle\Entity\User;
|
|
||||||
use src\PointToolsBundle\Repository\UserRenameEventRepository;
|
|
||||||
use src\PointToolsBundle\Repository\UserRepository;
|
|
||||||
use src\PointToolsBundle\Repository\{SubscriptionEventRepository};
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Ob\HighchartsBundle\Highcharts\Highchart;
|
use Ob\HighchartsBundle\Highcharts\Highchart;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Repository\{SubscriptionEventRepository, UserRenameEventRepository, UserRepository};
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\{Request, Response};
|
use Symfony\Component\HttpFoundation\{Request, Response};
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class UserController extends AbstractController
|
class UserController extends AbstractController
|
||||||
{
|
{
|
||||||
/** @var TranslatorInterface */
|
public function __construct(
|
||||||
private $translator;
|
private readonly TranslatorInterface $translator,
|
||||||
|
) {
|
||||||
public function __construct(TranslatorInterface $translator)
|
|
||||||
{
|
|
||||||
$this->translator = $translator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $login
|
|
||||||
*/
|
|
||||||
public function showAction(
|
public function showAction(
|
||||||
Request $request,
|
Request $request,
|
||||||
string $login,
|
string $login,
|
||||||
|
@ -68,16 +60,16 @@ class UserController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo move to the service
|
* @param DailyEventsDTO[] $eventsByDay
|
||||||
|
*@todo move to the service
|
||||||
*
|
*
|
||||||
* @param DailyEvents[] $eventsByDay
|
|
||||||
*/
|
*/
|
||||||
private function createEventsDynamicChart(array $eventsByDay = []): Highchart
|
private function createEventsDynamicChart(array $eventsByDay = []): Highchart
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($eventsByDay as $dailyEvents) {
|
foreach ($eventsByDay as $dailyEvents) {
|
||||||
$data[$dailyEvents->getDate()->format('d.m')] = $dailyEvents->getEventsCount();
|
$data[$dailyEvents->date->format('d.m')] = $dailyEvents->eventsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->createChart('eventschart', 'line', $data, 'Events by day', 'amount');
|
return $this->createChart('eventschart', 'line', $data, 'Events by day', 'amount');
|
||||||
|
@ -93,7 +85,7 @@ class UserController extends AbstractController
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($topUsers as $topUser) {
|
foreach ($topUsers as $topUser) {
|
||||||
$data[$topUser->getLogin()] = $topUser->getSubscribersCount();
|
$data[$topUser->login] = $topUser->subscribersCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->createChart('topchart', 'bar', $data, 'Top users', 'amount');
|
return $this->createChart('topchart', 'bar', $data, 'Top users', 'amount');
|
9
src/Exception/Factory/Blog/FactoryException.php
Normal file
9
src/Exception/Factory/Blog/FactoryException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exception\Factory\Blog;
|
||||||
|
|
||||||
|
class FactoryException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
9
src/Exception/Factory/Blog/InvalidDataException.php
Normal file
9
src/Exception/Factory/Blog/InvalidDataException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exception\Factory\Blog;
|
||||||
|
|
||||||
|
class InvalidDataException extends FactoryException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
17
src/Exception/Factory/Blog/InvalidPostDataException.php
Normal file
17
src/Exception/Factory/Blog/InvalidPostDataException.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exception\Factory\Blog;
|
||||||
|
|
||||||
|
use App\DTO\Api\Post;
|
||||||
|
|
||||||
|
class InvalidPostDataException extends InvalidDataException
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly Post $post,
|
||||||
|
$code = 0,
|
||||||
|
\Exception $previous = null
|
||||||
|
) {
|
||||||
|
parent::__construct('Invalid post data', $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
17
src/Exception/Factory/InvalidUserDataException.php
Normal file
17
src/Exception/Factory/InvalidUserDataException.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exception\Factory;
|
||||||
|
|
||||||
|
use App\DTO\Api\User as UserDTO;
|
||||||
|
|
||||||
|
class InvalidUserDataException extends \Exception
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly UserDTO $user,
|
||||||
|
$code = 0,
|
||||||
|
\Exception $previous = null
|
||||||
|
) {
|
||||||
|
parent::__construct('Invalid user data', $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Exception\Telegram;
|
namespace App\Exception\Telegram;
|
||||||
|
|
||||||
|
|
||||||
class CommandProcessingException extends \Exception
|
class CommandProcessingException extends \Exception
|
||||||
{
|
{
|
|
@ -25,13 +25,13 @@ class UserFactory extends AbstractFactory
|
||||||
// @todo LOG
|
// @todo LOG
|
||||||
|
|
||||||
if (!$userData->isValid()) {
|
if (!$userData->isValid()) {
|
||||||
throw new InvalidUserDataException('Invalid user data', $userData);
|
throw new InvalidUserDataException($userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @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(),
|
(int) $userData->getId(),
|
||||||
\DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime()
|
\DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime()
|
||||||
);
|
);
|
||||||
$this->userRepository->add($user);
|
$this->userRepository->add($user);
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace src\PointToolsBundle\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
class UserSearchType extends AbstractType
|
class UserSearchType extends AbstractType
|
||||||
{
|
{
|
||||||
/**
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
* @param FormBuilderInterface $builder
|
|
||||||
* @param array $options
|
|
||||||
*/
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('login')
|
->add('login')
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\DTO\DailyEventsDTO;
|
||||||
use App\Entity\SubscriptionEvent;
|
use App\Entity\SubscriptionEvent;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
@ -101,14 +102,14 @@ class SubscriptionEventRepository extends ServiceEntityRepository
|
||||||
return $qb->getQuery()->getResult();
|
return $qb->getQuery()->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return SubscriptionEvent[] */
|
/** @return DailyEventsDTO[] */
|
||||||
public function getLastEventsByDay(int $days = 30): array
|
public function getLastEventsByDay(int $days = 30): array
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('se');
|
$qb = $this->createQueryBuilder('se');
|
||||||
|
|
||||||
$rows = $qb
|
$rows = $qb
|
||||||
->select([
|
->select([
|
||||||
'NEW Skobkin\Bundle\PointToolsBundle\DTO\DailyEvents(DAY(se.date), COUNT(se))',
|
'NEW App\DTO\DailyEventsDTO(DAY(se.date), COUNT(se))',
|
||||||
'DAY(se.date) as day',
|
'DAY(se.date) as day',
|
||||||
])
|
])
|
||||||
->groupBy('day')
|
->groupBy('day')
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\DTO\TopUserDTO;
|
||||||
|
use App\Entity\Subscription;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
@ -114,11 +115,11 @@ class UserRepository extends ServiceEntityRepository
|
||||||
*/
|
*/
|
||||||
public function getTopUsers(int $limit = 30): array
|
public function getTopUsers(int $limit = 30): array
|
||||||
{
|
{
|
||||||
$qb = $this->getEntityManager()->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s');
|
$qb = $this->getEntityManager()->getRepository(Subscription::class)->createQueryBuilder('s');
|
||||||
|
|
||||||
$rows = $qb
|
$rows = $qb
|
||||||
->select([
|
->select([
|
||||||
'NEW Skobkin\Bundle\PointToolsBundle\DTO\TopUserDTO(a.login, COUNT(s.subscriber))',
|
'NEW App\DTO\TopUserDTO(a.login, COUNT(s.subscriber))',
|
||||||
'COUNT(s.subscriber) as subscribers_count'
|
'COUNT(s.subscriber) as subscribers_count'
|
||||||
])
|
])
|
||||||
->innerJoin('s.author', 'a')
|
->innerJoin('s.author', 'a')
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
"config/packages/jms_serializer.yaml"
|
"config/packages/jms_serializer.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"knplabs/knp-paginator-bundle": {
|
||||||
|
"version": "v6.2.0"
|
||||||
|
},
|
||||||
"phpunit/phpunit": {
|
"phpunit/phpunit": {
|
||||||
"version": "9.6",
|
"version": "9.6",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|
Loading…
Reference in a new issue