@@ -70,7 +70,8 @@ public function insert(object $entity): object {
7070 $ entityInfo = $ this ->getEntityInfo ($ entity ::class);
7171 $ insert = $ this ->connection ->getQueryBuilder ();
7272
73- $ isSnowflake = false ;
73+ $ isComposite = $ entityInfo ->hasCompositeIdProperty ();
74+ $ autoIncrementProperty = null ;
7475 $ values = [];
7576
7677 foreach ($ entityInfo ->propertiesAttributes as $ propertyAttributes ) {
@@ -80,12 +81,33 @@ public function insert(object $entity): object {
8081 if ($ generatorClass ) {
8182 if ($ generatorClass === ISnowflakeGenerator::class) {
8283 $ generator = Server::get ($ generatorClass );
83- $ isSnowflake = true ;
8484 $ values [$ propertyAttributes ->column ->name ] = $ generator ->nextId ();
8585 $ property ->setValue ($ entity , $ insert ->createNamedParameter ($ values [$ propertyAttributes ->column ->name ]));
8686 }
87+
88+ continue ;
8789 }
8890
91+ if ($ isComposite ) {
92+ // A composite primary key can't rely on a single autoincrement column: every
93+ // part must already be set on the entity (e.g. a foreign key id, or a value
94+ // assigned by the caller) before insert() is called.
95+ /** @var mixed $value */
96+ $ value = $ property ->getValue ($ entity );
97+ if ($ value === null ) {
98+ throw new \LogicException ($ entity ::class . ':: ' . $ property ->getName () . ' is part of a composite primary key and must be set before insert(); it cannot rely on DB autoincrement. ' );
99+ }
100+ if (!is_string ($ value ) && !is_int ($ value )) {
101+ throw new \LogicException ($ entity ::class . ':: ' . $ property ->getName () . ' is part of a composite primary key and must be set to a int or string before insert();. ' );
102+ }
103+
104+ $ type = $ this ->getParameterType ($ propertyAttributes ->column ->type , false );
105+ $ values [$ propertyAttributes ->column ->name ] = $ insert ->createNamedParameter ($ value , $ type );
106+ continue ;
107+ }
108+
109+ // Single autoincrement primary key: let the DB generate it, then read it back below.
110+ $ autoIncrementProperty = $ property ;
89111 continue ;
90112 }
91113
@@ -106,7 +128,7 @@ public function insert(object $entity): object {
106128 if ($ targetEntity === null ) {
107129 $ values [$ joinColumn ->name ] = $ insert ->createNamedParameter (null );
108130 } else {
109- $ values [$ joinColumn ->name ] = $ insert ->createNamedParameter ($ targetEntityInfo ->getIdProperty ()->getValue ($ targetEntity ));
131+ $ values [$ joinColumn ->name ] = $ insert ->createNamedParameter ($ targetEntityInfo ->getSingleIdProperty ()->getValue ($ targetEntity ));
110132 }
111133
112134 continue ;
@@ -122,8 +144,8 @@ public function insert(object $entity): object {
122144 ->values ($ values )
123145 ->executeStatement ();
124146
125- if (! $ isSnowflake ) {
126- $ entityInfo -> getIdProperty () ->setValue ($ entity , $ insert ->getLastInsertId ());
147+ if ($ autoIncrementProperty !== null ) {
148+ $ autoIncrementProperty ->setValue ($ entity , $ insert ->getLastInsertId ());
127149 }
128150
129151 return $ entity ;
@@ -151,7 +173,7 @@ public function update(object $entity): object {
151173 throw new \LogicException ('Trying to update an entity with no primary key set. ' );
152174 }
153175
154- $ update ->andWhere ($ update ->expr ()->eq ($ entityInfo -> mappingPropertyToColumn [ $ entityInfo -> getIdProperty ()-> getName ()] , $ update ->createNamedParameter ($ property -> getValue ( $ entity ) )));
176+ $ update ->andWhere ($ update ->expr ()->eq ($ propertyAttributes -> column -> name , $ update ->createNamedParameter ($ value )));
155177 // don't update the id
156178 continue ;
157179 }
@@ -169,7 +191,7 @@ public function update(object $entity): object {
169191 if ($ targetEntity === null ) {
170192 $ update ->set ($ joinColumn ->name , $ update ->createNamedParameter (null ));
171193 } else {
172- $ update ->set ($ joinColumn ->name , $ update ->createNamedParameter ($ targetEntityInfo ->getIdProperty ()->getValue ($ targetEntity )));
194+ $ update ->set ($ joinColumn ->name , $ update ->createNamedParameter ($ targetEntityInfo ->getSingleIdProperty ()->getValue ($ targetEntity )));
173195 }
174196
175197 continue ;
@@ -266,11 +288,19 @@ public function createTable(string $entityClass, SchemaWrapper $schema): void {
266288
267289 $ table = $ schema ->createTable ($ entityInfo ->tableName );
268290
291+ /** @var list<string> $idColumns */
292+ $ idColumns = [];
269293 foreach ($ entityInfo ->propertiesAttributes as $ propertyAttributes ) {
270- $ this ->createProperty ($ propertyAttributes , $ table );
294+ $ this ->createProperty ($ entityInfo , $ propertyAttributes , $ table );
295+
296+ if ($ propertyAttributes ->id instanceof Id && $ propertyAttributes ->column instanceof Column) {
297+ $ idColumns [] = $ propertyAttributes ->column ->name ;
298+ }
271299
272300 $ this ->createRelationColumn ($ propertyAttributes , $ table , $ schema );
273301 }
302+
303+ $ table ->setPrimaryKey ($ idColumns );
274304 }
275305
276306 /**
@@ -281,7 +311,7 @@ public function dropTable(string $entityClass, string $prefix): void {
281311 $ this ->connection ->dropTable ($ prefix . $ entityInfo ->tableName );
282312 }
283313
284- private function createProperty (PropertyAttributes $ attributes , Table $ table ): void {
314+ private function createProperty (EntityInfo $ entityInfo , PropertyAttributes $ attributes , Table $ table ): void {
285315 if (!$ attributes ->column instanceof Column) {
286316 return ;
287317 }
@@ -299,15 +329,12 @@ private function createProperty(PropertyAttributes $attributes, Table $table): v
299329 $ options ['default ' ] = $ columnAttribute ->default ;
300330 }
301331
302- if ($ attributes ->id instanceof Id && $ attributes ->id ->generatorClass === null ) {
332+ // A composite primary key can't rely on a single autoincrement column; see insert().
333+ if ($ attributes ->id instanceof Id && $ attributes ->id ->generatorClass === null && !$ entityInfo ->hasCompositeIdProperty ()) {
303334 $ options ['autoincrement ' ] = true ;
304335 }
305336
306337 $ table ->addColumn ($ columnAttribute ->name , $ columnAttribute ->type , $ options );
307-
308- if ($ attributes ->id instanceof Id) {
309- $ table ->setPrimaryKey ([$ columnAttribute ->name ]);
310- }
311338 }
312339
313340 private function createRelationColumn (PropertyAttributes $ attributes , Table $ table , SchemaWrapper $ schema ): void {
0 commit comments