<?php
namespace App\Controller\Rest;
use App\Entity\Course;
use App\Entity\CartItem;
use App\Entity\CourseData;
use App\Service\CartService;
use Doctrine\DBAL\Connection;
use Swagger\Annotations as SWG;
use App\Entity\CourseOccurrence;
use App\Repository\PersonRepository;
use App\Repository\CartItemRepository;
use App\Service\ConfigurationService;
use FOS\RestBundle\Request\ParamFetcher;
use App\Repository\CourseFieldRepository;
use App\Repository\CourseDataRepository;
use Doctrine\Persistence\ManagerRegistry;
use Nelmio\ApiDocBundle\Annotation\Model;
use App\Repository\OAuth\ClientRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use App\Repository\CourseOccurrenceRepository;
use App\Repository\OAuth\AccessTokenRepository;
use FOS\RestBundle\Controller\Annotations\View;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\CourseOccurrenceTimeRepository;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use App\Controller\Traits\SingleOccurrenceRequestValidatable;
use League\Bundle\OAuth2ServerBundle\Manager\AccessTokenManagerInterface;
/**
* @Route("/rest/cart")
*/
class CartRestController extends AbstractRestCartableController
{
use SingleOccurrenceRequestValidatable;
private $courseOccurrenceRepo;
protected $managerRegistry;
public function __construct(
ClientRepository $clientRepository,
AccessTokenManagerInterface $accessTokenManagerInterface,
AccessTokenRepository $accessTokenRepository,
Security $security,
CartService $cartService,
PersonRepository $personRepo,
CourseOccurrenceRepository $courseOccurrenceRepo,
ManagerRegistry $managerRegistry
) {
parent::__construct($clientRepository, $accessTokenManagerInterface, $accessTokenRepository, $security, $managerRegistry, $cartService, $personRepo);
$this->courseOccurrenceRepo = $courseOccurrenceRepo;
$this->managerRegistry = $managerRegistry;
}
/**
* @Route("/add", name="rest_cart_add", methods="PUT")
* @SWG\Put(
* produces={"application/json"},
* consumes={"application/x-www-form-urlencoded"}
* )
* @SWG\Parameter(name="courseOccurrence", in="body", required=true, schema=@SWG\Schema(type="integer"))
* @SWG\Parameter(name="quantity", in="body", required=true, schema=@SWG\Schema(type="integer"))
* @SWG\Parameter(name="courseOccurrenceTime", in="body", required=false, schema=@SWG\Schema(type="integer"))
* @SWG\Response(
* response=200,
* description="Returns json containing a confirmation message",
* @SWG\Schema(
* type="object",
* @SWG\Property(property="success", type="boolean", description="Flag if request was successful"),
* @SWG\Property(property="message", type="string", description="Response message."),
* )
* )
* @SWG\Response(
* response=400,
* description="Returned if request parameter were invalid."
* )
* @SWG\Response(
* response=403,
* description="Returned if access to course occurrence was denied."
* )
* @SWG\Response(
* response=404,
* description="Returned if course occurrence was not found."
* )
* @SWG\Tag(name="rest")
* @View(serializerGroups={"public"})
*/
public function add(
Request $request,
CourseOccurrenceTimeRepository $courseOccurrenceTimeRepository
)
{
$courseOccurrenceId = $request->get('courseOccurrence');
$quantity = $request->get('quantity');
if (empty($quantity)) {
throw new \Exception(sprintf('Der Parameter „%s“ fehlt leider.', 'quantity'));
}
$violation = $this->validateSingleOccurrenceRequest($courseOccurrenceId, $quantity);
if ($violation !== null) {
return $violation;
}
$courseOccurrence = $this->getCourseOccurrence($courseOccurrenceId);
$courseOccurrenceTimeId = null;
// Validate course template availability
if ($courseOccurrence->getCourse()->getCourseNature() == 'CourseTemplate') {
// Obtain course occurrence time id
$courseOccurrenceTimeId = $request->get('courseOccurrenceTime');
if (empty($courseOccurrenceTimeId)) {
throw new \Exception(sprintf('Der Parameter „%s“ fehlt leider.', 'courseOccurrenceTime'));
}
// Fetch time
$courseOccurrenceTime = $courseOccurrenceTimeRepository->find($courseOccurrenceTimeId);
if ($courseOccurrenceTime === null) {
throw $this->createNotFoundException('Der angegebene Zeit-Slot konnte nicht gefunden werden.');
}
if ($courseOccurrenceTime->getAvailability() == 'NotAvailable') {
throw new \Exception(sprintf('Time is not available.'));
}
// Todo check collision with booked courses
} else {
$courseOccurrenceTime = null;
if (!$courseOccurrence->isAvailable()) {
throw $this->createNotFoundException('Dieser Kurs kann nicht in den Warenkorb gelegt werden.');
}
}
$em = $this->managerRegistry->getManager();
$cart = $this->getCart();
if (!$cart->getId()) {
$em->flush();
}
$cartItem = $this->cartService->getCartItemIfExists($courseOccurrence, $cart, false, $courseOccurrenceTimeId);
if ($cartItem) {
if ($courseOccurrence->getCourse()->getCourseNature() != 'CourseTemplate') {
$cartItem->increaseQuantity($quantity);
foreach ($cartItem->getMaterialCosts() as $materialCost) {
$materialCost->increaseQuantity($quantity);
}
}
} else {
$cartItem = CartItem::createWithCart($cart, $courseOccurrence, $quantity, $courseOccurrenceTime);
$em->persist($cartItem);
if ($courseOccurrence->getMaterialCost() > 0) {
$materialCost = CartItem::createWithCartItem($cartItem);
$em->persist($materialCost);
}
}
$em->flush();
return [
'success' => true,
'message' => $quantity . ($quantity > 1 ? ' Kurse' : ' Kurs') . ' in den Warenkorb gelegt.',
];
}
/**
* @Route("/set-quantity", name="rest_cart_set-quantity", methods="PUT")
* @SWG\Put(
* produces={"application/json"},
* consumes={"application/x-www-form-urlencoded"}
* )
* @SWG\Parameter(name="courseOccurrence", in="body", required=true, schema=@SWG\Schema(type="integer"))
* @SWG\Parameter(name="quantity", in="body", required=true, schema=@SWG\Schema(type="integer"))
* @SWG\Response(
* response=200,
* description="Returns json containing a confirmation message"
* )
* @SWG\Response(
* response=400,
* description="Returned if request parameter were invalid."
* )
* @SWG\Response(
* response=403,
* description="Returned if access to course occurrence was denied."
* )
* @SWG\Response(
* response=404,
* description="Returned if course occurrence was not found."
* )
* @SWG\Tag(name="rest")
* @View(serializerGroups={"public"})
*/
public function setQuantity(Request $request)
{
$courseOccurrenceId = $request->get('courseOccurrence');
$quantity = $request->get('quantity');
$violation = $this->validateSingleOccurrenceRequest($courseOccurrenceId, $quantity);
if ($violation !== null) {
return $this->json($violation, 400);
}
$courseOccurrence = $this->getCourseOccurrence($courseOccurrenceId);
$cart = $this->getCart();
$cartItem = $this->cartService->getCartItemIfExists($courseOccurrence, $cart);
if (!$cartItem) {
throw $this->createNotFoundException('The requested item is not in current cart.');
}
$message = '';
if ($quantity > $courseOccurrence->getFreeSlots()) {
if (!$courseOccurrence->getReservationAllowed()) {
return [
'success' => false,
'message' => 'Es sind keine freien Plätze mehr verfügbar.',
];
}
$message = 'Menge im Warenkorb aktualisiert. Es sind nicht mehr genug Plätze verfügbar. Ihre Anmeldung wird auf die Warteliste gestellt.';
}
$cartItem->setQuantity($quantity);
if ($cartItem->getMaterialCosts()) {
foreach ($cartItem->getMaterialCosts() as $materialCost) {
$materialCost->setQuantity($quantity);
}
}
$em = $this->managerRegistry->getManager();
$em->flush();
return [
'success' => true,
'message' => $message ? $message : 'Menge im Warenkorb aktualisiert.',
];
}
/**
* @Route("/remove", name="rest_cart_remove", methods="DELETE")
* @SWG\Delete(
* produces={"application/json"},
* consumes={"application/x-www-form-urlencoded"}
* )
* @SWG\Parameter(name="courseOccurrence", in="body", required=true, schema=@SWG\Schema(type="integer"))
* @SWG\Response(
* response=200,
* description="Returns json containing a confirmation message",
* @SWG\Schema(
* type="object",
* @SWG\Property(property="success", type="boolean", description="Flag if request was successful"),
* @SWG\Property(property="message", type="string", description="Response message."),
* )
* )
* @SWG\Response(
* response=400,
* description="Returned if request parameter were invalid."
* )
* @SWG\Response(
* response=403,
* description="Returned if access to course occurrence was denied."
* )
* @SWG\Response(
* response=404,
* description="Returned if course occurrence was not found."
* )
* @SWG\Tag(name="rest")
* @View(serializerGroups={"public"})
*/
public function remove(Request $request)
{
$courseOccurrenceId = $request->get('courseOccurrence');
if (!is_numeric($courseOccurrenceId)) {
return $this->json([
'success' => false,
'message' => 'Es wurden ungültige Werte übergeben',
'errors' => ['courseOccurrence' => 'Dieser Wert muss ein Integer sein.'],
], 400);
}
$courseOccurrence = $this->getCourseOccurrence($courseOccurrenceId);
$cart = $this->getCart();
$em = $this->managerRegistry->getManager();
$cartItem = $this->cartService->getCartItemIfExists($courseOccurrence, $cart);
if (!$cartItem) {
throw $this->createNotFoundException('Der angeforderte Kurs befindet sich nicht im Warenkorb.');
}
if ($cartItem->getMaterialCosts()) {
foreach ($cartItem->getMaterialCosts() as $materialCost) {
$em->remove($materialCost);
}
}
$cart->removeItem($cartItem);
$em->remove($cartItem);
$em->flush();
return [
'success' => true,
'message' => 'Der Kurs wurde aus dem Warenkorb entfernt.',
];
}
/**
* @Route("/show", name="rest_cart_show", methods="GET")
* @SWG\Get(
* produces={"application/json"},
* @SWG\Parameter(name="limit", in="query", type="integer"),
* @SWG\Parameter(name="offset", in="query", type="integer"),
* )
* @QueryParam(
* name="limit",
* requirements="\d+",
* default="100",
* )
* @QueryParam(
* name="offset",
* requirements="\d+",
* default="0",
* )
* @QueryParam(
* name="order",
* requirements="asc|desc",
* default="asc",
* description="Possible values: asc|desc",
* )
* @QueryParam(
* name="orderby",
* requirements="title|price|priceSum|quantity",
* default="title",
* description="Possible values: title|price|priceSum|quantity",
* )
* @SWG\Response(
* response=200,
* description="Returns cart content in json format.",
* @Model(type=Cart::class, groups={"personal"})
* )
* @SWG\Tag(name="rest")
* @View(serializerGroups={"personal"})
*/
public function show(
ParamFetcher $paramFetcher,
CartItemRepository $cartItemRepo,
Connection $connection,
CourseDataRepository $courseDataRepository,
CourseFieldRepository $courseFieldRepository,
ConfigurationService $configService
)
{
$order = $paramFetcher->get('order');
$orderby = $paramFetcher->get('orderby');
$limit = $paramFetcher->get('limit');
$offset = $paramFetcher->get('offset');
$cart = $this->getCart();
if ($cart->getId()) {
$cartItems = $cartItemRepo->findBy(['cart' => $cart, 'courseItem' => null], [$orderby => $order], $limit, $offset);
foreach ($cartItems as $cartItem) {
$sqlCourse = 'SELECT
c.id,
c.title,
c.description,
c.category_id,
c.series_id,
c.type_id
FROM
course c
WHERE
c.id = :courseId';
$stmtCourse = $connection->prepare($sqlCourse);
$stmtCourse->bindValue('courseId', $cartItem->getCourse()->getId());
$stmtCourse->executeQuery();
$courseData = $stmtCourse->fetchAllAssociative();
// Falls der Kurs nicht existiert, überspringen
if (!$courseData) {
continue;
}
$sql = 'SELECT
f.*,
d.value_text,
d.value_integer
FROM
course_field f
LEFT JOIN
course_data d
ON
d.field_id = f.id AND
d.course_id = :courseId
WHERE f.certificate = 0';
$stmt = $connection->prepare($sql);
$stmt->bindValue(
'courseId',
$cartItem->getCourse()->getId()
);
$stmt->executeQuery();
$result = $stmt->fetchAll();
$fields = [];
foreach ($result as $field) {
$fields[] = [
'id' => $field['id'],
'name' => $field['name'],
'value' => !empty($field['value_integer']) ? $field['value_integer'] : $field['value_text'],
];
}
$fields[] = [
'id' => 'category_id',
'name' => 'Category ID',
'value' => $courseData[0]['category_id'],
];
$fields[] = [
'id' => 'series_id',
'name' => 'Series ID',
'value' => $courseData[0]['series_id'],
];
$fields[] = [
'id' => 'type_id',
'name' => 'Type ID',
'value' => $courseData[0]['type_id'],
];
$cartItem->setField($fields);
$cartItem->setField($fields); // oder $cartItem->field = 'yes';
}
$cart->setItems($cartItems);
}
return $cart;
}
/**
* Get course occurrence by id. It will be checked if entity exists and access is allowed.
*
* @param $courseOccurrenceId
* @return CourseOccurrence|null
*/
protected function getCourseOccurrence($courseOccurrenceId)
{
$courseOccurrence = $this->courseOccurrenceRepo->findOneBy(['id' => $courseOccurrenceId]);
if (!$courseOccurrence) {
throw $this->createNotFoundException('The requested course occurrence does not exist.');
}
#$this->denyAccessUnlessGranted('frontend_client_allowed', $courseOccurrence);
return $courseOccurrence;
}
private function createDescription($field, $option)
{
switch ($option) {
case 'course':
if (!empty($field['certificate'])) {
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für den Kurs und das Zertifikat'
);
} else {
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für den Kurs'
);
}
break;
case 'certificate':
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für das Zertifikat'
);
break;
default:
break;
}
return $field;
}
private function generateHTMLForDescription($name, $text)
{
return '<strong>' . $name . '</strong>' . '<span style="font-size: 0.7rem"> (' . $text . ')</span>';
}
}