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.

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