Skip to content

Allow deleting a bundle item which has already been ordered - #66

Open
lruozzi9 wants to merge 1 commit into
BitBagCommerce:masterfrom
webgriffe:fix/product-bundle-order-item-nullable-bundle-item
Open

Allow deleting a bundle item which has already been ordered#66
lruozzi9 wants to merge 1 commit into
BitBagCommerce:masterfrom
webgriffe:fix/product-bundle-order-item-nullable-bundle-item

Conversation

@lruozzi9

@lruozzi9 lruozzi9 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

ProductBundle removes its items with orphan-removal="true", so removing a product from a bundle in the admin issues a DELETE on bitbag_product_bundle_item.

But ProductBundleOrderItem references that same row through a join column mapped nullable="false" with no on-delete behaviour. As soon as a bundle item has been ordered at least once, deleting it fails:

An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1451
Cannot delete or update a parent row: a foreign key constraint fails
(`bitbag_product_bundle_order_item`, CONSTRAINT `FK_A615CDA9B7FE950B`
 FOREIGN KEY (`product_bundle_item_id`) REFERENCES `bitbag_product_bundle_item` (`id`))

Stack trace ends in ResourceUpdateHandler->handle()UnitOfWork->executeDeletions(), i.e. the plain admin product update.

This is not an edge case: any product ever sold inside a bundle becomes impossible to remove from that bundle.

Fix

-<join-column name="product_bundle_item_id" referenced-column-name="id" nullable="false"/>
+<join-column name="product_bundle_item_id" referenced-column-name="id" nullable="true" on-delete="SET NULL"/>

Why this is safe

  • ProductBundleOrderItem::getProductBundleItem() is never read. ProductBundleOrderItemFactory::createFromProductBundleItem() writes it, and already copies the two values that matter onto the order item itself:

    $productBundleOrderItem->setProductBundleItem($bundleItem);
    $productBundleOrderItem->setProductVariant($bundleItem->getProductVariant()); // copied
    $productBundleOrderItem->setQuantity($bundleItem->getQuantity());             // copied

    So the order row is already self-sufficient; the pointer is redundant.

  • Both ProductBundleOrderItemInterface::getProductBundleItem() and setProductBundleItem() are already typed nullable — only the mapping disagreed.

  • config/serialization/ProductBundleOrderItem.xml exposes only id, productVariant and quantity; productBundleItem is in no serialization group, so API responses are unaffected.

  • templates/Admin/Order/Show/_productBundleOrderItems.html.twig renders item.productVariant / item.quantity only.

  • It mirrors the sibling order_item_id join column in the same file, which already declares on-delete="CASCADE".

Order history is preserved: the ordered variant and quantity stay on the row, only the link to the now-deleted bundle line is cleared.

Verification

Applied in a Sylius 2 project and tested against real data (~1600 bitbag_product_bundle_order_item rows):

  • Deleting a bundle item referenced by 128 order rows now succeeds; those rows keep product_variant_id and quantity, with product_bundle_item_id set to NULL.
  • doctrine:schema:update --dump-sql is clean afterwards.
  • Removing a product from a bundle in the admin no longer raises the constraint violation.

Note for applications

The plugin ships no migrations, so consuming applications need one:

ALTER TABLE bitbag_product_bundle_order_item DROP FOREIGN KEY FK_A615CDA9B7FE950B;
ALTER TABLE bitbag_product_bundle_order_item CHANGE product_bundle_item_id product_bundle_item_id INT DEFAULT NULL;
ALTER TABLE bitbag_product_bundle_order_item ADD CONSTRAINT FK_A615CDA9B7FE950B FOREIGN KEY (product_bundle_item_id) REFERENCES bitbag_product_bundle_item (id) ON DELETE SET NULL;

Product bundles remove their items with orphan removal, but
ProductBundleOrderItem referenced them through a non nullable join column
with no on delete behaviour. Removing a product from a bundle therefore
failed with a foreign key constraint violation as soon as that bundle item
had been ordered at least once:

    SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
    update a parent row: a foreign key constraint fails
    (`bitbag_product_bundle_order_item`, CONSTRAINT `FK_A615CDA9B7FE950B`
    FOREIGN KEY (`product_bundle_item_id`) REFERENCES
    `bitbag_product_bundle_item` (`id`))

Order items already store the ordered product variant and its quantity, and
the association is already typed as nullable on both the entity and its
interface, so the reference can simply be set to null when the bundle item
is deleted, leaving order history intact.

Applications need a migration for the schema change: the plugin ships none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lruozzi9
lruozzi9 force-pushed the fix/product-bundle-order-item-nullable-bundle-item branch from 4c86b51 to 06092ac Compare August 31, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant