Использование данных коллекции Doctrine от ассоциации OneToMany в JSON - PullRequest
0 голосов
/ 11 ноября 2018

Я видел много примеров того, как установить связь OneToMany между сущностями.Тем не менее, я не видел ничего о том, как вывести данные из ассоциации.(например, преобразование в JSON или просто наличие чистого массива)

Итак, вот пример кода:

declare(strict_types=1);

namespace Banks\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
 * https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/basic-mapping.html
 *
 * @ORM\Entity
 * @ORM\Table(name="bank")
 **/
class Banks implements \JsonSerializable
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer", name="id", nullable=false)
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * A Bank could have Many Branches
     *
     * @ORM\OneToMany(targetEntity="Branches\Entity\Branches", mappedBy="bank")
     *
     */
    protected $branches;

    /**
     * @ORM\Column(type="string", nullable=true)
     */
    protected $name;

    /**
     *
     * @return array|mixed
     */
    public function jsonSerialize()
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'branches' => $this->getBranches()
        ];
    }

    public function __construct()
    {
        $this->branches = new ArrayCollection();
    }

    public function getBranches(): Collection
    {
        return $this->branches;
    }

    // ... Other getter/setters removed
}

Тогда у нас также есть объект Branches:

declare(strict_types=1);

namespace Branches\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/basic-mapping.html
 *
 * @ORM\Entity
 * @ORM\Table(name="branches")
 **/
class Branches implements \JsonSerializable
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * A Branch has one Bank
     *
     * @ORM\ManyToOne(targetEntity="Banks\Entity\Banks", inversedBy="branches")
     * @ORM\JoinColumn(name="bank_id", referencedColumnName="id")
     */
    protected $bank;

    /**
     * @ORM\Column(type="integer", nullable=false)
     */
    protected $bank_id;

    /**
     * @ORM\Column(type="string", nullable=true)
     */
    protected $name;

    /**
     *
     * @return array|mixed
     */
    public function jsonSerialize()
    {
        return [
            'id' => $this->id,
            'bank_id' => $this->bank_id,
            'name' => $this->name,
            'bank' => $this->getBank()
        ];
    }

    public function getBank()
    {
        return $this->bank;
    }

    // ... Other getter/setters removed
}

Запрос к обоим сущностям в целом работает нормально, с вызовами $result->jsonSerialize(), затем возвращением с return new JsonResponse($result) для получения объекта JSON.Хотя запрос Филиала имеет ожидаемый результат, когда я получаю Филиал вместе с ассоциированным Банком как часть выходных данных, запрос к Банку не возвращает ассоциированные Филиалы и вместо этого отображается только как "branches": {}

Iзнаете, это потому, что $branches является коллекцией, но как вывести его таким образом, чтобы он был частью результирующего объекта JSON?

Я пробовал $this->branches->toArray(), но это приводит к массиву объектовследовательно, он не может быть закодирован в JSON и заканчивается ошибкой.

ПРИМЕЧАНИЕ. Содержимое (объект) $this->getBranches() действительно содержит ветви, как и ожидалось, что можно увидеть по $this->branches->count().Но как связаться с ними таким образом, чтобы JsonSerializable мог создать JSON?

В соответствии с запросом приведен код промежуточного программного обеспечения, оставляемый до использования Entity:

Фабрика используется для создания того, чтоТребуется обработчик:

class BanksViewHandlerFactory
{
    public function __invoke(ContainerInterface $container) : BanksViewHandler
    {
        $entityManager = $container->get(EntityManager::class);

        $entityManager->getConfiguration()->addEntityNamespace('Banks', 'Banks\Entity');

        $entityRepository = $entityManager->getRepository('Banks:Banks');

        return new BanksViewHandler($entityManager, $entityRepository);
    }
}

Фабрика вызывает обработчик:

class BanksViewHandler implements RequestHandlerInterface
{
    protected $entityManager;
    protected $entityRepository;

    public function __construct(
        EntityManager $entityManager,
        EntityRepository $entityRepository,
    ) {
        $this->entityManager = $entityManager;
        $this->entityRepository = $entityRepository;
    }

    public function handle(ServerRequestInterface $request) : ResponseInterface
    {
        $return = $this->entityRepository->find($request->getAttribute('id'));

$result['Result']['Banks'] = $return->jsonSerialize();

        return new JsonResponse($result);
    }
}

Обработчик возвращает JSON.

1 Ответ

0 голосов
/ 12 ноября 2018

Важно отметить, что при реализации интерфейса \JsonSerializable прямой вызов jsonSerialize() не возвращает JSON, и вы не вызываете этот метод явно.

Как указано в документах:

Объекты, реализующие JsonSerializable, могут настроить свое представление JSON при кодировании с помощью json_encode ().

Целью реализации этого интерфейса является принудительное применение метода jsonSerialize(), который вызывается изнутри при передаче объекта (объектов) в json_encode(); например:

$result = $banksRepository->find($id);
$json = json_encode($result);

Кроме того, если вы также хотите сериализовать дочерние сущности Branch, вам необходимо:

  1. Реализация \JsonSerializable для этого объекта (что вы сделали)
  2. Doctrine вернет эти ветви как объект ArrayCollection, содержащий все дочерние объекты ветви. Чтобы обеспечить правильное кодирование json_encode() в JSON, необходимо преобразовать ArrayCollection в массив, используя toArray().

Для иллюстрации - (как вы указали, вы также реализовали это):

public function jsonSerialize()
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'branches' => $this->getBranches()->toArray(), // <-- 
    ];
}

Это должно сериализовать ваш Банк и связанные филиалы, как и ожидалось. Надеюсь, это поможет:)

...