1

I just triying to create a new service when I find this error. When I try to list the doctrine commands avaiable it show me the next error:

Doctrine\ORM\Mapping\OneToMany::__construct() must be of the type array or null, string given, called in /var/www/html/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php

I try to reset the entity responsable of it without results. Here is the all trace:

TypeError {#478
#message: "Argument 3 passed to Doctrine\ORM\Mapping\OneToMany::__construct() must be of the type array or null, string given, called in /var/www/html/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php on line 971"
  #code: 0
  #file: "./vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php"
  #line: 44
  trace: {
    ./vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php:44 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php:971 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php:719 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php:376 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php:178 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php:155 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php:88 { …}
    ./vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php:98 { …}
    ./vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php:331 { …}
    ./vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/Driver/MappingDriverChain.php:79 { …}
    ./vendor/doctrine/doctrine-bundle/Mapping/MappingDriver.php:45 { …}
    ./vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:134 { …}
    ./vendor/doctrine/doctrine-bundle/Mapping/ClassMetadataFactory.php:19 { …}
    ./vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php:382 { …}
    ./vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php:251 { …}
    ./vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:303 { …}
    ./var/cache/dev/ContainerX9n3NQZ/EntityManager_9a5be93.php:94 {
      ContainerX9n3NQZ\EntityManager_9a5be93->getClassMetadata($className)^
      › 
      ›     return $this->valueHolderda610->getClassMetadata($className);
      › }
      arguments: {
        $className: "App\Entity\App\City"
      }
    }
    ./vendor/doctrine/doctrine-bundle/Repository/ServiceEntityRepository.php:45 { …}
    ./src/Repository/App/CityRepository.php:26 { …}
    ./var/cache/dev/ContainerX9n3NQZ/getCityRepositoryService.php:27 { …}
    ./var/cache/dev/ContainerX9n3NQZ/App_KernelDevDebugContainer.php:525 { …}
    ./var/cache/dev/ContainerX9n3NQZ/getCityServiceService.php:23 { …}
    ./var/cache/dev/ContainerX9n3NQZ/App_KernelDevDebugContainer.php:525 { …}
    ./var/cache/dev/ContainerX9n3NQZ/getDoctrine_FixturesLoadCommandService.php:43 { …}
    ./var/cache/dev/ContainerX9n3NQZ/App_KernelDevDebugContainer.php:525 { …}
    ./vendor/symfony/dependency-injection/Container.php:422 { …}
    ./vendor/symfony/dependency-injection/Argument/ServiceLocator.php:42 { …}
    ./vendor/symfony/console/CommandLoader/ContainerCommandLoader.php:45 { …}
    ./vendor/symfony/console/Application.php:551 { …}
    ./vendor/symfony/console/Application.php:519 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:126 { …}
    ./vendor/symfony/console/Application.php:664 { …}
    Symfony\Component\Console\Application->Symfony\Component\Console\{closure}() {}
    ./vendor/symfony/console/Application.php:665 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:116 { …}
    ./vendor/symfony/console/Application.php:254 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:82 { …}
    ./vendor/symfony/console/Application.php:166 { …}
    ./bin/console:43 { …}
  }
}

Here is the City entity:

<?php

namespace App\Entity\App;

use App\DBAL\Types\Geolocation\Point;
use App\Entity\App\Graveyard;
use App\Repository\App\CityRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
 * @ORM\Entity(repositoryClass=CityRepository::class)
 * @ORM\Table (name="location", schema="app")
 */
class City
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column (type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255, nullable=false)
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $country;

    /**
     * @ORM\OneToMany(targetEntity=Company::class, mappedBy="city", cascade="persist")
     */
    private $companies;

    /**
     * @ORM\OneToMany(targetEntity=Graveyard::class, mappedBy="city", cascade="persist")
     */
    private $graveyards;

    /**
     * @ORM\OneToMany(targetEntity=FuneralParlor::class, mappedBy="city", cascade="persist")
     */
    private $funeralParlors;

    /**
     * @ORM\OneToMany(targetEntity=AdvertisementTransfer::class, mappedBy="city")
     */
    private $advertisementTransfers;

    /**
     * @ORM\OneToMany(targetEntity=Crematorium::class, mappedBy="city")
     */
    private $crematoria;

    /**
     * @ORM\Column(type="point")
     */
    private $coordinate;

    /**
     * @ORM\Column(type="string", length=255, nullable=true, options={"default" : null})
     */
    private $province;

    public function __construct()
    {
        $this->companies = new ArrayCollection();
        $this->graveyards = new ArrayCollection();
        $this->funeralParlors = new ArrayCollection();
        $this->advertisementTransfers = new ArrayCollection();
        $this->crematoria = new ArrayCollection();
    }

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getCountry(): ?string
    {
        return $this->country;
    }

    public function setCountry(?string $country): self
    {
        $this->country = $country;

        return $this;
    }

    /**
     * @return Collection|Company[]
     */
    public function getCompanies(): Collection
    {
        return $this->companies;
    }

    public function addCompany(Company $company): self
    {
        if (!$this->companies->contains($company)) {
            $this->companies[] = $company;
            $company->setCity($this);
        }

        return $this;
    }

    public function removeCompany(Company $company): self
    {
        if ($this->companies->removeElement($company)) {
            // set the owning side to null (unless already changed)
            if ($company->getCity() === $this) {
                $company->setCity(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Graveyard[]
     */
    public function getGraveyards(): Collection
    {
        return $this->graveyards;
    }

    public function addGraveyard(Graveyard $graveyard): self
    {
        if (!$this->graveyards->contains($graveyard)) {
            $this->graveyards[] = $graveyard;
            $graveyard->setCity($this);
        }

        return $this;
    }

    public function removeGraveyard(Graveyard $graveyard): self
    {
        if ($this->graveyards->removeElement($graveyard)) {
            // set the owning side to null (unless already changed)
            if ($graveyard->getCity() === $this) {
                $graveyard->setCity(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|FuneralParlor[]
     */
    public function getFuneralParlors(): Collection
    {
        return $this->funeralParlors;
    }

    public function addFuneralParlor(FuneralParlor $funeralParlor): self
    {
        if (!$this->funeralParlors->contains($funeralParlor)) {
            $this->funeralParlors[] = $funeralParlor;
            $funeralParlor->setCity($this);
        }

        return $this;
    }

    public function removeFuneralParlor(FuneralParlor $funeralParlor): self
    {
        if ($this->funeralParlors->removeElement($funeralParlor)) {
            // set the owning side to null (unless already changed)
            if ($funeralParlor->getCity() === $this) {
                $funeralParlor->setCity(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|AdvertisementTransfer[]
     */
    public function getAdvertisementTransfers(): Collection
    {
        return $this->advertisementTransfers;
    }

    public function addAdvertisementTransfer(AdvertisementTransfer $advertisementTransfer): self
    {
        if (!$this->advertisementTransfers->contains($advertisementTransfer)) {
            $this->advertisementTransfers[] = $advertisementTransfer;
            $advertisementTransfer->setCity($this);
        }

        return $this;
    }

    public function removeAdvertisementTransfer(AdvertisementTransfer $advertisementTransfer): self
    {
        if ($this->advertisementTransfers->removeElement($advertisementTransfer)) {
            // set the owning side to null (unless already changed)
            if ($advertisementTransfer->getCity() === $this) {
                $advertisementTransfer->setCity(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Crematorium[]
     */
    public function getCrematoria(): Collection
    {
        return $this->crematoria;
    }

    public function addCrematorium(Crematorium $crematorium): self
    {
        if (!$this->crematoria->contains($crematorium)) {
            $this->crematoria[] = $crematorium;
            $crematorium->setCity($this);
        }

        return $this;
    }

    public function removeCrematorium(Crematorium $crematorium): self
    {
        if ($this->crematoria->removeElement($crematorium)) {
            // set the owning side to null (unless already changed)
            if ($crematorium->getCity() === $this) {
                $crematorium->setCity(null);
            }
        }

        return $this;
    }

    public function getCoordinate(): Point
    {
        return $this->coordinate;
    }

    public function setCoordinate($coordinate): self
    {
        $this->coordinate = $coordinate;

        return $this;
    }

    public function toJsonArray(): array
    {
        $coordinate = $this->getCoordinate();
        return [
            "id" => $this->getId(),
            "name" => $this->getName(),
            "country" => $this->getCountry(),
            "coordinate" => $coordinate->toJsonArray()
        ];
    }

    public function getProvince(): ?string
    {
        return $this->province;
    }

    public function setProvince(?string $province): self
    {
        $this->province = $province;

        return $this;
    }
}

City Repository:

<?php

namespace App\Repository\App;

use App\DBAL\Types\Geolocation\Point;
use App\Entity\App\City;
use App\Exception\City\CityAlreadyExistException;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\FetchMode;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Persistence\ManagerRegistry;

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

    /**
     * Get all cities on DDBB
     * @return int|mixed|string
     */
    public function getAllCities() {
        return $this->createQueryBuilder("c")->where('')->orderBy('name')->getQuery()->getResult();
    }

    public function getCityById(int $id): City {
        return $this->createQueryBuilder('c')->where('id = :id')->setParameter('id', $id)->getQuery()->getResult();
    }

    public function getCityByName(string $name): ?City
    {
        $city = $this->createQueryBuilder('c')->where('name = :name')->setParameter('name', $name)->getQuery()->getResult();
        return $city === null ? null : $city;
    }

    /**
     * @throws CityAlreadyExistException
     */
    public function createCity(City $city): ?City
    {
        try {
            $entityManager = $this->getEntityManager();
            $entityManager->persist($city);
            $entityManager->flush();
            return $city;
        } catch (ORMException $e) {
            throw new CityAlreadyExistException(sprintf("City %s already exist", $city->getName()));
        }
    }

    public function calculateDistance(City $city, $lat, $lon)
    {

        $SQL = "
            SELECT (ST_Distance(ST_MakePoint({$lat}, {$lon})::geography, ST_MakePoint(coordinate[0], coordinate[1])::geography) / 1000) as distance
            FROM app.location
            WHERE id = {$city->getId()}
        ";

        return $this->executeSQL($SQL);
    }

    public function calculateDistanceBetweenPointInKm(Point $origin, Point $destiny)
    {
        $SQL = "
            SELECT (ST_DISTANCE
            (
                ST_MakePoint({$origin->getLongitude()}, {$origin->getLatitude()})::geography,
                ST_MakePoint({$destiny->getLongitude()}, {$destiny->getLatitude()})::geography
            ) / 1000) as distance
        ";

        return $this->executeSQL($SQL)[0];
    }

    private function executeSQL(string $SQL)
    {
        $em = $this->getEntityManager();
        $stmt = $em->getConnection()->prepare($SQL);
        $stmt->execute();
        return $stmt->fetchAll(FetchMode::COLUMN);
    }
}

Service Entity Repository (This is by default from doctrine and symfony):

class ServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
    /**
     * @param string $entityClass The class name of the entity this repository manages
     * @psalm-param class-string<T> $entityClass
     */
    public function __construct(ManagerRegistry $registry, string $entityClass)
    {
        $manager = $registry->getManagerForClass($entityClass);

        if ($manager === null) {
            throw new LogicException(sprintf(
                'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
                $entityClass
            ));
        }

        parent::__construct($manager, $manager->getClassMetadata($entityClass));
    }
}
  • 1
    You are prone to SQL injection, you should prepare your request, even when trying to execute rawSQL. See example here: https://stackoverflow.com/questions/3325012/execute-raw-sql-using-doctrine-2 – Dylan KAS Oct 19 '21 at 12:52
  • I have not problems with rawSQL. It was working until yesterday. Today I was developing a new tool to access Google Drive Api. When I try to upload the changes I get that error. I didn't touch the city class for a long time. And it was working. – Alexander Álvarez Oct 19 '21 at 12:59
  • I know I nees to clean my code and modulate it. One of my task for the future days will be to abstract the raw SQL execuition of all classes. But my problem right now is I can't use doctrine because I got this error. – Alexander Álvarez Oct 19 '21 at 13:01
  • read the error, the problem is with your OneToMany annotations, check them and you'll find what's wrong. – yivi Oct 19 '21 at 13:11
  • I triying but I can't find anything. It was like my first upload, and didnt change. I dont know why the show that error. – Alexander Álvarez Oct 19 '21 at 13:41
  • If u see the deatils, they show the argument is a String => "App\Entity\App\City", and he expected an array or null – Alexander Álvarez Oct 19 '21 at 13:42

1 Answers1

9

SOLVED - Searching, with time, and patient, I found an error on ORM notation. The composer json file was updated by a teammate, and with the new versión the cascade must to be with brackets, In one place it was the open but not closed.