<?php
namespace App\Controller\Api;
use App\Component\ApiTrait;
use App\Component\CartService;
use App\Entity\Menu;
use App\Entity\Order;
use App\Entity\Shop;
use App\Entity\ShopPlan;
use App\Entity\User;
use App\Form\Type\ApiCartAddItemType;
use App\Form\Type\ApiCartEditItemType;
use App\Form\Type\ApiContactType;
use App\Form\Type\ApiShopFcmTokenType;
use App\Form\Type\ApiShopRegisterType;
use App\Form\Type\ApiShopSearchType;
use App\Form\Type\ApiShopUpdateEstimatedMinutesType;
use App\Form\Type\ApiShopUpdateHoursType;
use App\Form\Type\ApiUpdateMenuStockType;
use App\Form\Type\ApiUserFcmTokenType;
use App\Form\Type\ApiUserRegisterVerifyType;
use App\Form\Type\UserProfileType;
use App\Form\Type\UserRegisterType;
use App\Form\Type\UserRegisterVerifyType;
use App\Security\OTPService;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\ORM\EntityManagerInterface;
use Gesdinet\JWTRefreshTokenBundle\Model\RefreshTokenManagerInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Js;
use Knp\Component\Pager\PaginatorInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Mailgun\Exception\HttpClientException;
use Mailgun\Mailgun;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Routing\Annotation\Route;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Serializer\Serializer;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use function MongoDB\BSON\fromJSON;
/**
* @OA\Tag(name="Shop")
*/
class ShopController extends AbstractController
{
use ApiTrait;
/**
* @Route("/api/shop-register", name="api_shop_register", methods={"POST"})
* @see \App\Controller\IndexController::register()
* @OA\Response(
* response=200,
* description="店舗の掲載依頼を送信します。",
* )
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="application-json",
* @OA\Schema(ref=@Model(type=ApiShopRegisterType::class))
* )
* )
* @OA\Post(summary="店舗掲載依頼", description="成功時、店舗登録し、完了メールをユーザーに送信します。")
* @OA\Tag(name="Shop")
* @return JsonResponse
*/
public function shopRegister(EntityManagerInterface $em, Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// $user = $this->getUser();
$shop = new Shop();
$shop->setReady(false);
$form = $this->createForm(ApiShopRegisterType::class, $shop, [
'csrf_protection' => false,
]);
if ($request->getContent()) {
$data = json_decode($request->getContent(), true);
$form->submit($data);
}
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$data = $form->getData();
$shopPlan = $em->getRepository('App:ShopPlan')->createQueryBuilder('sp')
->andWhere('sp.slug = :plan')
->setParameter('plan', ShopPlan::SLUG_STANDARD)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$shop->setShopPlan($shopPlan);
$shop->setPassword($passwordEncoder->encodePassword($shop, $shop->getPlainPassword()));
$em->persist($shop);
$em->flush();
try {
$mailgun = Mailgun::create($_ENV['MAILGUN_API_KEY']);
$mailgun->messages()->send($_ENV['MAILGUN_DOMAIN'], array(
'from' => $_ENV['MAIL_FROM_ADDRESS'],
'to' => $shop->getEmail(),
'bcc' => $_ENV['MAIL_TO_ADDRESS'],
'subject' => '掲載申し込み',
//'text' => $this->renderView('index/register.eml.twig', ['shop' => $shop])
'template' => 'registration',
'h:X-Mailgun-Variables' => json_encode([
'url' => $this->generateUrl('shop_dashboard', [], UrlGeneratorInterface::ABSOLUTE_URL),
'name' => $shop->getName(),
'homepage' => 'https://uchideri.com'
])
));
} catch (HttpClientException $e) {
} catch (\Exception $e) {
} catch (\Throwable $e) {
}
return new JsonResponse();
}
}
return $this->createValidationErrorJsonResponse($form);
}
/**
* @Route("/api/shop/update-fcm-token", name="api_shop_update_fcm_token", methods={"PATCH"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(ref=@Model(type=ApiShopFcmTokenType::class))
* )
* )
* @OA\Patch(summary="ユーザーFCMトークン更新", description="")
*/
public function updateFcmToken(Request $request, EntityManagerInterface $em)
{
$user = $this->getUser();
$form = $this->createForm(ApiShopFcmTokenType::class, $user, [
'csrf_protection' => false,
]);
if ($request->getContent()) {
$data = json_decode($request->getContent(), true);
$form->submit($data);
}
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em->persist($user);
$em->flush();
} else {
return $this->createValidationErrorJsonResponse($form);
}
}
$serializer = $this->container->get('serializer');
$em->refresh($user);
// $cart = $em->getRepository('App:Cart')->find($cart->getId());
return JsonResponse::fromJsonString($serializer->serialize($user, 'json'));
}
/**
* @Route("/api/shops", name="api_shops", methods={"GET"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Get(summary="店舗一覧取得", description="店舗一覧取得を返します。")
* @OA\Tag(name="Shop")
*/
public function shops(Request $request, EntityManagerInterface $em, PaginatorInterface $paginator)
{
$builder = $this->createShopListBuilder($em, $request->get('area'), $request->get('location'), $request->get('categories'), $request->get('prepaid'), $request->get('delivery'), $request->get('onSale'));
$pagination = $paginator->paginate($builder, $request->get('page', 1), 20);
$serializer = $this->container->get('serializer');
$response = JsonResponse::fromJsonString($serializer->serialize($pagination, 'json'));
$response->headers->set('Content-Type', 'application/json; charset=utf-8');
$response->headers->set('x-total-count', $pagination->getTotalItemCount());
return $response;
}
/**
* @Route("/api/shop/order-manager", name="api_shop_order_manager", methods={"GET"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Get(summary="注文管理ページ表示", description="JWTを元にCookieベースのログイン状態にし、注文管理ページにリダイレクトします。")
*/
public function toOrderManager(Request $request, EventDispatcherInterface $eventDispatcher)
{
$user = $this->getUser();
// クッキーベースでログイン状態にする
$token = new UsernamePasswordToken($user, 'main', $user->getRoles());
$this->container->get('security.token_storage')->setToken($token);
$this->container->get('session')->set('_security_main', serialize($token));
$loginEvent = new InteractiveLoginEvent($request, $token);
$eventDispatcher->dispatch($loginEvent, 'security.interactive_login');
return $this->redirectToRoute('shop_order_manager', ['app' => 1]);
}
/**
* @Route("/api/shop", name="api_shop_show", methods={"GET"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Get(summary="店舗情報取得", description="自店舗情報を返します。")
* @OA\Tag(name="Shop")
*/
public function index(Request $request, EventDispatcherInterface $eventDispatcher)
{
$shop = $this->getUser();
$serializer = $this->container->get('serializer');
return JsonResponse::fromJsonString($serializer->serialize($shop, 'json'));
}
/**
* @Route("/owner/api/menus", name="api_owner_menu", methods={"GET"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Get(summary="メニュー一覧取得", description="自店舗のメニュー一覧を返します。")
* @OA\Tag(name="Shop")
*/
public function menu(Request $request, EntityManagerInterface $em, PaginatorInterface $paginator)
{
$shop = $this->getUser();
$builder = $em->getRepository('App:MenuGroup')->createQueryBuilder('mg')
->leftJoin('mg.menus', 'm')
->addSelect('m')
->andWhere('mg.shop = :shop')
->setParameter('shop', $shop)
->orderBy('mg.position')
->getQuery()
;
$pagination = $paginator->paginate($builder, $request->get('page', 1), 20);
$serializer = $this->container->get('serializer');
return JsonResponse::fromJsonString($serializer->serialize($pagination, 'json'));
}
/**
* @Route("/api/shop-menus/{id}", name="api_shop_menu", methods={"GET"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Get(summary="指定店舗のメニュー一覧取得", description="指定店舗のメニュー一覧を返します。")
* @OA\Tag(name="Shop")
*/
public function shopMenus(Request $request, Shop $shop, EntityManagerInterface $em, PaginatorInterface $paginator)
{
$builder = $em->getRepository('App:MenuGroup')->createQueryBuilder('mg')
->leftJoin('mg.menus', 'm')
->addSelect('m')
->andWhere('mg.shop = :shop')
->setParameter('shop', $shop)
->andWhere('m.shop = :shop')
->setParameter('shop', $shop)
->orderBy('mg.position')
->getQuery()
;
$pagination = $paginator->paginate($builder, $request->get('page', 1), 20);
$serializer = $this->container->get('serializer');
$response = JsonResponse::fromJsonString($serializer->serialize($pagination, 'json'));
$response->headers->set('Content-Type', 'application/json; charset=utf-8');
return $response;
}
/**
* @Route("/owner/api/update-estimated-minutes", name="api_shop_update_estimated_minutes", methods={"POST"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="application-json",
* @OA\Schema(ref=@Model(type=ApiShopUpdateEstimatedMinutesType::class))
* )
* )
* @OA\Post(summary="待ち時間変更", description="")
* @OA\Tag(name="Shop")
*/
public function updateEstimatedMinutes(Request $request, EntityManagerInterface $em)
{
/** @var Shop $shop */
$shop = $this->getUser();
$form = $this->createForm(ApiShopUpdateEstimatedMinutesType::class, $shop, [
'csrf_protection' => false,
'_block_prefix' => false,
]);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em->persist($shop);
$em->flush();
return new JsonResponse([
'estimatedMinutes' => $shop->getAppEstimatedMinutes(),
]);
} else {
return $this->createValidationErrorJsonResponse($form);
}
}
return new JsonResponse([], 422);
}
/**
* @Route("/owner/api/menu/{id}/update-stock", name="api_shop_menu_update_stock", methods={"POST"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(ref=@Model(type=ApiUpdateMenuStockType::class))
* )
* )
* @OA\Post(summary="メニューの在庫数変更", description="")
* @OA\Tag(name="Shop")
*/
public function updateMenuStock(Request $request, Menu $menu, EntityManagerInterface $em)
{
/** @var Shop $shop */
$shop = $this->getUser();
if ($menu->getShop()->getId() != $shop->getId()) {
return new JsonResponse([], 403);
}
$form = $this->createForm(ApiUpdateMenuStockType::class, $menu, [
'csrf_protection' => false,
'_block_prefix' => false,
]);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
try {
$em->persist($menu);
$em->flush();
} catch (DeadlockException $e) {
$MenuError = new FormError('他の処理と競合したため在庫数の更新を中止しました。', '', [], null, 'menu');
$form->addError($MenuError);
return $this->createValidationErrorJsonResponse($form);
}
return new JsonResponse([
'appStock' => $menu->getAppStock(),
]);
} else {
return $this->createValidationErrorJsonResponse($form);
}
}
return new JsonResponse([], 422);
}
/**
* @Route("/owner/api/update-hours", name="api_shop_update_hours", methods={"POST"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="application-json",
* @OA\Schema(ref=@Model(type=ApiShopUpdateHoursType::class))
* )
* )
* @OA\Post(summary="受付時間変更", description="")
* @OA\Tag(name="Shop")
*/
public function updateHours(Request $request, EntityManagerInterface $em)
{
/** @var Shop $shop */
$shop = $this->getUser();
$form = $this->createForm(ApiShopUpdateHoursType::class, $shop, [
'csrf_protection' => false,
'_block_prefix' => false,
]);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em->persist($shop);
$em->flush();
return new JsonResponse([
'hourFrom' => $shop->getAppHourFrom()->format('H:i'),
'hourTo' => $shop->getAppHourTo()->format('H:i'),
]);
} else {
return $this->createValidationErrorJsonResponse($form);
}
}
return new JsonResponse([], 422);
}
/**
* @Route("/owner/api/closed", name="api_shop_closed", methods={"POST"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Post(summary="受付終了", description="")
* @OA\Tag(name="Shop")
*/
public function closed(Request $request, EntityManagerInterface $em)
{
/** @var Shop $shop */
$shop = $this->getUser();
$shop->setAppClosed(true);
$em->persist($shop);
$em->flush();
return new JsonResponse();
}
/**
* @Route("/owner/api/open", name="api_shop_open", methods={"POST"})
* @OA\Response(
* response=200,
* description="",
* )
* @OA\Post(summary="受付再開", description="受付時間外の場合はクローズしたまま")
* @OA\Tag(name="Shop")
*/
public function open(Request $request, EntityManagerInterface $em)
{
/** @var Shop $shop */
$shop = $this->getUser();
$shop->setAppClosed(false);
$em->persist($shop);
$em->flush();
return new JsonResponse();
}
}