Skip to content

Commit 473c610

Browse files
docs: document coupon constraints and pivot/FK conventions
Note the coupon creator FKs and the three scoping pivots' foreign-key and unique rules in the schema doc, add migration/multi-select conventions to AGENTS.md, and mark Coupons done in IMPLEMENTATION.md. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9ae70a8 commit 473c610

3 files changed

Lines changed: 17 additions & 10 deletions

File tree

admin/AGENTS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ When adding a new entity, build the files in this order, matching the existing f
199199
- Select fields backed by an enum use `->options(SomeEnum::options())` and `->default(SomeEnum::CASE->value)`.
200200
- Table text columns that can be long (headings, relation labels) use `->limit(30)->wrap()`.
201201
- Enum-backed table columns render via `->getStateUsing(fn ($record) => $record->field->label())` and `->color(fn ($record) => $record->field->color())`.
202+
- Manage many-to-many pivots with a relationship multi-select: `Select::make('products')->relationship('products', 'heading')->multiple()->searchable()->preload()` (see `CouponResource`). No separate resource for pure scoping pivots.
202203

203204
## Models
204205

@@ -218,7 +219,9 @@ When adding a new entity, build the files in this order, matching the existing f
218219
## Migrations
219220

220221
- Anonymous class style: `return new class extends Migration`.
221-
- Use `$table->foreignIdFor(Model::class)` for foreign keys (add `->nullable()` when optional).
222+
- Use `$table->foreignIdFor(Model::class)` for foreign keys (add `->nullable()` when optional). Chain `->constrained()->cascadeOnDelete()` / `->nullOnDelete()` / `->restrictOnDelete()` to add the real FK constraint with its delete rule.
223+
- For a second FK to the same table, use a named column: `$table->foreignId('user_creator_id')->nullable()->constrained('users')->nullOnDelete()` (see `coupons`).
224+
- Pivot tables add `$table->unique([...])` on the key pair and `->cascadeOnDelete()` on both FKs (see `coupon_product`).
222225
- Default enum columns to a case value: `$table->unsignedTinyInteger('status')->default(ProductStatusEnum::PUBLISHED->value);`.
223226
- Always implement `down()` with `Schema::dropIfExists(...)`.
224227

admin/IMPLEMENTATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Catalog layer and platform basics.
2222
- [x] Products (+ `product_attribute` pivot, product images)
2323
- [x] Varieties (+ `variety_counts` auto-sync on Product)
2424
- [x] Discounts (auto-applied price rules per variety)
25+
- [x] Coupons (+ `coupon_product`, `coupon_variety`, `category_coupon` scoping pivots)
2526
- [x] Images (polymorphic, used via uploads - no standalone resource by design)
2627
- [~] Addresses (model only, no resource yet)
2728

admin/ShoFlow db doc.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ This table stores cards.
163163

164164
Scopes a coupon so it applies only to products in specific categories.
165165

166-
* `category_id` specifies the category the coupon is allowed for.
167-
* `coupon_id` specifies the coupon this scope belongs to.
166+
* `category_id` specifies the category the coupon is allowed for. Foreign key; cascades on category delete.
167+
* `coupon_id` specifies the coupon this scope belongs to. Foreign key; cascades on coupon delete.
168+
* The pair `(category_id, coupon_id)` is unique.
168169

169170
# cities
170171

@@ -184,9 +185,9 @@ Stores discount coupons. Unlike discounts, a coupon is applied manually: the cus
184185
* `max_discount`: The maximum discount amount the coupon can give.
185186
* `total_used`: How many times this coupon has already been used.
186187
* `total_uses`: How many times this coupon is allowed to be used in total.
187-
* `user_id`: Limits usage to a specific user.
188-
* `user_creator_id`: The admin user who created the coupon, if created by an admin.
189-
* `seller_creator_id`: The seller who created the coupon, if created by a seller.
188+
* `user_id`: Limits usage to a specific user. Nullable foreign key to `users`; set to null when the user is deleted.
189+
* `user_creator_id`: The admin user who created the coupon, if created by an admin. Nullable foreign key to `users`.
190+
* `seller_creator_id`: The seller who created the coupon, if created by a seller. Nullable foreign key to `users`.
190191
* `status`: Has states "active" (default, usable), "canceled", "used", and "under review".
191192
* `is_percent`: Indicates if the discount is a percentage or a fixed amount.
192193
* `shipping`: Indicates if this coupon includes free shipping (applies only to free shipping, not to the price).
@@ -197,14 +198,16 @@ Stores discount coupons. Unlike discounts, a coupon is applied manually: the cus
197198
# coupon\_product
198199

199200
* Scopes a coupon so it can only be applied to certain products.
200-
* `coupon_id`: The coupon this scope belongs to.
201-
* `product_id`: A product the coupon is allowed for.
201+
* `coupon_id`: The coupon this scope belongs to. Foreign key; cascades on coupon delete.
202+
* `product_id`: A product the coupon is allowed for. Foreign key; cascades on product delete.
203+
* The pair `(coupon_id, product_id)` is unique.
202204

203205
# coupon\_variety
204206

205207
* Scopes a coupon so it applies to certain varieties only, useful for sellers who want a coupon for some of their varieties.
206-
* `coupon_id`: The coupon this scope belongs to.
207-
* `variety_id`: A variety the coupon is allowed for.
208+
* `coupon_id`: The coupon this scope belongs to. Foreign key; cascades on coupon delete.
209+
* `variety_id`: A variety the coupon is allowed for. Foreign key; cascades on variety delete.
210+
* The pair `(coupon_id, variety_id)` is unique.
208211

209212
# discounts
210213

0 commit comments

Comments
 (0)