Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

310 строки
11KB

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

Powered by TurnKey Linux.