Allow multiple billets in invitation form, dynamic add/remove lines
- Form sends items[] array with billet_id and quantity per line - JS button to add more billet lines with remove button - Controller iterates over items to create multiple BilletBuyerItems - Same flow: all tickets generated with isInvitation=true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -712,20 +712,14 @@ class AccountController extends AbstractController
|
||||
$firstName = trim($request->request->getString('first_name'));
|
||||
$lastName = trim($request->request->getString('last_name'));
|
||||
$email = trim($request->request->getString('email'));
|
||||
$billetId = $request->request->getInt('billet_id');
|
||||
$quantity = $request->request->getInt('quantity', 1);
|
||||
$items = $request->request->all('items');
|
||||
|
||||
if ('' === $firstName || '' === $lastName || '' === $email || 0 === $billetId) {
|
||||
if ('' === $firstName || '' === $lastName || '' === $email || 0 === \count($items)) {
|
||||
$this->addFlash('error', 'Tous les champs sont requis.');
|
||||
|
||||
return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId(), 'tab' => 'invitations']);
|
||||
}
|
||||
|
||||
$billet = $em->getRepository(Billet::class)->find($billetId);
|
||||
if (!$billet || $billet->getCategory()->getEvent()->getId() !== $event->getId()) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$count = $em->getRepository(BilletBuyer::class)->count([]) + 1;
|
||||
|
||||
$order = new BilletBuyer();
|
||||
@@ -736,12 +730,28 @@ class AccountController extends AbstractController
|
||||
$order->setOrderNumber(date('Y-m-d').'-'.$count);
|
||||
$order->setTotalHT(0);
|
||||
|
||||
$item = new BilletBuyerItem();
|
||||
$item->setBillet($billet);
|
||||
$item->setBilletName($billet->getName());
|
||||
$item->setQuantity($quantity);
|
||||
$item->setUnitPriceHT(0);
|
||||
$order->addItem($item);
|
||||
foreach ($items as $itemData) {
|
||||
$billetId = (int) ($itemData['billet_id'] ?? 0);
|
||||
$qty = max(1, (int) ($itemData['quantity'] ?? 1));
|
||||
|
||||
$billet = $em->getRepository(Billet::class)->find($billetId);
|
||||
if (!$billet || $billet->getCategory()->getEvent()->getId() !== $event->getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = new BilletBuyerItem();
|
||||
$item->setBillet($billet);
|
||||
$item->setBilletName($billet->getName());
|
||||
$item->setQuantity($qty);
|
||||
$item->setUnitPriceHT(0);
|
||||
$order->addItem($item);
|
||||
}
|
||||
|
||||
if ($order->getItems()->isEmpty()) {
|
||||
$this->addFlash('error', 'Aucun billet valide selectionne.');
|
||||
|
||||
return $this->redirectToRoute('app_account_edit_event', ['id' => $event->getId(), 'tab' => 'invitations']);
|
||||
}
|
||||
|
||||
$em->persist($order);
|
||||
$em->flush();
|
||||
|
||||
@@ -378,19 +378,24 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="inv_billet" class="text-xs font-black uppercase tracking-widest form-label">Type de billet</label>
|
||||
<select id="inv_billet" name="billet_id" required class="form-input focus:border-indigo-600">
|
||||
{% for cat_billets in billets %}
|
||||
{% for billet in cat_billets %}
|
||||
<option value="{{ billet.id }}">{{ billet.category.name }} — {{ billet.name }} ({{ billet.priceHTDecimal|number_format(2, ',', ' ') }} €)</option>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="inv_qty" class="text-xs font-black uppercase tracking-widest form-label">Quantite</label>
|
||||
<input type="number" id="inv_qty" name="quantity" required min="1" value="1" class="form-input focus:border-indigo-600">
|
||||
<label class="text-xs font-black uppercase tracking-widest form-label">Billets</label>
|
||||
<div id="inv-items">
|
||||
<div class="flex flex-wrap gap-3 items-end mb-3" data-inv-line>
|
||||
<div class="flex-1 min-w-[200px]">
|
||||
<select name="items[0][billet_id]" required class="form-input focus:border-indigo-600">
|
||||
{% for cat_billets in billets %}
|
||||
{% for billet in cat_billets %}
|
||||
<option value="{{ billet.id }}">{{ billet.category.name }} — {{ billet.name }}</option>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-24">
|
||||
<input type="number" name="items[0][quantity]" min="1" value="1" required class="form-input focus:border-indigo-600 text-center">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="inv-add-line" class="text-xs font-black uppercase tracking-widest text-indigo-600 hover:text-indigo-800 transition-colors cursor-pointer">+ Ajouter un billet</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -399,6 +404,28 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.getElementById('inv-add-line').addEventListener('click', function() {
|
||||
var container = document.getElementById('inv-items')
|
||||
var index = container.querySelectorAll('[data-inv-line]').length
|
||||
var first = container.querySelector('[data-inv-line]')
|
||||
var clone = first.cloneNode(true)
|
||||
var select = clone.querySelector('select')
|
||||
select.name = 'items[' + index + '][billet_id]'
|
||||
select.selectedIndex = 0
|
||||
var input = clone.querySelector('input[type="number"]')
|
||||
input.name = 'items[' + index + '][quantity]'
|
||||
input.value = '1'
|
||||
var removeBtn = document.createElement('button')
|
||||
removeBtn.type = 'button'
|
||||
removeBtn.className = 'px-2 py-1 border-2 border-red-800 bg-red-600 text-white text-xs font-black uppercase cursor-pointer hover:bg-red-800 transition-all'
|
||||
removeBtn.textContent = '\u2715'
|
||||
removeBtn.addEventListener('click', function() { clone.remove() })
|
||||
clone.appendChild(removeBtn)
|
||||
container.appendChild(clone)
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user