【问题标题】:Symfony 3 - Outsourcing Controller Code into Service LayerSymfony 3 - 将控制器代码外包到服务层
【发布时间】:2016-11-15 17:20:41
【问题描述】:

我是 Symfony 3 的新手,我想避免 我的控制器中的业务逻辑。 到目前为止我所做的是:

    <?php

    namespace RestBundle\Controller;

    use RestBundle\Entity\Attribute;
    use RestBundle\Entity\DistributorProduct;
    use RestBundle\Entity\AttributeValue;
    use RestBundle\Entity\ProductToImage;
    use Symfony\Component\HttpFoundation\Request;
    use RestBundle\Entity\Product;
    use FOS\RestBundle\Controller\FOSRestController;


        /**
         * Product controller.
         *
         */
        class ProductController extends FOSRestController
        {

                /**
                 * Creates a new Product entity.
                 *
                 */
                public function createProductAction(Request $request)
                {
                    // Doctrine Manager
                    $em = $this->getDoctrine()->getManager();

                    // todo: get the logged in distributor object
                    $distributor = $em->getRepository('RestBundle:Distributor')->find(1);

                    // Main Product
                    $product = new Product();
                    $product->setEan($request->get('ean'));
                    $product->setAsin($request->get('asin'));
                    $em->persist($product);

                    // New Distributor Product
                    $distributorProduct = new DistributorProduct();
                    $distributorProduct->setDTitle($request->get('title'));
                    $distributorProduct->setDDescription($request->get('description'));
                    $distributorProduct->setDPrice($request->get('price'));
                    $distributorProduct->setDProductId($request->get('product_id'));
                    $distributorProduct->setDStock($request->get('stock'));
                    // Relate this distributorProduct to the distributor
                    $distributorProduct->setDistributor($distributor);
                    // Relate this distributorProduct to the product
                    $distributorProduct->setProduct($product);
                    $em->persist($distributorProduct);

                    // Save it
                    $em->flush();

                    $response = $em->getRepository('RestBundle:Product')->find($product->getUuid());

                    return array('product' => $response);
                }
            }
        }

我知道这不是好的代码,因为所有的业务逻辑都在控制器中。

但是,我如何以及在哪里可以将此代码(将请求设置为模型、持久化并使用原则刷新等)放入服务或对其使用依赖注入?还是为此目的的服务不是正确的方式?

我知道这个页面和教程http://symfony.com/doc/current/best_practices/business-logic.html 但我不清楚将 CRUD 操作放在哪里。 一项服务是否可以保存具有所有相关实体的整个项目?并使用 Symfony\Component\HttpFoundation\Request;在服务中?那么将整个控制器代码放在我收到请求并将模型分配给服务的地方吗? 谢谢

【问题讨论】:

  • 如果你使用某种表单来创建产品,那么使用 Symfony 表单会自动将表单数据映射到你的实体数据:symfony.com/doc/current/book/forms.html
  • 放入单独的服务并在其中注入实体管理器
  • 不,我只使用 API,所以没有视图。只接收 POST 数据,分配给不同的实体,保存,响应保存的产品。
  • 好的,但该服务的最佳实践是什么?一项服务是否可以保存具有所有相关实体的整个项目?并使用 Symfony\Component\HttpFoundation\Request;在服务中?那么把我收到请求并分配给模型的整个控制器代码放入服务中?

标签: php dependency-injection doctrine-orm symfony


【解决方案1】:

更新 2:I've extended this answer in a post。请务必检查!

更新:使用 Symfony 3.3(2017 年 5 月)与 PSR-4 service autodiscovery 和 PHP 7.1 类型。


我将向您展示我如何在公司中讲授控制器存储库解耦

有两个简单的规则:

让我们把它应用到你的控制器上

注意:这是伪代码,我没试过,但逻辑应该很容易理解。如果更改太多,只需检查步骤 3 和 4。

我们将创建和保存过程解耦。对于两个实体。 这将引导我们获得 4 项服务:

# app/config/services.yml
services:
    _defaults:  
        autowire: true

    App\Domain\:
        resource: ../../App/Domain
    App\Repository\:
        resource: ../../App/Repository

1。产品工厂解耦创建过程

// ProductFactory.php
namespace App\Domain\Product;

final class ProductFactory
{
    public function createFromRequest(Request $request): Product
    {
        $product = new Product();
        $product->setEan($request->get('ean'));
        $product->setAsin($request->get('asin'));
        return $product;
    }
}

2。经销商产品工厂解耦创建过程

// DistributorProductFactory.php
namespace App\Domain\Product;

final class DistributorProductFactory
{
    public function createFromRequestProductAndDistributor(
        Request $request,
        Product $product,
        Distributor $distributor
    ): DistributorProduct {
        $distributorProduct = new DistributorProduct();
        $distributorProduct->setDTitle($request->get('title'));
        $distributorProduct->setDDescription($request->get('description'));
        $distributorProduct->setDPrice($request->get('price'));
        $distributorProduct->setDProductId($request->get('product_id'));
        $distributorProduct->setDStock($request->get('stock'));

        // Relate this distributorProduct to the product
        $distributorProduct->setProduct($product);

        // Relate this distributorProduct to the product
        $distributorProduct->setDistributor($distributor);

        return $distributorProduct;
    }
}

3。创建自己的 ProductRepository 服务

// ProductRepository.php
namespace App\Repository;

use RestBundle\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;

final class ProductRepository
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public funtion __construct(EntityManagerInterface $entityManager)    
    {
        $this->entityManager = $entityManager;
    }

    public function save(Product $product): void
    {
        $this->entityManager->persist($product);
        $this->entityManager->flush();
    }
}

4。创建自己的 DistributorProductRepository 服务

// DistributorProductRepository.php
namespace App\Repository;

use RestBundle\Entity\DistributorProduct;
use Doctrine\ORM\EntityManagerInterface;

final class DistributorProductRepository
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public funtion __construct(EntityManagerInterface $entityManager)    
    {
        $this->entityManager = $entityManager;
    }

    public function save(DistributorProduct $distributorProduct): void
    {
        $this->entityManager->persist($distributorProduct);
        $this->entityManager->flush();
    }
}

5。我们完成了漂亮而轻薄的控制器!

namespace RestBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\FOSRestController;

final class ProductController extends FOSRestController
{
    // get here dependencies via constructor

    public function createProductAction(Request $request): array
    {
        // todo: get the logged in distributor object
        $distributor = $em->getRepository('RestBundle:Distributor')->find(1);

        $product = $this->productFactory->createFromRequest($request);
        $distributorProduct = $this->distributorProductFactory->createFromRequestProductAndDistributor(
            $request,
            $product,
            $distributor
        );

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

        return [
            'product' => $product
        ];
    }
}

就是这样!

【讨论】:

  • 很好的答案,谢谢!我将创建工厂和存储库,并检查它是如何工作的!在那之后,我会接受你的回答。非常感谢你把我推向正确的方向!
  • 如果我将它与工厂和存储库一起使用会出现错误:类型错误:参数 1 传递给 RestBundle\\Repository\\ProductRepository::__construct() 必须实现接口 Doctrine\\ORM\\ EntityManagerInterface,没有给出,在第 2729 行的 /var/www/var/cache/dev/appDevDebugProjectContainer.php 中调用,...我用我的 services.yml 中的 arguments 参数修复它:product_repository:类:RestBundle\Repository\ ProductRepository 参数:["@doctrine.orm.entity_manager"]
  • 您的解决方案有效,但它删除了非常有用的自动装配功能。要使用它,我建议添加 github.com/Symplify/ControllerAutowire bundle 和 github.com/Symplify/DefaultAutowire 但它不需要。我很高兴你喜欢它并且它有效:) 干得好!
  • 如果你需要更新数据,你应该如何使用工厂类来处理它?还有应该如何处理初始的空字段?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 2011-05-01
  • 1970-01-01
相关资源
最近更新 更多