PriceCategoryForm.php (5629B)
1 <?php 2 3 /** 4 * @file 5 * Location: src/Form/PriceCategoryForm.php 6 * 7 * Form handler for price category add and edit forms. 8 */ 9 10 namespace Drupal\taler_turnstile\Form; 11 12 use Drupal\Core\Entity\EntityForm; 13 use Drupal\Core\Form\FormStateInterface; 14 use Drupal\taler_turnstile\TalerMerchantApiService; 15 use Symfony\Component\DependencyInjection\ContainerInterface; 16 17 /** 18 * Form handler for the price category add and edit forms. 19 */ 20 class PriceCategoryForm extends EntityForm { 21 22 /** 23 * The Turnstile API service. 24 * 25 * @var \Drupal\taler_turnstile\TalerMerchantApiService 26 */ 27 protected $apiService; 28 29 /** 30 * Constructs a PriceCategoryForm object. 31 * 32 * @param \Drupal\taler_turnstile\TalerMerchantApiService $api_service 33 * The API service. 34 */ 35 public function __construct(TalerMerchantApiService $api_service) { 36 $this->apiService = $api_service; 37 } 38 39 /** 40 * {@inheritdoc} 41 */ 42 public static function create(ContainerInterface $container) { 43 return new static( 44 $container->get('taler_turnstile.api_service') 45 ); 46 } 47 48 /** 49 * {@inheritdoc} 50 */ 51 public function form(array $form, FormStateInterface $form_state) { 52 $form = parent::form($form, $form_state); 53 /** @var TurnstilePriceCategory $price_category */ 54 $price_category = $this->entity; 55 56 $form['label'] = [ 57 '#type' => 'textfield', 58 '#title' => $this->t('Name'), 59 '#maxlength' => 255, 60 '#default_value' => $price_category->label(), 61 '#description' => $this->t('The name of the price category.'), 62 '#required' => TRUE, 63 ]; 64 65 $form['id'] = [ 66 '#type' => 'machine_name', 67 '#default_value' => $price_category->id(), 68 '#machine_name' => [ 69 'exists' => '\Drupal\taler_turnstile\Entity\TurnstilePriceCategory::load', 70 ], 71 '#disabled' => !$price_category->isNew(), 72 ]; 73 74 $form['description'] = [ 75 '#type' => 'textarea', 76 '#title' => $this->t('Description'), 77 '#default_value' => $price_category->getDescription(), 78 '#description' => $this->t('A description of this price category.'), 79 ]; 80 81 // Get subscriptions and currencies from API. 82 $subscriptions = $this->apiService->getSubscriptions(); 83 $currencies = $this->apiService->getCurrencies(); 84 85 if (empty($subscriptions) || empty($currencies)) { 86 $this->messenger()->addWarning($this->t('Unable to load subscriptions or currencies from API. Please check your configuration.')); 87 } 88 89 $form['prices'] = [ 90 '#type' => 'fieldset', 91 '#title' => $this->t('Prices'), 92 '#tree' => TRUE, 93 ]; 94 95 $existing_prices = $price_category->getPrices(); 96 97 foreach ($subscriptions as $subscription_id => $subscription) { 98 $subscription_label = $subscription['label'] ?? $subscription['name']; 99 100 $form['prices'][$subscription_id] = [ 101 '#type' => 'details', 102 '#title' => $subscription_label, 103 '#open' => ($subscription_id === '%none%'), 104 ]; 105 106 foreach ($currencies as $currency) { 107 $currency_code = $currency['code'] ?? $currency['name']; 108 $currency_label = $currency['label'] ?? $currency['code']; 109 110 $form['prices'][$subscription_id][$currency_code] = [ 111 '#type' => 'number', 112 '#title' => $currency_label, 113 '#default_value' => $existing_prices[$subscription_id][$currency_code] ?? '', 114 '#min' => 0, 115 '#step' => $currency['step'] ?? 0.01, 116 '#size' => 20, 117 '#description' => $this->t('Leave empty for no price.'), 118 ]; 119 } 120 } 121 122 return $form; 123 } 124 125 126 /** 127 * {@inheritdoc} 128 */ 129 public function validateForm(array &$form, FormStateInterface $form_state) { 130 parent::validateForm($form, $form_state); 131 132 $prices = $form_state->getValue('prices'); 133 $ok = FALSE; 134 if (is_array($prices)) { 135 foreach ($prices as $subscription_id => $currencies) { 136 if (is_array($currencies)) { 137 foreach ($currencies as $currency_code => $price) { 138 // Skip empty values as they are allowed. 139 if ($price === '' || $price === NULL) { 140 continue; 141 } 142 143 // Validate that the price is a valid non-negative number. 144 if (!is_numeric($price) || $price < 0) { 145 $form_state->setErrorByName( 146 "prices][${subscription_id}][${currency_code}", 147 $this->t('Prices cannot be negative.') 148 ); 149 } 150 $ok = TRUE; 151 } 152 } 153 } 154 } 155 if (! $ok) { 156 $form_state->setErrorByName( 157 "", 158 $this->t('At least one price must be set.') 159 ); 160 } 161 } 162 163 164 /** 165 * {@inheritdoc} 166 */ 167 public function save(array $form, FormStateInterface $form_state) { 168 $price_category = $this->entity; 169 170 // Filter out empty prices. 171 $prices = $form_state->getValue('prices'); 172 $filtered_prices = []; 173 foreach ($prices as $subscription_id => $currencies) { 174 foreach ($currencies as $currency_code => $price) { 175 if ($price !== '') { 176 $filtered_prices[$subscription_id][$currency_code] = $price; 177 } 178 } 179 } 180 181 $price_category->setPrices($filtered_prices); 182 $status = $price_category->save(); 183 184 if ($status === SAVED_NEW) { 185 $this->messenger()->addStatus($this->t('Created the %label price category.', [ 186 '%label' => $price_category->label(), 187 ])); 188 } 189 else { 190 $this->messenger()->addStatus($this->t('Updated the %label price category.', [ 191 '%label' => $price_category->label(), 192 ])); 193 } 194 195 $form_state->setRedirectUrl($price_category->toUrl('collection')); 196 } 197 198 }