Attempting to use non-attribute class "Doctrine\ORM\Mapping\JoinColumns" as attribute #7020

Closed
opened 2026-01-22 15:43:16 +01:00 by admin · 1 comment
Owner

Originally created by @DurandA on GitHub (Aug 9, 2022).

JoinColumns cannot be used as a PHP8 attribute:

#[ORM\ManyToOne(targetEntity: YearlyBar::class)]
#[ORM\JoinColumns(value: [
    new ORM\JoinColumn(name: "year", referencedColumnName: "year"),
    new ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id"),
])]
public YearlyBar $yearlyBar;

This triggers Attempting to use non-attribute class "Doctrine\ORM\Mapping\JoinColumns" as attribute.

I attempted to patch JoinColumns to add attribute support:

--- <unnamed>
+++ <unnamed>
@@ -4,12 +4,20 @@
 
 namespace Doctrine\ORM\Mapping;
 
+use Attribute;
+
 /**
  * @Annotation
  * @Target("PROPERTY")
  */
+#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
 final class JoinColumns implements Annotation
 {
     /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
     public $value;
+
+    public function __construct(array $value)
+    {
+        $this->value = $value;
+    }
 }

However, it does not have the expected behavior:

Foo

#[ORM\Entity]
class Foo
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;
}

YearlyBar

#[ORM\Entity]
class YearlyBar
{
    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: Foo::class)]
    public Foo $foo;
    
    #[ORM\Id]
    #[ORM\Column(type: 'integer')]
    public int $year;

    #[ORM\OneToMany(targetEntity: Baz::class, mappedBy: 'yearlyBar')]
    private Collection $bazs;

    public function getBazs()
    {
        return $this->bazs->getValues();
    }

    public function addBaz(Baz $baz): self
    {
        $this->bazs->add($baz);

        return $this;
    }

    public function removeBaz(Baz $baz): self
    {
        $this->bazs->removeElement($baz);

        return $this;
    }

Baz

#[ORM\Entity]
class Baz
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: YearlyBar::class)]
    #[ORM\JoinColumns(value: [
        new ORM\JoinColumn(name: "year", referencedColumnName: "year"),
        new ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id"),
    ])]
    public YearlyBar $yearlyBar;
}

This throws Column name "id" referenced for relation from App\Entity\Baz towards App\Entity\YearlyBar does not exist. I am not sure if this is due to the attribute support or if I am missusing JoinColumns as I did not have previous experience with composite keys using Doctrine.

Originally created by @DurandA on GitHub (Aug 9, 2022). `JoinColumns` cannot be used as a PHP8 attribute: ```php #[ORM\ManyToOne(targetEntity: YearlyBar::class)] #[ORM\JoinColumns(value: [ new ORM\JoinColumn(name: "year", referencedColumnName: "year"), new ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id"), ])] public YearlyBar $yearlyBar; ``` This triggers `Attempting to use non-attribute class "Doctrine\ORM\Mapping\JoinColumns" as attribute`. I attempted to patch `JoinColumns` to add attribute support: ```patch --- <unnamed> +++ <unnamed> @@ -4,12 +4,20 @@ namespace Doctrine\ORM\Mapping; +use Attribute; + /** * @Annotation * @Target("PROPERTY") */ +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] final class JoinColumns implements Annotation { /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */ public $value; + + public function __construct(array $value) + { + $this->value = $value; + } } ``` However, it does not have the expected behavior: ### Foo ```php #[ORM\Entity] class Foo { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; } ``` ### YearlyBar ```php #[ORM\Entity] class YearlyBar { #[ORM\Id] #[ORM\ManyToOne(targetEntity: Foo::class)] public Foo $foo; #[ORM\Id] #[ORM\Column(type: 'integer')] public int $year; #[ORM\OneToMany(targetEntity: Baz::class, mappedBy: 'yearlyBar')] private Collection $bazs; public function getBazs() { return $this->bazs->getValues(); } public function addBaz(Baz $baz): self { $this->bazs->add($baz); return $this; } public function removeBaz(Baz $baz): self { $this->bazs->removeElement($baz); return $this; } ``` ### Baz ```php #[ORM\Entity] class Baz { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; #[ORM\ManyToOne(targetEntity: YearlyBar::class)] #[ORM\JoinColumns(value: [ new ORM\JoinColumn(name: "year", referencedColumnName: "year"), new ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id"), ])] public YearlyBar $yearlyBar; } ``` This throws `Column name "id" referenced for relation from App\Entity\Baz towards App\Entity\YearlyBar does not exist`. I am not sure if this is due to the attribute support or if I am missusing `JoinColumns` as I did not have previous experience with composite keys using Doctrine.
admin closed this issue 2026-01-22 15:43:16 +01:00
Author
Owner

@DurandA commented on GitHub (Aug 10, 2022):

There is no need for a JoinColumns attribute as JoinColum attributes can be repeated as follows:

#[ORM\JoinColumn(name: "year", referencedColumnName: "year")]
#[ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id")]
@DurandA commented on GitHub (Aug 10, 2022): There is no need for a `JoinColumns` attribute as `JoinColum` attributes can be repeated as follows: ```php #[ORM\JoinColumn(name: "year", referencedColumnName: "year")] #[ORM\JoinColumn(name: "foo_id", referencedColumnName: "foo_id")] ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7020