API Platform version(s) affected: 2.4.5
Description
Custom or ApiPlatform filtering nested resource dont work in an entity with composite primary key
How to reproduce
Create an entity A with composite key, and add a ManyToOne relation to entity B.
Create an entity B with single id key and other fields.
Create a custom or a api-platform filter in the entity A to a field from entity B (can be entity B primary key, or not)
Additional Context
The filtering works when entity A has a single id key.
Example Entities *ENTITY A
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\NumericFilter;
use App\Filter\TipoSancionFilter;
/**
* AntecedentesOSanciones
*
* @ORM\Entity
* @ApiResource(
* normalizationContext={"groups"={"getCollectionAntecedentes"}},
* )
* @ApiFilter(SearchFilter::class , properties={"codTipoSancion.codTipoSancion":"exact", "codTipoSancion.nombreSancion":"exact"})
*/
class AntecedentesOSanciones
{
/**
* @var \PadronEscribanos
*
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
* @ORM\ManyToOne(targetEntity="PadronEscribanos" , inversedBy="antecedentes")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="matricula", referencedColumnName="matricula")
* })
* @Groups({ "getCollectionAntecedentes"})
*/
private $matricula;
/**
* @var int
*
* @ORM\Column(name="renglon", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
* @Groups({"getCollectionAntecedentes"})
*/
private $renglon;
/**
* @var \TiposSanciones
*
* @ORM\ManyToOne(targetEntity="TiposSanciones")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="cod_tipo_sancion", referencedColumnName="cod_tipo_sancion")
* })
* @Groups({"getCollectionAntecedentes"})
*/
private $codTipoSancion;
public function getRenglon(): ?int
{
return $this->renglon;
}
public function setRenglon($renglon): self
{
$this->renglon = $renglon;
return $this;
}
public function getCodTipoSancion(): ?TiposSanciones
{
return $this->codTipoSancion;
}
public function setCodTipoSancion(?TiposSanciones $codTipoSancion): self
{
$this->codTipoSancion = $codTipoSancion;
return $this;
}
public function getMatricula(): ?PadronEscribanos
{
return $this->matricula;
}
public function setMatricula(?PadronEscribanos $matricula): self
{
$this->matricula = $matricula;
return $this;
}
}
*ENTITY B
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* TiposSanciones
*
* @ORM\Entity
* @ApiResource
*/
class TiposSanciones
{
/**
* @var int
*
* @ORM\Column(name="cod_tipo_sancion", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @Groups({"getCollectionAntecedentes"})
*/
private $codTipoSancion;
/**
* @var string
*
* @ORM\Column(name="nombre_sancion", type="string", length=36, nullable=false)
* @Groups({"getCollectionAntecedentes"})
*/
private $nombreSancion;
public function getCodTipoSancion(): ?int
{
return $this->codTipoSancion;
}
public function getNombreSancion(): ?string
{
return $this->nombreSancion;
}
public function setNombreSancion(string $nombreSancion): self
{
$this->nombreSancion = $nombreSancion;
return $this;
}
}
**ERROR OUTPUT *Example get url: http://localhost:8282/api/antecedentes_o_sanciones?codTipoSancion.codTipoSancion=4
[
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:1,
codTipoSancion:{
codTipoSancion:1,
nombreSancion:"SUSPENSIÓN "
}
},
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:2,
codTipoSancion:{
codTipoSancion:1,
nombreSancion:"SUSPENSIÓN "
}
},
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:3,
codTipoSancion:{
codTipoSancion:1,
nombreSancion:"SUSPENSIÓN "
}
},
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:4,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
},
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:5,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
},
{
matricula:{
matricula:570,
nombres:"MARCELO MIGUEL",
apellidos:"GILETTA"
},
renglon:1,
codTipoSancion:{
codTipoSancion:1,
nombreSancion:"SUSPENSIÓN "
}
},
{
matricula:{
matricula:570,
nombres:"MARCELO MIGUEL",
apellidos:"GILETTA"
},
renglon:2,
codTipoSancion:{
codTipoSancion:2,
nombreSancion:"SUSPENSIÓN PREVENTIVA "
}
},
{
matricula:{
matricula:570,
nombres:"MARCELO MIGUEL",
apellidos:"GILETTA"
},
renglon:3,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
},
{
matricula:{
matricula:652,
nombres:"MARIA COVADONGA SUSANA",
apellidos:"MARCUZZI"
},
renglon:1,
codTipoSancion:{
codTipoSancion:2,
nombreSancion:"SUSPENSIÓN PREVENTIVA "
}
},
{
matricula:{
matricula:652,
nombres:"MARIA COVADONGA SUSANA",
apellidos:"MARCUZZI"
},
renglon:2,
codTipoSancion: {
codTipoSancion: 1,
nombreSancion: " SUSPENSIÓN "
}
}
]
**EXPECTED OUTPUT *Example get url: http://localhost:8282/api/antecedentes_o_sanciones?codTipoSancion.codTipoSancion=4
[
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:4,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
},
{
matricula:{
matricula:325,
nombres:"AIDEE MARIA DEL ROSARIO",
apellidos:"COSTAMAGNA"
},
renglon:5,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
},
{
matricula:{
matricula:570,
nombres:"MARCELO MIGUEL",
apellidos:"GILETTA"
},
renglon:3,
codTipoSancion:{
codTipoSancion:4,
nombreSancion:"DESTITUCIÓN "
}
}
]
** SQL Doctrine executes:
SELECT
...
FROM
sec_antecedentes_o_sanciones s0_
LEFT JOIN sec_padron_escribanos s1_ ON s0_.matricula = s1_.matricula
LEFT JOIN sec_tipos_sanciones s2_ ON s0_.cod_tipo_sancion = s2_.cod_tipo_sancion
WHERE
s0_.matricula IN (
SELECT
s3_.matricula AS sclr_40
FROM
sec_antecedentes_o_sanciones s3_
LEFT JOIN sec_padron_escribanos s4_ ON s3_.matricula = s4_.matricula
LEFT JOIN sec_tipos_sanciones s5_ ON s3_.cod_tipo_sancion = s5_.cod_tipo_sancion
WHERE
s5_.cod_tipo_sancion = '4'
)
ORDER BY
s0_.matricula ASC,
s0_.renglon ASC
LIMIT
10
As you can see, the problem is en the WHERE clause of the sql select in the line that api-platform writes: s0_.matricula IN .. in than way, the query brings me all the "antecedentes_o_sanciones" where the matricula involved has at least one cod_tipo_sancion = 4. When i do this test with entities with single_id primary keys, the filter works perfect.