Doctrine Repository 不解析私有属性,而是 returns Symfony 6 中的一个空对象

Doctrine Repository does not resolve private properties, instead returns an empty object in Symfony 6

这可能是个愚蠢的问题。但是对于许多框架来说,一件非常令人沮丧的事情是当一些东西被期望如此简单和直接以至于它甚至没有被提及时,然后当它需要几个小时才能弄清楚时非常令人沮丧。

我正在重新学习 Symfony,我有一个简单的 API 动物实体,并且我在数据库中创建了一些条目。

现在我不想使用迁移,因为我希望能够手动设计架构。 我想要的只是让存储库 return 所有对象。 它做得很好,但不知何故,它每次 return 都是一个空对象。 我意识到这是因为属性是私有的。 现在我已经创建了我自己的方法,它基本上使用 getter 来提供对属性的访问,但这似乎违背了使用 ORM 的全部目的。

我花了两天时间试图找到文档,但找不到任何东西。这让我觉得很明显我必须使用 getter 来获取属性,但这是我希望开箱即用的东西。想要

所以我的问题基本上是,这是怎么回事?还是我错过了一些直接的东西?我之所以问这个问题,是因为我发现框架不会提供开箱即用的功能,这让我很惊讶。

已安装的捆绑包:

<?php

return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
    Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
];

这是学说配置文件:

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'

        # IMPORTANT: You MUST configure your server version,
        # either here or in the DATABASE_URL env var (see .env file)
        #server_version: '13'
    orm:
        auto_generate_proxy_classes: true
        #-> Simply comment this out: Fixes the issue of column name underscores naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App

when@test:
    doctrine:
        dbal:
            # "TEST_TOKEN" is typically set by ParaTest
            dbname_suffix: '_test%env(default::TEST_TOKEN)%'

when@prod:
    doctrine:
        orm:
            auto_generate_proxy_classes: false
            query_cache_driver:
                type: pool
                pool: doctrine.system_cache_pool
            result_cache_driver:
                type: pool
                pool: doctrine.result_cache_pool

    framework:
        cache:
            pools:
                doctrine.result_cache_pool:
                    adapter: cache.app
                doctrine.system_cache_pool:
                    adapter: cache.system

这是简单的 API:

的代码
//AnimalController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\AnimalRepository;

class AnimalController extends AbstractController
{

    private $repository;

    public function __construct(AnimalRepository $repository)
    {
       $this->repository = $repository;
    }

    #[Route('/api/animals', name: 'animals')]
    public function index(): Response
    {
        return $this->json(['animals' => $this->repository->getAnimals()]);
    }

    #[Route('/api/animals/{habitat}', name: 'animalsByHabitat')]
    public function getAnimalsByHabitat(string $habitat):Response
    {
        return $this->json([
            'byHabitat' => $habitat,
            'animals' => $this->repository->findByHabitat($habitat)
        ]);
    }
}


//Animal.php
<?php

namespace App\Entity;

use App\Repository\AnimalRepository;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: AnimalRepository::class)]
class Animal
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private $commonName;

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private $scientificName;

    #[ORM\Column(type: 'decimal', precision: 8, scale: 2, nullable: true)]
    private $weight;

    #[ORM\Column(type: 'integer', nullable: true)]
    private $lifeSpan;

    #[ORM\Column(type: 'string', length: 100)]
    private $integument;

    #[ORM\Column(type: 'boolean', nullable: true)]
    private $activeAllYear;

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private $foodType;

    #[ORM\Column(type: 'boolean', nullable: true)]
    private $territorial;

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private $habitat;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCommonName(): ?string
    {
        return $this->commonName;
    }

    public function setCommonName(?string $commonName): self
    {
        $this->commonName = $commonName;

        return $this;
    }

    public function getScientificName(): ?string
    {
        return $this->scientificName;
    }

    public function setScientificName(?string $scientificName): self
    {
        $this->scientificName = $scientificName;

        return $this;
    }

    public function getWeight(): ?string
    {
        return $this->weight;
    }

    public function setWeight(?string $weight): self
    {
        $this->weight = $weight;

        return $this;
    }

    public function getLifeSpan(): ?int
    {
        return $this->lifeSpan;
    }

    public function setLifeSpan(?int $lifeSpan): self
    {
        $this->lifeSpan = $lifeSpan;

        return $this;
    }

    public function getIntegument(): ?string
    {
        return $this->integument;
    }

    public function setIntegument(string $integument): self
    {
        $this->integument = $integument;

        return $this;
    }

    public function getActiveAllYear(): ?bool
    {
        return $this->activeAllYear;
    }

    public function setActiveAllYear(?bool $activeAllYear): self
    {
        $this->activeAllYear = $activeAllYear;

        return $this;
    }

    public function getFoodType(): ?string
    {
        return $this->foodType;
    }

    public function setFoodType(?string $foodType): self
    {
        $this->foodType = $foodType;

        return $this;
    }

    public function getTerritorial(): ?bool
    {
        return $this->territorial;
    }

    public function setTerritorial(?bool $territorial): self
    {
        $this->territorial = $territorial;

        return $this;
    }

    public function getHabitat(): ?string
    {
        return $this->habitat;
    }

    public function setHabitat(?string $habitat): self
    {
        $this->habitat = $habitat;

        return $this;
    }
}


//AnimalRepository.php
<?php

namespace App\Repository;

use App\Entity\Animal;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method Animal|null find($id, $lockMode = null, $lockVersion = null)
 * @method Animal|null findOneBy(array $criteria, array $orderBy = null)
 * @method Animal[]    findAll()
 * @method Animal[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class AnimalRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Animal::class);
    }

    /**
     * Seems like this might need to be done everytime?
     * With the normal Twig Controllers this was done automatically,
     * I don't think it's about serialiser, the Json method does it,
     * Its about getting these getters & setters to work, I don't want to do this everytime
     */
    public function getAnimals():array
    {
       
        $animalsObj = $this->findAll();
        $animals = [];

        foreach ($animalsObj as $animal) {
            $animals[] = $this->getArrayFromPrivateProps($animal);
        }

        return $animals;

   }


    
    /** 
     * @return Animal[] Returns an array of Animal objects
     * 
    */
    public function findByHabitat(string $habitat)
    {
        $animals = $this->createQueryBuilder('a')
            ->andWhere('a.habitat = :habitat')
            ->setParameter('habitat', $habitat)
            ->getQuery()
            ->getResult()
        ;

        return $animals;
    }

    private function getArrayFromPrivateProps(Animal $animal):array
    {
        return array(
            'id' => $animal->getId(),
            'commonName' => $animal->getCommonName(),
            'scientificName' => $animal->getScientificName(),
            'weight' => $animal->getWeight(),
            'lifeSpan' => $animal->getLifeSpan(),
            'habitat' => $animal->getHabitat(),
            'integument' => $animal->getIntegument(),
            'activeAllYear' => $animal->getActiveAllYear(),
            'foodType' => $animal->getFoodType(),
            'territorial' => $animal->getTerritorial()
        );
    }
   
}

我很难相信我每次想要访问实体道具时都需要创建这个方法? p.s。我已将访问修饰符更改为 public 然后它们就可以正常工作了。

我使用的是 Symfony 6,但 API 不是完整的应用程序。我需要带什么特别的包来实现这个吗?

结果如下:

编辑 1:

我已经从存储库中执行了 dd($this->findAll()) 并且工作正常。见截图:

然后当我执行 dd(json_encode($this->findAll())) 时,我得到同样的错误,其中编码方法无法转换对象。 Symfony 文档确实说 json_encode 可以用作 Serialise 组件的替代品?

万一其他人也面临同样的问题: 将以下代码放入 framework.yaml 文件有效:

# config/packages/framework.yaml
framework:
    # ...
    serializer:
        default_context:
            enable_max_depth: true

文档在这里:https://symfony.com/doc/current/serializer.html

我只是偶然发现它,但是,它应该开箱即用