Doctrine ORMを使っている中で、照合順序でハマってしまったのでその体験を共有します。
ある日突然、スキーマ更新時にエラーが発生。
以下のように、ManyToOneのリレーションを定義していました。
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Author")
* @ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false)
*/
private $author;
このような設定はごく一般的で、エンティティのマッピングも正しく行われているはずでした。そのため、スキーマ更新も問題なく通ると思っていたのですが、以下のようなエラーが発生してしまいました。
SQLSTATE[HY000]: General error: 1825 Failed to add the foreign key constraint on table 'table_name'. Incorrect options in FOREIGN KEY constraint 'app/XXXXXXXXXXXX'
綴りや型に間違いはなく、何度見直しても原因がわからないまま時間だけが過ぎていきました。
最原因は「照合順序(Collation)の不一致」でした。
具体的には、片方のカラムが utf8mb4_bin、もう片方が utf8mb4_general_ci というように、Collationが異なっていたため、外部キー制約が成立しなかったようです。
Doctrineでは、「@JoinColumn」という アノテーションの「 options」でカラム毎に照合順序を明示できます。今回のケースでは、以下のように修正することで問題を解消できました。
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Author")
* @ORM\JoinColumn(
* name="author_id",
* referencedColumnName="id",
* nullable=false,
* options={"collation": "utf8mb4_unicode_ci"}
* )
*/
private $author;
これで、Collationが一致し、マイグレーションも無事に通るようになりました。
開発の途中でデータベースのデフォルトのCollation設定が変更されていたことが原因でした。そのため、ある時期以降に作成されたテーブルのカラムは、以前と異なるCollationが自動的に設定されていました。
DoctrineなどのORMを使用する際は、勝手に色々設定がついてきて、照合順序のような細かいところは見落としがち。
しかし、この細かい設定の違いでも、外部キー制約などで重大なエラーを引き起こすことがあります。
今後は、特に異なる時期に作成したテーブルやカラム間でリレーションを定義する際には、Collationの一致を意識していこうと思いました。
ただ、カラム毎に照合順序を指定できるようにしてくれていたのは非常に助かりました。
すでにテーブル定義の変更が難しい段階になってしまった場合の一時的な対策としては、このように、collation を明示的に指定すれば切り抜けられます。
(参考)
https://www.doctrine-project.org/projects/doctrine-orm/en/2.17/reference/annotations-reference.html