Symfony 5: установка свойства на основе значения родительского свойства - PullRequest
0 голосов
/ 21 июня 2020

В Symfony 5, допустим, у нас есть 3 объекта, связанных следующим образом:

  • Foo - это объект, имеющий Bar в качестве дочернего. Foo как одно свойство с именем fooProperty.
  • Bar имеет Foo как родительский, а Baz как дочерний
  • Baz, конечно, Bar как родительский. Baz имеет одно свойство, называемое bazProperty.

Допустим, значение bazProperty зависит от значения значения fooProperty. Моя первая идея заключалась в том, чтобы сослаться на сущность foo внутри класса сущности baz:

function setBazProperty($value) {
    if ($this->getBar()->getFoo()->getFooProperty > 0) {
        $this->bazProperty = $value;
    } else {
        $this->bazProperty = 0;
    }
}

Но это происходит во многих sql запросах, так как Doctrine сначала попросит получить 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);
    
}

И, наконец, в контроллере, теперь я хотел бы получить Foo объект :

$bazService = new BazService($baz);
$foo = $bazService->getFoo();

Вот мои вопросы:

  1. Я не могу инициализировать bazService в контроллере. Конструктор запрашивает 2 аргумента (сущность и репозиторий), и я хотел бы только предоставить сущность и автоматически ввести класс репозитория. Я безуспешно пытался добавить его в serices.yaml (вероятно, потому, что я не создал экземпляр bazService в конструкторе своего контроллера):

    App\Service\BazService:
        arguments:
            $bazRepository: App\Repository\BazRepository
    

Есть ли другое решение? Как я могу по-другому внедрить класс сущности в класс обслуживания?

Рекомендуемое решение - использование службы при слишком сложной настройке свойства? В некоторых статьях ( здесь , здесь и здесь ) рекомендуется использовать службу, когда метод внутри класса сущности становится более сложным и требует внешней сущности или репозиториев. Но, может быть, есть более легкое решение ...

1 Ответ

2 голосов
/ 21 июня 2020

Разделение проблем - ИМХО правильный аргумент, на который стоит взглянуть. Есть несколько подходов к go for, которые во многом зависят от того, как вы получаете объект. Однако, на мой взгляд, задача сущности НЕ состоит в том, чтобы получить данные других сущностей из базы данных, это забота репозитория или , возможно, контроллера. Итак, давайте посмотрим, как это сделать ...

Один из способов - автоматически получить родительский объект / объекты. В зависимости от вашего варианта использования вы можете сделать это в целом (через fetch="EAGER" - см .: @ ManyToOne / @ OneToOne ), в противном случае вы могли бы реализовать специальную функцию репозитория, которая выбирает дополнительные сущности. Если у ваших сущностей всегда есть не более одного родительского элемента, это может полностью сократить количество запросов с 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();
}

если дочерний объект обращается к родительскому объекту, он должен просто использовать объект из кеша и избегать второго запроса (источник: https://symfonycasts.com/screencast/doctrine-relations/join-n-plus-one)

Если сущностей уже «слишком много», вы может немного обмануть, (снова) создав собственный метод репозитория, который не только извлекает сущность 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;
}

(Отказ от ответственности: проверьте результат $ здесь, чтобы убедиться, что доступ правильный)

теперь вы можете получить доступ к нему в 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();
    }
}
...