Docker update

This commit is contained in:
Ernest 2022-08-10 11:04:45 +02:00
parent b057c11327
commit fa3ea4e6a3
15 changed files with 182 additions and 167 deletions

30
.dockerignore Normal file
View file

@ -0,0 +1,30 @@
**/*.log
**/*.md
**/*.php~
**/*.dist.php
**/*.dist
**/*.cache
**/._*
**/.dockerignore
**/.DS_Store
**/.git/
**/.gitattributes
**/.gitignore
**/.gitmodules
**/docker-compose.*.yaml
**/docker-compose.*.yml
**/docker-compose.yaml
**/docker-compose.yml
**/Dockerfile
**/Thumbs.db
.github/
docs/
public/bundles/
tests/
var/
vendor/
.editorconfig
.env.*.local
.env.local
.env.local.php
.env.test

View file

@ -8,8 +8,20 @@ ARG PHP_VERSION=8.1
ARG CADDY_VERSION=2
ARG ELASTIC_VERSION=7.4.0
# "php" stage
FROM php:${PHP_VERSION}-fpm-alpine AS symfony_php
# Prod image
FROM php:${PHP_VERSION}-fpm-alpine AS app_php
# Allow to use development versions of Symfony
ARG STABILITY="stable"
ENV STABILITY ${STABILITY}
# Allow to select Symfony version
ARG SYMFONY_VERSION=""
ENV SYMFONY_VERSION ${SYMFONY_VERSION}
ENV APP_ENV=prod
WORKDIR /srv/app
# persistent / runtime deps
RUN apk add --no-cache \
@ -29,7 +41,6 @@ RUN apk add --no-cache \
apk-cron \
;
ARG APCU_VERSION=5.1.21
RUN set -eux; \
apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
@ -51,7 +62,7 @@ RUN set -eux; \
docker-php-ext-configure sysvsem; \
docker-php-ext-install -j$(nproc) sysvsem; \
pecl install \
apcu-${APCU_VERSION} \
apcu \
imagick \
; \
# pecl install \
@ -71,51 +82,10 @@ RUN set -eux; \
| sort -u \
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
)"; \
apk add --no-cache --virtual .phpexts-rundeps $runDeps; \
apk add --no-cache --virtual .app-phpexts-rundeps $runDeps; \
\
apk del .build-deps
COPY docker/php/docker-healthcheck.sh /usr/local/bin/docker-healthcheck
RUN chmod +x /usr/local/bin/docker-healthcheck
HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD ["docker-healthcheck"]
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY docker/php/conf.d/symfony.prod.ini $PHP_INI_DIR/conf.d/symfony.ini
#RUN echo "extension=mongodb.so" > $PHP_INI_DIR/conf.d/mongodb.ini
COPY docker/php/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf
COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint
VOLUME /var/run/php
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH="${PATH}:/root/.composer/vendor/bin"
WORKDIR /srv/app
# Allow to choose skeleton
ARG SKELETON="symfony/skeleton"
ENV SKELETON ${SKELETON}
# Allow to use development versions of Symfony
ARG STABILITY="stable"
ENV STABILITY ${STABILITY}
# Allow to select skeleton version
ARG SYMFONY_VERSION=""
ENV SYMFONY_VERSION ${SYMFONY_VERSION}
# Download the Symfony skeleton and leverage Docker cache layers
RUN composer create-project "symfony/skeleton ${SYMFONY_VERSION}" . --stability=$STABILITY --prefer-dist --no-dev --no-progress --no-interaction; \
composer clear-cache
###> recipes ###
###> doctrine/doctrine-bundle ###
RUN apk add --no-cache --virtual .pgsql-deps postgresql-dev; \
@ -125,22 +95,75 @@ RUN apk add --no-cache --virtual .pgsql-deps postgresql-dev; \
###< doctrine/doctrine-bundle ###
###< recipes ###
COPY . .
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY docker/php/conf.d/app.ini $PHP_INI_DIR/conf.d/
COPY docker/php/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/
#RUN echo "extension=mongodb.so" > $PHP_INI_DIR/conf.d/mongodb.ini
RUN set -eux; \
mkdir -p var/cache var/log; \
composer install --prefer-dist --no-dev --no-progress --no-scripts --no-interaction; \
composer dump-autoload --classmap-authoritative --no-dev; \
composer symfony:dump-env prod; \
# @todo important: building bug 224a94dac9e166867c62db6b6145d23ff1393432
# composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync
VOLUME /srv/app/var
COPY docker/php/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf
RUN mkdir -p /var/run/php
COPY docker/php/docker-healthcheck.sh /usr/local/bin/docker-healthcheck
RUN chmod +x /usr/local/bin/docker-healthcheck
HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD ["docker-healthcheck"]
COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint
ENTRYPOINT ["docker-entrypoint"]
CMD ["php-fpm"]
FROM caddy:${CADDY_VERSION}-builder-alpine AS symfony_caddy_builder
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH="${PATH}:/root/.composer/vendor/bin"
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# prevent the reinstallation of vendors at every changes in the source code
COPY composer.* symfony.* ./
RUN set -eux; \
if [ -f composer.json ]; then \
composer install --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress; \
composer clear-cache; \
fi
# copy sources
COPY . .
RUN rm -Rf docker/
RUN set -eux; \
mkdir -p var/cache var/log; \
if [ -f composer.json ]; then \
composer dump-autoload --classmap-authoritative --no-dev; \
composer dump-env prod; \
composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync; \
fi
# Dev image
FROM app_php AS app_php_dev
ENV APP_ENV=dev XDEBUG_MODE=offr
VOLUME /srv/app/var/
RUN rm $PHP_INI_DIR/conf.d/app.prod.ini; \
mv "$PHP_INI_DIR/php.ini" "$PHP_INI_DIR/php.ini-production"; \
mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY docker/php/conf.d/app.dev.ini $PHP_INI_DIR/conf.d/
RUN set -eux; \
apk add --no-cache --virtual .build-deps $PHPIZE_DEPS; \
pecl install xdebug; \
docker-php-ext-enable xdebug; \
apk del .build-deps
RUN rm -f .env.local.php
# Build Caddy with the Mercure and Vulcain modules
FROM caddy:${CADDY_VERSION}-builder-alpine AS app_caddy_builder
RUN xcaddy build \
--with github.com/dunglas/mercure \
@ -148,13 +171,13 @@ RUN xcaddy build \
--with github.com/dunglas/vulcain \
--with github.com/dunglas/vulcain/caddy
FROM caddy:${CADDY_VERSION} AS symfony_caddy
# Caddy image
FROM caddy:${CADDY_VERSION} AS app_caddy
WORKDIR /srv/app
COPY --from=dunglas/mercure:v0.11 /srv/public /srv/mercure-assets/
COPY --from=symfony_caddy_builder /usr/bin/caddy /usr/bin/caddy
COPY --from=symfony_php /srv/app/public public/
COPY --from=app_caddy_builder /usr/bin/caddy /usr/bin/caddy
COPY --from=app_php /srv/app/public public/
COPY docker/caddy/Caddyfile /etc/caddy/Caddyfile
#FROM symfony_php as symfony_php_debug

View file

@ -62,10 +62,16 @@ Based on [https://github.com/dunglas/symfony-docker](https://github.com/dunglas/
$ cp .env.example .env
$ docker compose build --pull --no-cache
$ docker compose up
$ SERVER_NAME="app.localhost, caddy:80" docker compose up --build
$ docker compose exec php bin/console doctrine:fixtures:load
$ docker compose exec php bin/phpunit
# Using Xdebug
# Linux / Mac
$ XDEBUG_MODE=debug docker compose up -d
# Windows
$ set XDEBUG_MODE=debug&& docker compose up -d&set XDEBUG_MODE=
```
#### Production

View file

@ -14,7 +14,7 @@ ap_nodeinfo:
methods: [ GET ]
ap_shared_inbox:
controller: App\Controller\ActivityPub\InboxController
controller: App\Controller\ActivityPub\SharedInboxController
path: /f/inbox
methods: [ POST, GET ]
condition: '%kbin_ap_route_condition%'

View file

@ -3,20 +3,25 @@ version: "3.4"
# Development environment override
services:
php:
build:
target: app_php_dev
volumes:
# The "cached" option has no effect on Linux but improves performance on Mac
- ./:/srv/app:rw,cached
- ./docker/php/conf.d/symfony.dev.ini:/usr/local/etc/php/conf.d/symfony.ini
# If you develop on Mac you can remove the var/ directory from the bind-mount
# for better performance by enabling the next line
# - /srv/app/var
- ./:/srv/app
- ./docker/php/conf.d/app.dev.ini:/usr/local/etc/php/conf.d/app.dev.ini:ro
# If you develop on Mac or Windows you can remove the vendor/ directory
# from the bind-mount for better performance by enabling the next line:
#- /srv/app/vendor
environment:
APP_ENV: dev
# See https://xdebug.org/docs/all_settings#mode
XDEBUG_MODE: "${XDEBUG_MODE:-off}"
extra_hosts:
# Ensure that host.docker.internal is correctly defined on Linux
- host.docker.internal:host-gateway
caddy:
volumes:
- ./docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./public:/srv/app/public:ro
- ./docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
###> symfony/mercure-bundle ###
###< symfony/mercure-bundle ###

View file

@ -4,7 +4,6 @@ version: "3.4"
services:
php:
environment:
APP_ENV: prod
APP_SECRET: ${APP_SECRET}
MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET}

View file

@ -4,10 +4,9 @@ services:
php:
build:
context: .
target: symfony_php
target: app_php
args:
SYMFONY_VERSION: ${SYMFONY_VERSION:-}
SKELETON: ${SKELETON:-symfony/skeleton}
STABILITY: ${STABILITY:-stable}
restart: unless-stopped
volumes:
@ -19,7 +18,7 @@ services:
start_period: 30s
environment:
# Run "composer require symfony/orm-pack" to install and configure Doctrine ORM
DATABASE_URL: postgresql://${POSTGRES_USER:-symfony}:${POSTGRES_PASSWORD:-ChangeMe}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-13}
DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-14}
# Run "composer require symfony/mercure-bundle" to install and configure the Mercure integration
MERCURE_URL: ${CADDY_MERCURE_URL:-http://caddy/.well-known/mercure}
MERCURE_PUBLIC_URL: https://${SERVER_NAME:-localhost}/.well-known/mercure
@ -30,7 +29,7 @@ services:
caddy:
build:
context: .
target: symfony_caddy
target: app_caddy
depends_on:
- php
environment:

View file

@ -0,0 +1,5 @@
; See https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host
; See https://github.com/docker/for-linux/issues/264
; The `client_host` below may optionally be replaced with `discover_client_host=yes`
; Add `start_with_request=yes` to start debug session on each request
xdebug.client_host = 'host.docker.internal'

View file

@ -4,7 +4,7 @@ apc.enable_cli = 1
session.use_strict_mode = 1
zend.detect_unicode = 0
# https://symfony.com/doc/current/performance.html
; https://symfony.com/doc/current/performance.html
realpath_cache_size = 4096K
realpath_cache_ttl = 600
opcache.interned_strings_buffer = 16

View file

@ -0,0 +1,2 @@
opcache.preload_user = www-data
opcache.preload = /srv/app/config/preload.php

View file

@ -1,15 +0,0 @@
expose_php = 0
date.timezone = UTC
apc.enable_cli = 1
session.use_strict_mode = 1
zend.detect_unicode = 0
# https://symfony.com/doc/current/performance.html
realpath_cache_size = 4096K
realpath_cache_ttl = 600
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.memory_consumption = 256
opcache.enable_file_override = 1
opcache.preload_user=www-data
opcache.preload=/srv/app/config/preload.php

View file

@ -7,16 +7,11 @@ if [ "${1#-}" != "$1" ]; then
fi
if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
if [ "$APP_ENV" != 'prod' ]; then
ln -sf "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
fi
mkdir -p var/cache var/log
# The first time volumes are mounted, the project needs to be recreated
# Install the project the first time PHP is started
# After the installation, the following block can be deleted
if [ ! -f composer.json ]; then
CREATION=1
composer create-project "$SKELETON $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install
composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install
cd tmp
composer require "php:>=$PHP_VERSION"
@ -28,11 +23,11 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
fi
if [ "$APP_ENV" != 'prod' ]; then
rm -f .env.local.php
composer install --prefer-dist --no-progress --no-interaction
fi
if grep -q ^DATABASE_URL= .env; then
# After the installation, the following block can be deleted
if [ "$CREATION" = "1" ]; then
echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build"
sleep infinity
@ -59,7 +54,7 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
echo "The db is now ready and reachable"
fi
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
bin/console doctrine:migrations:migrate --no-interaction
fi
fi

View file

@ -1,62 +0,0 @@
<?php declare(strict_types=1);
namespace App\Controller\ActivityPub;
use App\Message\ActivityPub\ActivityMessage;
use App\Repository\ApActivityRepository;
use App\Service\ActivityPub\MarkdownConverter;
use App\Service\MentionManager;
use League\HTMLToMarkdown\HtmlConverter;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Messenger\MessageBusInterface;
class InboxController
{
public function __construct(
private MessageBusInterface $bus,
private MentionManager $mentionManager,
private ApActivityRepository $repository,
private MarkdownConverter $markdownConverter
) {
}
public function __invoke(Request $request): JsonResponse
{
// $headers = [
// 'cdn-loop' => [0 => 'cloudflare',],
// 'cf-ray' => [0 => '735f30da48c79b39-FRA',],
// 'cf-ipcountry' => [0 => 'DE',],
// 'content-length' => [0 => '243',],
// 'content-type' => [0 => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',],
// 'user-agent' => [0 => '(Pixelfed/0.11.3; +https://pixelfed.krbn.pl)',],
// 'digest' => [0 => 'SHA-256=tSbQexNXVn9NtYcHxGI6DAXewWMeS+PupreH6ZfFOVY=',],
// 'cf-connecting-ip' => [0 => '2a01:4f8:1c0c:71a5::1',],
// 'x-forwarded-proto' => [0 => 'https',],
// 'date' => [0 => 'Fri, 05 Aug 2022 11:28:42 GMT',],
// 'host' => [0 => 'dev.karab.in',],
// 'x-forwarded-host' => [0 => 'dev.karab.in',],
// 'accept' => [0 => 'application/activity+json, application/json',],
// 'accept-encoding' => [0 => 'gzip',],
// 'signature' => [0 => 'keyId="https://pixelfed.krbn.pl/users/admin#main-key",headers="(request-target) date host accept digest content-type user-agent",algorithm="rsa-sha256",signature="PsicH5fDtEzZu+vegSfkaBynlgIGTey8cEYiZFtCyjZ5TwbpjlFPukIf48a+ms0UlHAmjTqC+XTYvbyf6/W3xW+pJ5dIXSlc/fYyRo6F8oQfcSdFH9az/zvyDbjfAly90nauucYEBrHXf382VwYZZa4MLICYcyy4CkPUwdY1VlX1Y2+oLb4sy9wGx7gXAb9gzwp1oAA80wJPOV59QE1Y5yw+nEkL8OQqquL2VRp+BQyNEFnwVmlpfP0Q8sqrc2fDGV9wKLBmHL5jH+X/IvkUfx12ZF7CDojgutp3QzgXYckrB6o/aIC2h6Zybq2E9c/LJeBRc13oIi/nU4vHcY4cBw=="',],
// 'cf-visitor' => [0 => '{"scheme":"https"}',],
// 'x-forwarded-for' => [0 => '162.158.91.106',],
// 'x-php-ob-level' => [0 => '1',],
// ];
//
//$body = '{"@context":["https://www.w3.org/ns/activitystreams",{"ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","sensitive":"as:sensitive","toot":"http://joinmastodon.org/ns#","votersCount":"toot:votersCount","blurhash":"toot:blurhash","focalPoint":{"@container":"@list","@id":"toot:focalPoint"}}],"id":"https://101010.pl/users/ernest/statuses/108771776341780181","type":"Note","summary":null,"inReplyTo":"https://mastodon.internet-czas-dzialac.pl/users/arek/statuses/108770638597320024","published":"2022-08-05T18:53:11Z","url":"https://101010.pl/@ernest/108771776341780181","attributedTo":"https://101010.pl/users/ernest","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://101010.pl/users/ernest/followers","https://mastodon.internet-czas-dzialac.pl/users/arek"],"sensitive":false,"atomUri":"https://101010.pl/users/ernest/statuses/108771776341780181","inReplyToAtomUri":"https://mastodon.internet-czas-dzialac.pl/users/arek/statuses/108770638597320024","conversation":"tag:mastodon.internet-czas-dzialac.pl,2022-08-05:objectId=3746:objectType=Conversation","content":"\u003cp\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.internet-czas-dzialac.pl/@arek\" class=\"u-url mention\"\u003e@\u003cspan\u003earek\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e właśnie kilka dni temu wspominałeś tu o YunoHost, sprawdziłem, totalny gamechanger. popchnął moją prace nad federacją karabina o tygodnie. doskonale sprawdza się jako mały domowy lab. skrypt instalacyjny yuno również dodany do todo-listy z wysokim priorytetem, dzięki.\u003c/p\u003e","contentMap":{"pl":"\u003cp\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.internet-czas-dzialac.pl/@arek\" class=\"u-url mention\"\u003e@\u003cspan\u003earek\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e właśnie kilka dni temu wspominałeś tu o YunoHost, sprawdziłem, totalny gamechanger. popchnął moją prace nad federacją karabina o tygodnie. doskonale sprawdza się jako mały domowy lab. skrypt instalacyjny yuno również dodany do todo-listy z wysokim priorytetem, dzięki.\u003c/p\u003e"},"attachment":[{"type":"Document","mediaType":"image/jpeg","url":"https://storage.waw.cloud.ovh.net/v1/AUTH_74714a37e6e24c7fb695d79be309da62/101010public/media_attachments/files/108/771/775/326/424/520/original/35dab55585c7fd71.jpeg","name":null,"blurhash":"UNEoc0mltQS6~WMxtQxuD%ozM{RjIUtRo3WA","width":2005,"height":1034}],"tag":[{"type":"Mention","href":"https://mastodon.internet-czas-dzialac.pl/users/arek","name":"@arek@mastodon.internet-czas-dzialac.pl"}],"replies":{"id":"https://101010.pl/users/ernest/statuses/108771776341780181/replies","type":"Collection","first":{"type":"CollectionPage","next":"https://101010.pl/users/ernest/statuses/108771776341780181/replies?only_other_accounts=true\u0026page=true","partOf":"https://101010.pl/users/ernest/statuses/108771776341780181/replies","items":[]}}}';
$test = '@mrk@101010.pl @jaczad@tfl.net.pl eh, cieszę się, że nie zajmuję @ernest się frontendem. Jeżeli w tym całym galimatiasie html/php/js/css... musiałbym jeszcze pamiętać o aria to faktycznie witki opadają :D';
dd($this->mentionManager->extract($test));
$this->bus->dispatch(new ActivityMessage($body, $headers));
// $this->bus->dispatch(new ActivityMessage($request->getContent(), $request->headers->all()));
$response = new JsonResponse();
$response->headers->set('Content-Type', 'application/activity+json');
return $response;
}
}

View file

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace App\Controller\ActivityPub;
use App\Service\MentionManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Messenger\MessageBusInterface;
class SharedInboxController
{
public function __construct(
private MessageBusInterface $bus,
private MentionManager $mentionManager,
private LoggerInterface $logger
) {
}
public function __invoke(Request $request): JsonResponse
{
$this->logger->error($request->getContent(), (string) $request->headers);
$response = new JsonResponse();
$response->headers->set('Content-Type', 'application/activity+json');
return $response;
}
}

View file

@ -20,7 +20,6 @@ use App\Service\PostManager;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use League\HTMLToMarkdown\HtmlConverter;
class Note
{
@ -89,7 +88,7 @@ class Note
ActivityPubActivityInterface $parent,
?ActivityPubActivityInterface $root = null
): ActivityPubActivityInterface {
$dto = new EntryCommentDto();
$dto = new EntryCommentDto();
if ($parent instanceof EntryComment) {
$dto->parent = $parent;
$dto->root = $parent->root ?? $parent;
@ -117,7 +116,7 @@ class Note
ActivityPubActivityInterface $parent,
?ActivityPubActivityInterface $root = null
): ActivityPubActivityInterface {
$dto = new PostCommentDto();
$dto = new PostCommentDto();
if ($parent instanceof PostComment) {
$dto->parent = $parent;
}