Как правильно построить архитектуру Symfony 4?

1 раз делаю проект на Symfony и я не уверен как правильно построить архитектуру. Сначала покажу код который у меня есть, а затем вопросы.
Проект будет RestApi. Принимаем json, отдаём json в формате JSend.
Архитектура примерно такая:
Контроллер -> Сервис -> Репозиторий -> Entity
Пример: Создание продукта. У продукта есть связь ManyToOne с Workspace.
ProductController:
public function store(
        Request $request,
        ProductStoreValidation $productStoreValidation,
        ValidationTransformer $validationTransformer
    ): Response {
        $body = $request->request->all();
        $violations = $productStoreValidation->validate($body);

        if ($violations->count()) {
            $errorsData = $validationTransformer->transform($violations);
            throw new ValidationRawException($errorsData);
        }

        $product = $this->productService->addProduct($body);

        $data = [
            'product' => $product
        ];

        $response = new JSendResponse(JSendResponse::SUCCESS, $data);

        return new JsonResponse($response, Response::HTTP_CREATED);
    }

ProductService:
public function addProduct(array $body): Product
    {
        $product = new Product();
        $product->setName($body['name']);
        $product->setSku($body['sku']);
        $product->setImage($body['image']);

        $workspace = $this->workspaceService->getWorkspaceById($body['workspace_id']);
        $product->setWorkspace($workspace);

        $product = $this->productRepository->save($product);

        return $product;
    }

DoctrineProductRepository:
public function save(Product $product): Product
    {
        $this->entityManager->beginTransaction();
        try {
            $this->entityManager->persist($product);
            $this->entityManager->flush();
            $this->entityManager->commit();
        } catch (\Exception $e) {
            $this->entityManager->rollback();
            throw new ProductRepositoryException('Error while saving product', $e->getCode(), $e);
        }

        return $product;
    }


Вопросы:
1) Где будет правильно проверять "Существует ли Workspace?"? Являет ли это бизнес-логикой?
сейчас я проверяю в классе валидации, но может быть это нужно сделать в Сервисе или где-то еще?
2) Где нужно заполнять объект Product?
сейчас я просто вызываю метод сервиса и передаю ему всё тело(после валидации) и там заполняется каждое свойство. Я посмотрел много примеров с формами и там форма выполняет валидацию и возвращает объект Product, может быть нужно сделать так же, но без форм? Я не использую формы.
3) Если я перенесу валидацию с из вопрос 1 в сервис. То сервис должен формировать ошибку и вываливать экзепшен?
4) Я понимаю для чего нужен репозиторий, энтети, но не особо понимаю, что должно быть в сервисе? в контроллере?

В целом может быть есть какие-то советы? Может я совсем не так всё делаю? Я стараюсь сделать тонкие контроллеры.

Другой пример:
Обновление Product
Код примерно такой же, но в примере создания я создаю Product в сервисе, а при обновлении получаю Product в контроллере и отдаю его в сервис. Верно или это решение?
POST - /product/{id}
ProductController
public function update(
        Request $request,
        int $id,
        ProductUpdateValidation $productUpdateValidation,
        ValidationTransformer $validationTransformer
    ): Response {
        if (null === $product = $this->productService->getProductById($id)) {
            throw new NotFoundHttpException('Product not found');
        }

        $body = $request->request->all();
        if (empty($body)) {
            throw new BadRequestHttpException('Bad request');
        }

        $violations = $productUpdateValidation->validate($body);
        if ($violations->count()) {
            $errorsData = $validationTransformer->transform($violations);
            throw new ValidationRawException($errorsData);
        }

        $this->productService->updateProduct($product, $body);

        $data = [
            'product' => $product
        ];

        $response = new JSendResponse(JSendResponse::SUCCESS, $data);

        return new JsonResponse($response, Response::HTTP_OK);
    }

ProductService
public function updateProduct(Product $product, array $body): Product
    {
        if (isset($body['name'])) {
            $product->setName($body['name']);
        }

        if (isset($body['sku'])) {
            $product->setSku($body['sku']);
        }

        if (isset($body['image'])) {
            $product->setImage($body['image']);
        }

        if (isset($body['workspace_id'])) {
            $workspace = $this->workspaceService->getWorkspaceById($body['workspace_id']);
            $product->setWorkspace($workspace);
        }

        return $this->productRepository->save($product);
    }

  • Вопрос задан
  • 1383 просмотра
Пригласить эксперта
Ответы на вопрос 2
@dreamerz
Как я собираю проэкт Симфонии
composer self-update
composer require symfony/symfony-skeletone (требует лимит мемори для пхп свыше 1800М)
Никогда неделайте ничего при старте вручную - Первое правило =)
Если Вам вдруг нужна авторизация из-корробки -
php bin/console make:auth
Первый Ваш контроллер
php bin/console make:controller
> MyShopController
Нужен класс для связи с БД
php bin/console make:entity
> Shop
в процессе Вам напишут что Реппозиторий создался автомматически
Мы забыли про БД! Непугайтесь сделайте ещё пару комманд)
php bin/console doctrine:database:create
Теперь можно выгружать таблицы:
php bin/console make:migration
Теперь чтобы увидеть таблицы в самой БД скажем комманду
php bin/console doctrine:migrations:migrate
Вуаля - это Магия Симфонии)

Если что - обращайтесь

Если конкретно по-вопросу -
У меня роутер выглядит так:
app_product
path: /product/{slug}/{action}
controller: App\Controller\ProductController:indexAction

По ссылке /product/update/#id
POST ajax request
ProductController:
$post = $request->request->all(); // все POST запросы
$product = new Product;
$product->setPrice($post['price']);
...
/product/show/#id
Загружается страничка с продуктом.
Это конкретный пример логики
Ответ написан
@Imrahil
У доктрины есть Filters решит ваши беды с валидацией (на Хабре есть пару статей).
Лично мое мнение - в проектах с водопадом и вечными бизнес задачами только сервисы. Это избавит вас от бед. Больше кода - ерунда, Он(код) же ваш, в нем все структурированно и понятно. Не мешайте Entity с логикой, это не AR Yii.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы