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.

375 lines
11KB

  1. <?php
  2. namespace App\Http\Controllers\Web;
  3. use App\Codes\EnvironmentName;
  4. use App\Codes\HTTPResultCode as ResultCode;
  5. use App\Codes\UserRole;
  6. use App\Exceptions\AppCommonException;
  7. use App\Exceptions\ExclusiveException;
  8. use App\Exceptions\GeneralErrorMessageException;
  9. use App\Sessions\SessionUser;
  10. use App\Util\DBUtil;
  11. use Exception;
  12. use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
  13. use Illuminate\Foundation\Bus\DispatchesJobs;
  14. use Illuminate\Foundation\Validation\ValidatesRequests;
  15. use Illuminate\Http\Client\Response as ClientResponse;
  16. use Illuminate\Http\JsonResponse;
  17. use Illuminate\Http\Request;
  18. use Illuminate\Http\Response;
  19. use Illuminate\Routing\Controller as BaseController;
  20. use Illuminate\Support\Arr;
  21. use Illuminate\Support\Facades\Auth;
  22. use Illuminate\Support\Facades\Log;
  23. use Illuminate\Support\Facades\Validator;
  24. use Illuminate\Support\Str;
  25. use Illuminate\Validation\ValidationException;
  26. use LogicException;
  27. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  28. use Symfony\Component\HttpKernel\Exception\HttpException;
  29. abstract class WebController extends BaseController
  30. {
  31. use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
  32. const COL_NAME_CREATED_AT = 'created_at';
  33. const COL_NAME_UPDATED_AT = 'updated_at';
  34. const COL_NAME_RESULT_CODE = 'result';
  35. const COL_NAME_DATA = 'data';
  36. const COL_NAME_MESSAGES = 'messages';
  37. const COL_NAME_GENERAL_MESSAGE = 'general';
  38. const COL_NAME_EMAIL_ID = 'email_id';
  39. const COL_NAME_ERRORS = 'errors';
  40. /**
  41. * バリデートした結果を格納
  42. *
  43. * @var array
  44. */
  45. protected $validated = [];
  46. /**
  47. * 画面へ返却するメールID
  48. *
  49. * @var integer|null
  50. */
  51. private int|null $emailId = null;
  52. /**
  53. * 返却するメッセージ
  54. *
  55. * @var array|null
  56. */
  57. private array|null $messages = null;
  58. /**
  59. * 返却するメッセージ
  60. *
  61. * @var string|null
  62. */
  63. private string|null $generalMessage = null;
  64. /**
  65. * 返却するデータ
  66. *
  67. * @var mixed|null
  68. */
  69. private $data = null;
  70. protected DBUtil $transaction;
  71. protected SessionUser $sessionUser;
  72. /**
  73. * 返却する結果コード
  74. *
  75. * @var ResultCode|null
  76. */
  77. protected ResultCode|null $resultCode = ResultCode::SECCESS;
  78. public function __construct()
  79. {
  80. $this->transaction = DBUtil::instance();
  81. $this->sessionUser = SessionUser::instance();
  82. }
  83. /**
  84. * パラメータオブジェクト
  85. */
  86. protected function getParam(): IParam
  87. {
  88. if (!property_exists(static::class, 'param')) {
  89. throw new LogicException("param未定義");
  90. }
  91. $param = $this->param;
  92. if (!is_subclass_of($param, IParam::class)) {
  93. throw new LogicException("param型不正");
  94. }
  95. return $this->param;
  96. }
  97. /**
  98. * コントローラーの名前
  99. * オーバーライドされることを想定
  100. * 主に、Routeのドキュメント作成用
  101. *
  102. * @return string
  103. */
  104. public function name(): string
  105. {
  106. return "---未設定---";
  107. }
  108. /**
  109. * コントローラーの説明
  110. * オーバーライドされることを想定
  111. * 主に、Routeのドキュメント作成用
  112. *
  113. * @return string
  114. */
  115. public function description(): string
  116. {
  117. return "---未設定---";
  118. }
  119. /**
  120. * オーバーライド必要
  121. * メインロジック
  122. *
  123. * @param Request $request
  124. * @return Response|JsonResponse|string
  125. */
  126. protected function run(Request $request): Response|JsonResponse|BinaryFileResponse|ClientResponse |string
  127. {
  128. return $this->successResponse();
  129. }
  130. private function getRules()
  131. {
  132. return $this->getParam()->rules();
  133. }
  134. public function entry(Request $request)
  135. {
  136. $this->setLogContext($request);
  137. try {
  138. $validator = Validator::make($request->all(), $this->getRules());
  139. $validator->validate();
  140. } catch (ValidationException $e) {
  141. logger("validate error", ['errors' => $e->errors(), 'request' => $request->all(), 'path' => $request->path()]);
  142. logger($request->toArray());
  143. return $this->validateErrorResponse($e);
  144. }
  145. try {
  146. $this->validated = $validator->validated();
  147. $this->getParam()->setData($this->validated);
  148. $this->transaction->beginTransaction();
  149. $ret = $this->run($request);
  150. $this->transaction->commit();
  151. return $ret;
  152. } catch (GeneralErrorMessageException $e) {
  153. $this->transaction->rollBack();
  154. return $this->failedResponse([], $e->getMessage());
  155. } catch (AppCommonException $e) {
  156. $this->transaction->rollBack();
  157. logs()->error(sprintf("Appエラー:%s File:%s Line:%d", $e->getMessage(), $e->getFile(), $e->getLine()));
  158. return $this->failedResponse();
  159. } catch (ExclusiveException $e) {
  160. $this->transaction->rollBack();
  161. logs()->error(sprintf("排他エラー:%s", $e->getMessage()));
  162. return $this->exclusiveErrorResponse();
  163. } catch (LogicException $e) {
  164. $this->transaction->rollBack();
  165. logs()->error([
  166. sprintf("実装エラー:%s", $e->getMessage()),
  167. get_class($e),
  168. $e->getFile(),
  169. $e->getLine(),
  170. $request->all(),
  171. ]);
  172. logger(array_filter($e->getTrace(), function ($val, $key) {
  173. return $key <= 5;
  174. }, ARRAY_FILTER_USE_BOTH));
  175. return $this->failedResponse();
  176. } catch (ValidationException $e) {
  177. $this->transaction->rollBack();
  178. return $this->validateErrorResponse($e);
  179. } catch (HttpException $e) {
  180. $this->transaction->rollBack();
  181. if ($e->getStatusCode() === 401) {
  182. return $this->unAuthorizedResponse();
  183. }
  184. throw $e;
  185. } catch (Exception $e) {
  186. $this->transaction->rollBack();
  187. logs()->error([
  188. sprintf("例外エラー:%s", $e->getMessage()),
  189. get_class($e),
  190. $e->getFile(),
  191. $e->getLine(),
  192. $request->all(),
  193. ]);
  194. logger(array_filter($e->getTrace(), function ($val, $key) {
  195. return $key <= 5;
  196. }, ARRAY_FILTER_USE_BOTH));
  197. return $this->failedResponse();
  198. }
  199. }
  200. protected function successResponse(array|object $data = [], array|string $messages = [])
  201. {
  202. return $this->setData($data)
  203. ->setMessages($messages)
  204. ->setResultCode(ResultCode::SECCESS)
  205. ->makeResponse();
  206. }
  207. protected function failedResponse(array|object $data = [], array|string $messages = [])
  208. {
  209. return $this->setData($data)
  210. ->setMessages($messages)
  211. ->setResultCode(ResultCode::FAILED)
  212. ->makeResponse();
  213. }
  214. protected function unAuthorizedResponse(array|object $data = [], array|string $messages = [])
  215. {
  216. return $this->setData($data)
  217. ->setMessages($messages)
  218. ->setResultCode(ResultCode::UNAUTHORIZED)
  219. ->makeResponse();
  220. }
  221. protected function exclusiveErrorResponse(array|object $data = [], array|string $messages = [])
  222. {
  223. return $this->setData($data)
  224. ->setMessages($messages)
  225. ->setResultCode(ResultCode::EXCLUSIVE_ERROR)
  226. ->makeResponse();
  227. }
  228. protected function validateErrorResponse(ValidationException|array $exception, string|null $generalMessage = null)
  229. {
  230. $errorMessages = [];
  231. $general = null;
  232. if ($exception instanceof ValidationException) {
  233. foreach ($exception->errors() as $key => $m) {
  234. $errorMessages[$key] = $m[0];
  235. }
  236. }
  237. if (is_array($exception)) {
  238. $errorMessages = $exception;
  239. }
  240. $general = $generalMessage ?? data_get($errorMessages, self::COL_NAME_GENERAL_MESSAGE);
  241. return $this->setData([])
  242. ->setMessages($errorMessages)
  243. ->setGeneralMessage($general)
  244. ->setResultCode(ResultCode::FAILED)
  245. ->makeResponse();
  246. }
  247. protected function makeResponse()
  248. {
  249. if ($this->resultCode === null) {
  250. abort(403);
  251. }
  252. $ret = [];
  253. Arr::set($ret, self::COL_NAME_RESULT_CODE, $this->resultCode->value);
  254. if ($this->data !== null) {
  255. Arr::set($ret, self::COL_NAME_DATA, $this->data);
  256. }
  257. if ($this->messages !== null) {
  258. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_ERRORS, $this->messages);
  259. }
  260. if ($this->generalMessage !== null) {
  261. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_GENERAL_MESSAGE, $this->generalMessage);
  262. }
  263. if ($this->emailId !== null) {
  264. Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_EMAIL_ID, $this->emailId);
  265. }
  266. if (request()->wantsJson()) {
  267. return response()
  268. ->json($ret)
  269. ->withHeaders($this->makeHeader());
  270. } else {
  271. if (app()->environment([EnvironmentName::PRODUCTOIN->value])) {
  272. abort(500);
  273. }
  274. return response()
  275. ->json($ret)
  276. ->withHeaders($this->makeHeader());
  277. }
  278. }
  279. private function makeHeader(): array
  280. {
  281. $header = [];
  282. $user = Auth::user();
  283. if ($user) {
  284. $header["App-User-Auth"] = sprintf("%s", $user->id);
  285. } else {
  286. $header["App-User-Auth"] = 'none';
  287. }
  288. return $header;
  289. }
  290. // 返却用データの登録
  291. protected function setEmailId(int $emailId)
  292. {
  293. $this->emailId = $emailId;
  294. return $this;
  295. }
  296. protected function setMessages(array|string $messages)
  297. {
  298. if (is_array($messages)) {
  299. $this->messages = $messages;
  300. } else {
  301. $this->setGeneralMessage($messages);
  302. }
  303. return $this;
  304. }
  305. protected function setGeneralMessage(string|null $generalMessage)
  306. {
  307. $this->generalMessage = $generalMessage;
  308. return $this;
  309. }
  310. protected function setData($data)
  311. {
  312. $this->data = $data;
  313. return $this;
  314. }
  315. protected function setResultCode(ResultCode $resultCode)
  316. {
  317. $this->resultCode = $resultCode;
  318. return $this;
  319. }
  320. protected function setLogContext(Request $request)
  321. {
  322. Log::withContext([
  323. '__requestUuid__' => strval(Str::uuid()),
  324. '__userId__' => Auth::id(),
  325. '__path__' => $request->path(),
  326. ]);
  327. }
  328. }