Home > OS >  Doctrine Migrations not creating join table
Doctrine Migrations not creating join table

Time:07-09

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,
    ) {
    }
}
  • Related