You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 line
9.9KB

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\CustomerType;
  5. use App\Repositories\CustomerTypeAuditRepository;
  6. use App\Repositories\CustomerTypeRepository;
  7. use App\ViewModels\CustomerTypeViewModel;
  8. use Core\Controller;
  9. use Core\Request;
  10. use Core\Response;
  11. use Core\Validator;
  12. class CustomerTypeController extends Controller
  13. {
  14. public function index(): Response
  15. {
  16. $request = Request::capture();
  17. $model = new CustomerTypeViewModel();
  18. $model->saved = $request->input('saved') === '1';
  19. $model->deleted = $request->input('deleted') === '1';
  20. return $this->view('customer-types.index', [
  21. 'model' => $model,
  22. 'pageTitle' => $model->title,
  23. ]);
  24. }
  25. public function data(): Response
  26. {
  27. $rows = $this->repo()->allOrderedByName();
  28. $data = array_map(static function (array $row): array {
  29. $attrs = !empty($row['attributes'])
  30. ? (json_decode((string) $row['attributes'], true) ?? [])
  31. : [];
  32. return [
  33. 'id' => (int) $row['id'],
  34. 'name' => (string) $row['name'],
  35. 'attribute_count' => count($attrs),
  36. 'attributes_summary' => implode(', ', array_column($attrs, 'name')),
  37. 'created_at' => (string) $row['created_at'],
  38. ];
  39. }, $rows);
  40. return $this->json($data);
  41. }
  42. public function create(): Response
  43. {
  44. $model = new CustomerTypeViewModel();
  45. $model->title = 'New Customer Type';
  46. return $this->view('customer-types.create', [
  47. 'model' => $model,
  48. 'pageTitle' => $model->title,
  49. ]);
  50. }
  51. public function store(): Response
  52. {
  53. $request = Request::capture();
  54. [$form, $errors] = $this->validateForm($request);
  55. if (empty($errors) && $this->repo()->findByName($form['name']) !== null) {
  56. $errors['name'][] = 'A customer type with that name already exists.';
  57. }
  58. if (!empty($errors)) {
  59. $model = new CustomerTypeViewModel();
  60. $model->title = 'New Customer Type';
  61. $model->form = $form;
  62. $model->errors = $errors;
  63. return $this->view('customer-types.create', [
  64. 'model' => $model,
  65. 'pageTitle' => $model->title,
  66. ]);
  67. }
  68. $customerType = new CustomerType();
  69. $customerType->name = $form['name'];
  70. $customerType->attributes = $form['attributes'];
  71. $this->repo()->create($customerType);
  72. $inserted = $this->repo()->findByName($form['name']);
  73. if ($inserted !== null) {
  74. $this->auditRepo()->log((int) $inserted['id'], 'I', $this->toAuditFields($inserted), $this->currentUsername());
  75. }
  76. return $this->redirect('/customer-types?saved=1');
  77. }
  78. public function edit(string $id): Response
  79. {
  80. $row = $this->repo()->find((int) $id);
  81. if ($row === null) {
  82. return $this->redirect('/customer-types');
  83. }
  84. $model = new CustomerTypeViewModel();
  85. $model->title = 'Edit Customer Type';
  86. $model->customerType = $row;
  87. $model->saved = Request::capture()->input('saved') === '1';
  88. $model->form = [
  89. 'name' => (string) $row['name'],
  90. 'attributes' => json_decode((string) ($row['attributes'] ?? '[]'), true) ?? [],
  91. ];
  92. return $this->view('customer-types.edit', [
  93. 'model' => $model,
  94. 'pageTitle' => $model->title,
  95. ]);
  96. }
  97. public function update(string $id): Response
  98. {
  99. $before = $this->repo()->find((int) $id);
  100. if ($before === null) {
  101. return $this->redirect('/customer-types');
  102. }
  103. $request = Request::capture();
  104. [$form, $errors] = $this->validateForm($request);
  105. if (empty($errors)) {
  106. $existing = $this->repo()->findByName($form['name']);
  107. if ($existing !== null && (int) $existing['id'] !== (int) $id) {
  108. $errors['name'][] = 'A customer type with that name already exists.';
  109. }
  110. }
  111. if (!empty($errors)) {
  112. $model = new CustomerTypeViewModel();
  113. $model->title = 'Edit Customer Type';
  114. $model->customerType = $before;
  115. $model->form = $form;
  116. $model->errors = $errors;
  117. return $this->view('customer-types.edit', [
  118. 'model' => $model,
  119. 'pageTitle' => $model->title,
  120. ]);
  121. }
  122. $customerType = new CustomerType();
  123. $customerType->id = (int) $id;
  124. $customerType->name = $form['name'];
  125. $customerType->attributes = $form['attributes'];
  126. $this->repo()->update($customerType);
  127. $after = $this->repo()->find((int) $id);
  128. $this->auditRepo()->log((int) $id, 'U', [
  129. 'before' => $this->toAuditFields($before),
  130. 'after' => $this->toAuditFields($after ?? []),
  131. ], $this->currentUsername());
  132. return $this->redirect('/customer-types/' . $id . '/edit?saved=1');
  133. }
  134. public function destroy(string $id): Response
  135. {
  136. $row = $this->repo()->find((int) $id);
  137. if ($row !== null) {
  138. $this->repo()->delete((int) $id);
  139. $this->auditRepo()->log((int) $row['id'], 'D', $this->toAuditFields($row), $this->currentUsername());
  140. }
  141. return $this->redirect('/customer-types?deleted=1');
  142. }
  143. // ── Helpers ───────────────────────────────────────────────────────────────
  144. private function toAuditFields(array $row): array
  145. {
  146. $attrs = [];
  147. if (!empty($row['attributes'])) {
  148. $raw = $row['attributes'];
  149. $attrs = is_string($raw) ? (json_decode($raw, true) ?? []) : (array) $raw;
  150. }
  151. return [
  152. 'name' => (string) ($row['name'] ?? ''),
  153. 'attributes' => $attrs,
  154. 'created_at' => (string) ($row['created_at'] ?? ''),
  155. 'updated_at' => (string) ($row['updated_at'] ?? ''),
  156. ];
  157. }
  158. private function currentUsername(): string
  159. {
  160. return auth()->user()?->username ?? 'system';
  161. }
  162. private function validateForm(Request $request): array
  163. {
  164. $name = trim((string) $request->input('name', ''));
  165. $attributeNames = (array) ($request->input('attribute_name') ?? []);
  166. $attributeTypes = (array) ($request->input('attribute_type') ?? []);
  167. $attributeOrders = (array) ($request->input('attribute_order') ?? []);
  168. $errors = [];
  169. if (!verify_csrf_token((string) $request->input('_token', ''))) {
  170. $errors['_token'][] = 'Your form session expired. Please refresh and try again.';
  171. }
  172. $errors = array_merge($errors, (new Validator())
  173. ->required('name', $name, 'Customer type name is required.')
  174. ->maxLength('name', $name, 255, 'Name must be 255 characters or fewer.')
  175. ->errors());
  176. $attributeAliases = (array) ($request->input('attribute_alias') ?? []);
  177. $attributeApiUrls = (array) ($request->input('attribute_api_url') ?? []);
  178. $attributeApiFormats = (array) ($request->input('attribute_api_format') ?? []);
  179. $attributeApiReturnTypes = (array) ($request->input('attribute_api_return_type') ?? []);
  180. $attributeApiMatchFields = (array) ($request->input('attribute_api_match_field') ?? []);
  181. $attributeApiAutoFills = (array) ($request->input('attribute_api_auto_fill') ?? []);
  182. $attributes = [];
  183. foreach ($attributeNames as $i => $attrName) {
  184. $attrName = trim((string) $attrName);
  185. $attrType = trim((string) ($attributeTypes[$i] ?? 'text'));
  186. if ($attrName === '') continue;
  187. $validatedType = in_array($attrType, ['text', 'number', 'date', 'boolean', 'api_lookup'], true) ? $attrType : 'text';
  188. $attr = [
  189. 'name' => $attrName,
  190. 'type' => $validatedType,
  191. 'alias' => trim((string) ($attributeAliases[$i] ?? '')),
  192. 'order' => isset($attributeOrders[$i]) && (string) $attributeOrders[$i] !== ''
  193. ? max(1, (int) $attributeOrders[$i])
  194. : count($attributes) + 1,
  195. ];
  196. if ($validatedType === 'api_lookup') {
  197. $rawFormat = trim((string) ($attributeApiFormats[$i] ?? ''));
  198. $rawReturnType = trim((string) ($attributeApiReturnTypes[$i] ?? ''));
  199. $attr['api_url'] = trim((string) ($attributeApiUrls[$i] ?? ''));
  200. $attr['api_format'] = in_array($rawFormat, ['json', 'xml'], true) ? $rawFormat : 'json';
  201. $attr['api_return_type'] = in_array($rawReturnType, ['text', 'number', 'date', 'boolean'], true) ? $rawReturnType : 'text';
  202. $attr['api_match_field'] = trim((string) ($attributeApiMatchFields[$i] ?? ''));
  203. $attr['api_auto_fill'] = trim((string) ($attributeApiAutoFills[$i] ?? ''));
  204. }
  205. $attributes[] = $attr;
  206. }
  207. usort($attributes, static fn(array $a, array $b): int => $a['order'] <=> $b['order']);
  208. foreach ($attributes as $seq => &$attr) {
  209. $attr['order'] = $seq + 1;
  210. }
  211. unset($attr);
  212. return [['name' => $name, 'attributes' => $attributes], $errors];
  213. }
  214. private function repo(): CustomerTypeRepository
  215. {
  216. return new CustomerTypeRepository(database());
  217. }
  218. private function auditRepo(): CustomerTypeAuditRepository
  219. {
  220. return new CustomerTypeAuditRepository(database());
  221. }
  222. }

Powered by TurnKey Linux.