Как установить учение ассоциации? - PullRequest
0 голосов
/ 09 февраля 2019

Я знаю, что свойство ассоциации в сущности - это \Doctrine\Common\Collections\Collection.Я знаю, что в конструкторе должны быть инициализированы такие свойства:

$this->collection = new \Doctrine\Common\Collections\ArrayCollection()

Я знаю, что могу изменять коллекции, используя ArrayCollection#add() и ArrayCollection#remove().Однако у меня есть другой случай.

Предположим, у меня есть новый простой массив ассоциативных объектов.Используя существующие методы, мне нужно проверить каждый элемент в массиве: есть ли в коллекции сущностей.Если нет - добавить элемент массива в коллекцию сущностей.В дополнение к этому мне нужно проверить каждый элемент в коллекции сущностей.Если какой-либо элемент коллекции отсутствует в новом массиве, то мне нужно удалить его из коллекции.Так много работы, чтобы сделать тривиальные вещи.

Что я хочу?Чтобы реализовать метод setProducts:

class Entity {
  private $products;

  // ... constructor

  public function setProducts(array $products)
  {
    // synchronize $products with $this->products
  }
}

Я попытался: $this->products = new ArrayCollection($products).Однако это заставляет доктрину удалять все продукты и добавлять их из параметра $products.Мне нужен аналогичный результат, но без запросов к базе данных.

Есть ли какое-либо встроенное решение в Doctrine для такого случая?

Редактировать : Я хотел бы иметь метод в ArrayCollection как fromArray, который объединяет элементы в коллекциях, удаляя ненужные.Это просто дублирует вызовы add/remove для каждого элемента в аргументе коллекции вручную.

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Я бы подошел к этому, создав собственный класс коллекции, который расширяет класс коллекции массивов Doctrine:

use Doctrine\Common\Collections\ArrayCollection;

class ProductCollection extends ArrayCollection
{
}

В самой сущности вы бы инициализировали его в __constructor:

public function __construct()
{
    $this->products = new ProductCollection();
}

Здесь, Doctrine, вы будете использовать свой класс коллекции для результатов продукта.После этого вы можете добавить свою собственную функцию для обработки вашего специального слияния, возможно что-то:

public function mergeProducts(ProductCollection $products): ProductCollection
{
    $result = new ProductCollection();
    foreach($products as $product) {
        $add = true;
        foreach($this->getIterator() as $p) {
            if($product->getId() === $p->getId()) {
                $result->add($product);
                $add = false;
            }
        }
        if($add) {
            $result->add($product);
        }
    }
    return $result;
}

Она вернет совершенно новую коллекцию продуктов, которую вы можете заменить своей другой коллекцией в сущности.Однако, если сущность присоединена и находится под контролем доктрины, это отразит SQL на другом конце. Если вы хотите играть с сущностью, не рискуя обновлениями базы данных, вам необходимо отсоединить сущность:

$entityManager->detach($productEntity);

Надеждыэто помогает

0 голосов
/ 09 февраля 2019

Коллекции Doctrine не имеют функции «слияния», которая будет добавлять / удалять сущности из массива или Collection в другой коллекции.

Если вы хотите «упростить» процесс ручного слияния, который вы описываете, используя add / remove, вы можете использовать array_merge, предполагая, что оба массива не являются числовыми, но вместо этого имеют какой-то уникальный ключ, например, сущность spl_object_hash:

public function setProducts(array $products)
{
    $this->products = new ArrayCollection(
        array_merge(
            array_combine(
                array_map('spl_object_hash', $this->products->toArray()),
                $this->products->toArray()
            ),
            array_combine(
                array_map('spl_object_hash', $products),
                $products->toArray()
            )
        )
    );
}

Возможно, вы захотите использовать идентификатор продукта вместо spl_object_hash как 2 продукта с одинаковым идентификатором, но созданные как отдельные объекты - например, от одного до findBy() в Doctrine и один вручнуюсозданный с помощью new Product() - будет распознан как 2 разных продукта и может вызвать еще одну попытку вставки.

Поскольку вы заменяете исходную коллекцию PersistentCollection, содержащую ранее выбранные продукты, новой ArrayCollection, это все равно может привести к ненужным запросам илиОднако при очистке EntityManager можно получить неожиданные результаты.Не говоря уже о том, что этот подход может быть сложнее для чтения, чем явный вызов addElement / removeElement вместо исходной коллекции.

...