Orm: Criteria in Many-To-Many relations broken if not manually accessing data before or EAGER used

Created on 16 May 2016  路  1Comment  路  Source: doctrine/orm

I updated doctrine/orm and doctrine/dbal to dev-master some time ago to be able to use Criteria in Many-To-Many relations (with https://github.com/doctrine/doctrine2/pull/885). Suddenly today (now I'm not sure if it ever worked but I would say it did when using the same function in other places) I wasn't getting any objects in the collections of any of the sides. The only way it worked was If I added a bogus loop before the function with Criteria code was called (I suppose because the objects were being loaded beforehand and then they were available) or if I added fetch="EAGER" in the ManyToMany relation.

I'll try to resume my code bellow as best as I can. The function with the Criteria is getActiveServices inside the Resource class. It's used to get the Services from the Resource that are available depending on their start/end date (both can have a value or be null at the same time: no dates, only start date, only end date or both dates specified).

The action in the controller:

    public function servicesLoadAction(Request $request, Business $business)
    {
        $params = $request->getMethod() === 'POST' ? $request->request->all() : $request->query->all();
        $resource = array_key_exists('resource', $params) ? $params['resource'] : null;

        if (!is_null($resource)) {
            $resource = $this->getDoctrine()->getRepository('AppBundle:Resource')->find($resource);
        }
/*
        // The bogus loop that make this work without using fetch="EAGER"
        foreach ($business->getResources() as $resource) {
            $i = $resource->getServices()->count();
        }
*/
        $scheduleManager = $this->get('app.model.schedule');
        if (!is_null($resource)) {
            $services = $scheduleManager->getAvailableResourceServices($business, $resource);
        } else {
            $services = $scheduleManager->getAvailableServices($business);
        }

        return new JsonResponse($this->json_decode_group($services, 'service_minimum'));
    }

The model:

    public function getAvailableResourceServices(Business $business, \AppBundle\Entity\Resource $resource)
    {
        if ($business->getEnabledHumanResources()->contains($resource)) {
            return $resource->getActiveServices(true); // THIS IS THE FUNCTION WITH Criteria
        }

        return array();
    }

Resource class:

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\ResourceRepository")
 * @ORM\Table(name="resources")
 */
class Resource
{
    /**
     * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\Service", mappedBy="resources", cascade={"persist","remove"})
     */
    protected $services;


    /**
     * Add service
     *
     * @param \AppBundle\Entity\Service $service
     * @return Resource
     */
    public function addService(Service $service)
    {
        $service->addResource($this);
        if (!$this->services->contains($service)) {
            $this->services[] = $service;
        }

        return $this;
    }

    /**
     * Remove service
     *
     * @param \AppBundle\Entity\Service $service
     */
    public function removeService(Service $service)
    {
        $service->getResources()->removeElement($this);
        $this->services->removeElement($service);
    }

    /**
     * Get services
     *
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getServices()
    {
        return $this->services;
    }

    /**
     * Get active services
     *
     * @param boolean $onlyVisible
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getActiveServices($onlyVisible = true)
    {
        $now = new DateTime();

        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('status', Service::STATUS_ENABLED))
            ->andWhere(Criteria::expr()->orX(
                Criteria::expr()->andX(
                    Criteria::expr()->isNull('dateStart'),
                    Criteria::expr()->isNull('dateEnd')
                ),
                Criteria::expr()->andX(
                    Criteria::expr()->lt('dateStart', $now),
                    Criteria::expr()->isNull('dateEnd')
                ),
                Criteria::expr()->andX(
                    Criteria::expr()->isNull('dateStart'),
                    Criteria::expr()->gt('dateEnd', $now)
                ),
                Criteria::expr()->andX(
                    Criteria::expr()->lt('dateStart', $now),
                    Criteria::expr()->gt('dateEnd', $now)
                )
            ))
        ;

        if ($onlyVisible) {
            $criteria->andWhere(Criteria::expr()->eq('visible', true));
        }

        return $this->getServices()->matching($criteria);
    }

    /**
     * Set services
     *
     * @param array
     * @return Resource
     */
    public function setServices($services)
    {
        $this->services = new ArrayCollection();

        if (!is_null($services)) {
            /** @var \AppBundle\Entity\Service $service */
            foreach ($services as $service) {
                $this->addService($service);
            }
        }

        return $this;
    }
}

Service class:

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\ServiceRepository")
 * @ORM\Table(name="services")
 */
class Service
{
    /**
     * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\Resource", inversedBy="services", cascade={"persist","remove"})
     * @ORM\JoinTable(name="resources_services")
     */
    protected $resources;

    /**
     * Add resource
     *
     * @param \AppBundle\Entity\Resource $resource
     * @return Service
     */
    public function addResource(\AppBundle\Entity\Resource $resource)
    {
        $resource->addService($this);
        if (!$this->resources->contains($resource)) {
            $this->resources[] = $resource;
        }

        return $this;
    }

    /**
     * Remove resource
     *
     * @param \AppBundle\Entity\Resource $resource
     */
    public function removeResource(\AppBundle\Entity\Resource $resource)
    {
        $resource->getServices()->removeElement($this);
        $this->resources->removeElement($resource);
    }

    /**
     * Get resources
     *
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getResources()
    {
        return $this->resources;
    }

    /**
     * Get enabled human resources
     *
     * @param boolean $onlyVisible
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getEnabledHumanResources($onlyVisible = true)
    {
        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('status', Resource::STATUS_ENABLED))
            ->andWhere(Criteria::expr()->eq('type', Resource::TYPE_HUMAN))
            ->orderBy(array('name' => Criteria::ASC))
        ;

        if ($onlyVisible) {
            $criteria->andWhere(Criteria::expr()->eq('visible', true));
        }

        return $this->getResources()->matching($criteria);
    }

    /**
     * Get enabled resources
     *
     * @param boolean $onlyVisible
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getEnabledResources($onlyVisible = true)
    {
        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('status', Resource::STATUS_ENABLED))
            ->orderBy(array('name' => Criteria::ASC))
        ;

        if ($onlyVisible) {
            $criteria->andWhere(Criteria::expr()->eq('visible', true));
        }

        return $this->getResources()->matching($criteria);
    }

    /**
     * Set resources
     *
     * @param array
     * @return Service
     */
    public function setResources($resources)
    {
        $this->resources = new ArrayCollection();

        if (!is_null($resources)) {
            /** @var \AppBundle\Entity\Resource $resource */
            foreach ($resources as $resource) {
                $resource->addService($this);
                $this->addResource($resource);
            }
        }

        return $this;
    }
}

Most helpful comment

Criteria is broken in so many ways, I'm not sure why it's included in stable releases, it's totally unfinished.

>All comments

Criteria is broken in so many ways, I'm not sure why it's included in stable releases, it's totally unfinished.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

doctrinebot picture doctrinebot  路  4Comments

doctrinebot picture doctrinebot  路  3Comments

doctrinebot picture doctrinebot  路  3Comments

podorozhny picture podorozhny  路  4Comments

neomerx picture neomerx  路  4Comments