Symfony 5:基于父属性值设置属性

Symfony 5:基于父属性值设置属性,symfony,service,repository,entity,Symfony,Service,Repository,Entity,在Symfony 5中,假设有3个实体链接如下: Foo是一个将Bar作为子项的实体Foo作为一个名为fooProperty的属性 Bar将Foo作为父项,将Baz作为子项 Baz当然要将Bar作为父项Baz有一个名为bazProperty的属性 假设bazProperty的值取决于fooProperty的值。我的第一个想法是在baz实体类中引用foo实体: function setBazProperty($value) { if ($this->getBar()->ge

在Symfony 5中,假设有3个实体链接如下:

  • Foo
    是一个将
    Bar
    作为子项的实体
    Foo
    作为一个名为
    fooProperty
    的属性
  • Bar
    Foo
    作为父项,将
    Baz
    作为子项
  • Baz
    当然要将Bar作为父项
    Baz
    有一个名为
    bazProperty
    的属性
假设
bazProperty
的值取决于
fooProperty
的值。我的第一个想法是在
baz
实体类中引用
foo
实体:

function setBazProperty($value) {
    if ($this->getBar()->getFoo()->getFooProperty > 0) {
        $this->bazProperty = $value;
    } else {
        $this->bazProperty = 0;
    }
}
但这会发生在许多sql查询中,因为条令会要求首先获取
Bar
实体,然后获取
Foo
实体

因此,我设想通过存储库类中管理的唯一查询来访问
Foo
实体

但是,由于,我不会将存储库注入
Baz
实体,而是使用服务

因此,我创建了一个
BazService
,构造函数中有两个参数:

public function __construct(Baz $baz, BazRepository $bazRepository)
{
    
    $this->baz = $baz;
    $this->bazRepository= $bazRepository;
    
}
在这个服务中,我还添加了一个获取
Foo
实体的方法:

public function getFoo()
{
    
    return $this->bazRepository->getFoo($this->baz);
    
}
$bazService = new BazService($baz);
$foo = $bazService->getFoo();
最后,在控制器中,现在我想得到
Foo
实体:

public function getFoo()
{
    
    return $this->bazRepository->getFoo($this->baz);
    
}
$bazService = new BazService($baz);
$foo = $bazService->getFoo();
以下是我的问题:

  • 我无法在控制器中初始化服务。构造函数要求2个参数(实体和存储库),我只想提供实体并自动注入存储库类。 我尝试将其添加到series.yaml中,但没有成功(可能是因为我没有在控制器的构造函数中实例化
    bazService
    ):

  • 还有其他解决办法吗?如何在服务类中以不同的方式注入实体类

  • 当设置属性太复杂时使用服务是推荐的解决方案吗?一些文章(、和)建议在实体类内的方法变得更复杂并且需要外部实体或存储库时使用服务。但也许有一个更轻的解决方案

  • 关注点分离是一个正确的论据。有一些方法可以采用,这在很大程度上取决于如何检索实体。然而,在我看来,一个实体所关心的不是从数据库中获取其他实体的数据,而是存储库或控制器的问题。那么让我们看看如何做到这一点

    一种方法是自动检索父实体。根据您的用例,您通常可以这样做(通过
    fetch=“EAGER”
    -请参见:/),否则您可以实现一个特殊的存储库函数,用于获取其他实体。如果您的实体总是每个实体最多只有一个父实体,这绝对可以将查询数量从3减少到1,因为可以同时检索父实体的父实体和父实体

    // in BazRepository
    public function getWithParents($id) {
       $qb = $this->createQueryBuilder('baz');
       $qb->leftJoin('baz.bar', 'bar')
          ->addSelect('bar')
          ->leftJoin('bar.foo', 'foo')
          ->addSelect('foo')
          ->where('baz.id = :id')
          ->setParameter('id', $id);
       return $qb->getQuery()->getOneOrNullResult();
    }
    
    如果子实体访问父实体,它应该只使用缓存中的实体,并避免第二次查询(源:)

    如果实体已经“太多”,您可以(再次)创建一个自定义存储库方法,该方法不仅获取Baz实体,还获取Foo.fooProperty值,并将其设置为Baz实体上的虚拟/临时属性,从而稍微作弊

    // in BazRepository
    public function getWithFooProperty(int $id) {
        $qb = $this->createQueryBuilder('baz');
        $qb->leftJoin('baz.bar', 'bar')
           ->lefTJoin('bar.foo', 'foo')
           ->select('foo.fooProperty as fooProperty')
           ->where('baz.id = :id')
           ->setParameter('id', $id);
        $result = $qb->getQuery()->getResult(); // should be an array with an array with two keys, but I might be wrong
        if(count($result) == 0) {
            return null;
        }
        $baz = $row[0][0];
        $baz->fooProperty = $row[0][1];
    
        return $baz;
    }
    
    (免责声明:请在此处检查$result,以查看访问是否正确)

    您现在可以在Baz中访问它:

    function getFooProperty() {
        if(isset($this->fooProperty)) {
            return $this->fooProperty;
        } else {
            // fallback, in case entity was fetched by another repository method
            return $this->getBar()->getFoo()->getFooProperty();
        }
    }
    

    关注点分离是一个正确的论据。有一些方法可以采用,这在很大程度上取决于如何检索实体。然而,在我看来,一个实体所关心的不是从数据库中获取其他实体的数据,而是存储库或控制器的问题。那么让我们看看如何做到这一点

    一种方法是自动检索父实体。根据您的用例,您通常可以这样做(通过
    fetch=“EAGER”
    -请参见:/),否则您可以实现一个特殊的存储库函数,用于获取其他实体。如果您的实体总是每个实体最多只有一个父实体,这绝对可以将查询数量从3减少到1,因为可以同时检索父实体的父实体和父实体

    // in BazRepository
    public function getWithParents($id) {
       $qb = $this->createQueryBuilder('baz');
       $qb->leftJoin('baz.bar', 'bar')
          ->addSelect('bar')
          ->leftJoin('bar.foo', 'foo')
          ->addSelect('foo')
          ->where('baz.id = :id')
          ->setParameter('id', $id);
       return $qb->getQuery()->getOneOrNullResult();
    }
    
    如果子实体访问父实体,它应该只使用缓存中的实体,并避免第二次查询(源:)

    如果实体已经“太多”,您可以(再次)创建一个自定义存储库方法,该方法不仅获取Baz实体,还获取Foo.fooProperty值,并将其设置为Baz实体上的虚拟/临时属性,从而稍微作弊

    // in BazRepository
    public function getWithFooProperty(int $id) {
        $qb = $this->createQueryBuilder('baz');
        $qb->leftJoin('baz.bar', 'bar')
           ->lefTJoin('bar.foo', 'foo')
           ->select('foo.fooProperty as fooProperty')
           ->where('baz.id = :id')
           ->setParameter('id', $id);
        $result = $qb->getQuery()->getResult(); // should be an array with an array with two keys, but I might be wrong
        if(count($result) == 0) {
            return null;
        }
        $baz = $row[0][0];
        $baz->fooProperty = $row[0][1];
    
        return $baz;
    }
    
    (免责声明:请在此处检查$result,以查看访问是否正确)

    您现在可以在Baz中访问它:

    function getFooProperty() {
        if(isset($this->fooProperty)) {
            return $this->fooProperty;
        } else {
            // fallback, in case entity was fetched by another repository method
            return $this->getBar()->getFoo()->getFooProperty();
        }
    }
    

    非常固执己见的问题;o/我同意这个问题有点具体,但我在更新依赖于父属性的属性时面临一个真正的问题。我想用最优雅的解决方案来编写一个干净而全面的代码。顺便说一句:如果一个“服务”需要创建一个实体,我会觉得它有点臭。IMHO语法应该是
    $bazService->getFoo($baz)
    。在你的控制器中,你可以通过控制器方法签名请求一个
    BazService
    ,然后通过依赖注入获得它。我同意你的看法。在使用服务时,将实体作为参数传递似乎不是很方便。我还尝试创建一个类
    bazService
    ,它扩展了实体
    baz
    。但这在坚持实体性的同时也带来了问题,并没有解决利益分离的问题;o/我同意这个问题有点具体,但我在更新依赖于父属性的属性时面临一个真正的问题。我想用最优雅的解决方案来写一篇cl