【问题标题】:Symfony / Doctrine: Restful API design and design pattern about repository injectionSymfony / Doctrine:关于存储库注入的 Restful API 设计和设计模式
【发布时间】:2018-08-06 07:18:48
【问题描述】:

我是 Symfony 的新手,这些问题是在最近的学习过程中提出的。

以商店为例,我将创建两个实体,Product 和 Category,它们具有双向多对一关系。

class Product
{
    private $id;
    private $name;
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
     */
    private $category;
}

class Category
{
    private $id;
    private $name;
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category")
     */
    private $products;
}

所以我的第一个问题是:

如果我想获取特定类别中的所有产品,URL应该是

/categories?categoryId=1&limit=20&orderBy=name (我知道这有点傻,但是 Category 记录应该包含所有 Product 信息吗?)

/products?categoryId=1&limit=20&orderBy=name

对于后一个问题,这里是第二个问题

我将 ProductRepository 注入到 ProductController

class ProductController extends Controller
{
    private $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    ...
}

为了获取一个类别中的所有产品,我写了一个这样的方法:

public function findByCategory(Category $category): array
{
    return $this->createQueryBuilder('p')
        ->andWhere('p.category = :category')
        ->setParameter('category', $category)
        ->orderBy('p.name', 'ASC')
        ->setMaxResults(20)
        ->getQuery()
        ->getResult()
    ;
}

那么,在 ProductController 中,我应该如何从 URL 中的查询字符串 'categoryId' 中获取 Category 对象?我应该也注入 CategoryRepository 还是应该简单地注入一个实体管理器对象?

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    Marco Pivetta aka Ocramius(Doctrine 的主要开发者之一)说:

    避免双向关联

    双向关联是开销

    只编写您的域逻辑工作所需的代码

    破解复杂的 DQL 查询,而不是通过双向性使它们变得更简单

    所以也许 you don't need bi-directional association这里。

    对于您的第一个问题,我认为第二个解决方案更好:

    /products?categoryId=1&limit=20&orderBy=name

    对于你的第二个问题,是的,如果你想访问 Category 对象,你应该注入 CategoryRepository,即使可能,也要避免访问控制器中的整个 entityManager。

    您应该在控制器中注入 服务。您的服务应该公开公共方法以通过data mappers 对数据库执行自定义 CRUD 访问。请注意,repository 不是数据映射器,而是它

    在域和数据映射层之间进行调解,就像内存中的域对象集合一样。

    EAA 目录的 P - Martin Fowler

    Repositories are services 实际上可以将它们注入控制器。

    有些人认为存储库不应包含 CREATEUPDATEDELETE,而应仅包含 READ。他们说这些操作使集合(可通过存储库访问)不一致。

    这篇文章也可以提供帮助:How should a model be structured in MVC?

    【讨论】:

    • 你为什么说:'避免在控制器中注入存储库'?主要是为了“尊重” MVC 吗?不错的链接顺便说一句,很好的材料。
    • @Elbarto 实际上我编辑了我的帖子。没有理由避免在控制器中注入存储库。感谢您向我指出这一点。
    • 谢谢!这非常鼓舞人心。唯一的问题是我能知道为什么注入实体管理器是一种不好的做法吗?
    • @yifei3212 这是不好的做法,因为您将使用实体管理器来检索调用方法的其他对象(存储库),这违反了得墨忒耳法则 (en.wikipedia.org/wiki/Law_of_Demeter)。你可以看看这篇有趣的文章:matthiasnoback.nl/2014/05/…
    【解决方案2】:

    对我来说,这里的问题是 ORM 在 Symfony 中的作用与数据库设计、建模和查询之间非常明显的混淆。

    在数据库中(使用 PhpMyAdmin),您会注意到 product 表中有一个名为 category(或 category_id)的列。保持简单,要获得属于某个类别的产品,您只需要 category_id 即可。将这些知识带到 Symfony,您不需要类别对象,请使用您从请求中获得的类别 ID。此外,只需在控制器中使用 EntityManager,不要使事情复杂化,尤其是因为您似乎才刚刚开始。

    use Symfony\Component\HttpFoundation\Request;
    
    class ProductController extends Controller
    {
        public function get_product_from_categoryAction(Request $request)
        {
            $category_id = (int) $request->get('category');
            $limit = (int) $request->get('limit');
            $orderBy = strip_tags($request->get('orderBy'));
    
            $em = $this->getDoctrine()->getManager();
            $products = $em
                ->getRepository('AppBundle:Products')
                ->queryProductsByCategoryId($category_id, $limit, $orderBy);
    
        }
    
        ...
    }
    

    还有回购

    public function queryProductsByCategoryId(int $category_id, int $limit = 10, string $orderBy = 'name')
    {
        return $this->createQueryBuilder('p')
            ->andWhere('p.category = :category')
            ->setParameter('category', $category_id)
            ->orderBy('p.name', 'ASC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult()
        ;
    }
    

    保持简单,然后当你需要更高级时,如果你愿意,可以尝试更花哨的东西。

    【讨论】:

    • 感谢您的回答。我完全同意你从简单的东西开始,但实际上我已经使用 Laravel 有一段时间了,所以我已经熟悉了大部分基本的东西 :)
    • 太好了,所以你不应该经历最艰难的调整。只需专注于首先让 Symfony 工作,然后再对其进行定制。我在上面为您发布的内容将做到这一点。一切顺利。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-24
    相关资源
    最近更新 更多