I'm trying to create a join table between two entities using Doctrine ORM and the Symfony maker bundle.
The two entities are User
and Member
. A User
should be able to reference multiple Member
entities, and Member
entities can be referenced by multiple users.
User:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'string', length: 255)]
public readonly string $username,
#[ORM\ManyToMany(targetEntity: Member::class, mappedBy: 'name')]
#[ORM\JoinTable(
name: 'tracked_members',
joinColumns: [
new ORM\JoinColumn(name: 'user', referencedColumnName: 'username'),
],
inverseJoinColumns: [
new ORM\JoinColumn(name: 'member', referencedColumnName: 'name'),
],
)]
public ArrayCollection $tracked_members,
) {
}
}
Member:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MemberRepository;
#[ORM\Entity(repositoryClass: MemberRepository::class)]
class Member
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'string', length: 255)]
public readonly string $name,
) {
}
}
However, when I run ./bin/console make:migration
, I receive the following migration class with no warnings or errors indicating a problem:
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220705021447 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE member (name VARCHAR(255) NOT NULL, PRIMARY KEY(name))');
$this->addSql('CREATE TABLE user (username VARCHAR(255) NOT NULL, PRIMARY KEY(username))');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE member');
$this->addSql('DROP TABLE user');
}
}
Why isn't the SQL to create the join table tracked_users
being added to the migration class?
Other relevant info:
Dependency | Version |
---|---|
PHP | 8.1.7 |
SQLite | 3.37.0 |
doctrine/doctrine-migrations-bundle | 3.2.2 |
doctrine/orm | 2.13.3 |
symfony/maker-bundle | 1.43.0 |
CodePudding user response:
Finally got this to work. Even though PHP 8.1 supports nested attributes, and #[JoinTable]
has joinColumns
and inverseJoinColumns
parameters, you must specify #[JoinColumn]
and #[InverseJoinColumn]
as top-level attributes.
The following updated User
entity generates the expected result:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'string', length: 255)]
public readonly string $username,
#[ORM\JoinColumn('username', referencedColumnName: 'username')]
#[ORM\InverseJoinColumn('member', referencedColumnName: 'name')]
#[ORM\JoinTable(name: 'tracked_members')]
#[ORM\ManyToMany(targetEntity: Member::class)]
public ArrayCollection $tracked_members,
) {
}
}
Resulting Migration class:
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220705021447 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE member (name VARCHAR(255) NOT NULL, PRIMARY KEY(name))');
$this->addSql('CREATE TABLE user (username VARCHAR(255) NOT NULL, PRIMARY KEY(username))');
$this->addSql('CREATE TABLE tracked_members (username VARCHAR(255) NOT NULL, member VARCHAR(255) NOT NULL, PRIMARY KEY(username, member))');
$this->addSql('CREATE INDEX IDX_CD2594E8F85E0677 ON tracked_members (username)');
$this->addSql('CREATE INDEX IDX_CD2594E870E4FA78 ON tracked_members (member)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE member');
$this->addSql('DROP TABLE user');
$this->addSql('DROP TABLE tracked_members');
}
}
CodePudding user response:
You should add inversedBy
.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MemberRepository;
use App\Entity\User;
#[ORM\Entity(repositoryClass: MemberRepository::class)]
class Member
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'string', length: 255)]
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'tracked_members')]
public readonly string $name,
) {
}
}