comsa / sulu-reservations
Sulu的预订组件
Requires
- php: ^8.0
- dantleech/phpcr-migrations-bundle: ^1.2
- doctrine/orm: >=2.0
- mollie/mollie-api-php: ^2.0
- phpoffice/phpspreadsheet: ^1.21
- sulu/form-bundle: ^2.2.1
- sulu/sulu: ^2.4
- symfony/config: ^4.3 || ^5.0
- symfony/dependency-injection: ^4.3 || ^5.0
- symfony/event-dispatcher: ^4.3 || ^5.0
- symfony/form: ^4.3 || ^5.0
- symfony/http-foundation: ^4.3 || ^5.0
- symfony/http-kernel: ^4.3 || ^5.0
- symfony/mailer: ^4.3 || ^5.0
- twig/twig: >=2.0 || ^3.0
Requires (Dev)
- jackalope/jackalope-doctrine-dbal: ^1.3.4
- jackalope/jackalope-jackrabbit: ^1.3
- phpcr/phpcr-shell: ^1.1
README
工作原理
这个组件是什么?
- 预订系统
- 与Sulu FormBundle集成
该组件利用您可创建的可预订项目。这些可以链接到您也可以创建的可预订选项。组件支持单次付款和按人付款。每个可预订项目都可以有不同的价格组,并有自己的价格(例如,成人 => €5,儿童 => €3)。付款配置在模块的设置选项卡内。支持3种类型,每种类型都有自己的额外费用(银行、现金或mollie)。
安装
添加到 assets/admin/package.json
"sulu-reservations-bundle": "file:node_modules/@sulu/vendor/comsa/sulu-reservations/Resources/js"
添加到 assets/website/package.json
"sulu-reservations-bundle": "file:../../vendor/comsa/sulu-reservations/Resources/js",
在两个地方运行 npm install
添加到 assets/admin/index.js
import 'sulu-reservations-bundle/admin'
添加到 assets/website/index.js
import 'sulu-reservations-bundle/website'
使用 npm run build
构建它们,这可能需要一些时间 :)
添加路由到 routes_admin.yaml
和 routes_website.yaml
在:config/routes_admin.yaml
sulu_reservations_admin:
type: rest
resource: "@SuluReservationsBundle/Resources/config/routes/admin.yaml"
prefix: /admin/reservations/api
在:config/routes_website.yaml
sulu_reservations_website:
resource: "@SuluReservationsBundle/Resources/config/routes/website.yaml"
prefix: /sulu-reservations
在 config/services.yaml 中创建以下参数并给出正确的值
comsa_sulu_reservations_mollie_api_key: '%env(COMSA_RE_MOLLIE_API_KEY)%'
创建一个名为 comsa_reservable 的模板(此键用于在添加可预订项目时生成页面)并添加以下字段
<property name="reservable" type="single_reservable_selection">
<meta>
<title lang="en">Reservable</title>
<title lang="nl">Reserveerbaar Item</title>
</meta>
</property>
在 config/services.yaml 中添加以下代码。
sulu_form.request_listener:
class: App\EventListener\RequestListener
arguments:
- '@sulu_form.builder'
- '@sulu_form.handler'
- '@sulu_form.configuration.form_configuration_factory'
- '@event_dispatcher'
- '@router.default'
tags:
- { name: 'kernel.event_listener', event: 'kernel.request', method: 'onKernelRequest' }
此代码覆盖了来自 sulu/form-bundle 的标准 RequestListener。创建以下文件
<?php
declare(strict_types=1);
namespace App\EventListener;
use Sulu\Bundle\FormBundle\Configuration\FormConfigurationFactory;
use Sulu\Bundle\FormBundle\Entity\Dynamic;
use Sulu\Bundle\FormBundle\Event\DynFormSavedEvent;
use Sulu\Bundle\FormBundle\Form\BuilderInterface;
use Sulu\Bundle\FormBundle\Form\HandlerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
class RequestListener
{
protected BuilderInterface $formBuilder;
protected HandlerInterface $formHandler;
protected FormConfigurationFactory $formConfigurationFactory;
protected EventDispatcherInterface $eventDispatcher;
protected RouterInterface $router;
/**
* RequestListener constructor.
*/
public function __construct(
BuilderInterface $formBuilder,
HandlerInterface $formHandler,
FormConfigurationFactory $formConfigurationFactory,
EventDispatcherInterface $eventDispatcher,
RouterInterface $router
) {
$this->formBuilder = $formBuilder;
$this->formHandler = $formHandler;
$this->formConfigurationFactory = $formConfigurationFactory;
$this->eventDispatcher = $eventDispatcher;
$this->router = $router;
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMasterRequest()) {
// do nothing if it's not the master request
return;
}
$request = $event->getRequest();
if (!$request->isMethod('post')) {
// do nothing if it's not a post request
return;
}
try {
$form = $this->formBuilder->buildByRequest($request);
if (!$form || !$form->isSubmitted() || !$form->isValid()) {
// do nothing when no form was found or not valid
return;
}
} catch (\Exception $e) {
// Catch all exception on build form by request
return;
}
/** @var Dynamic $dynamic */
$dynamic = $form->getData();
$configuration = $this->formConfigurationFactory->buildByDynamic($dynamic);
$dynamic->setLocale($request->getLocale()); // Need to be set to request locale for shadow pages, configuraiton will hold the original locale
if ($this->formHandler->handle($form, $configuration)) {
if (!$request->request->get("reservable")) {
$serializedObject = $dynamic->getForm()->serializeForLocale($dynamic->getLocale(), $dynamic);
$dynFormSavedEvent = new DynFormSavedEvent($serializedObject, $dynamic);
$this->eventDispatcher->dispatch($dynFormSavedEvent, DynFormSavedEvent::NAME);
$response = new RedirectResponse('?send=true');
} else {
$response = new RedirectResponse($this->router->generate("comsa_sulu_reservations_select_payment_method"));
}
$event->setResponse($response);
}
}
}
将以下代码添加到 twig 文件以渲染用 Vue.js 编写的预订表单
{% extends "base.html.twig" %}
{% block content %}
{% include '@SuluReservations/reservable.html.twig' %}
{% endblock %}
以下是为创建可预订概览的示例代码
XML
<?xml version="1.0" ?>
<type name="reservable_overview" xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xi="http://www.w3.org/2001/XInclude"
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/template-1.0.xsd">
<meta>
<title lang="en">Reservable overzicht</title>
<title lang="nl">Reserveerbare items overzicht</title>
</meta>
<properties>
<property name="title" type="text_line">
<meta>
<title lang="en">Title</title>
</meta>
</property>
<property name="reservables" type="smart_content">
<meta>
<title lang="en">Reservable overview</title>
<title lang="en">Reserveerbare items overzicht</title>
</meta>
<params>
<param name="provider" value="pages"/>
<param name="properties" type="collection">
<param name="reservable" value="reservable"/>
</param>
</params>
</property>
<xi:include href="includes/width.xml"/>
</properties>
</type>
HTML
<section class="container reservables">
<h2>{{ block.title }}</h2>
<ul class="row">
<table class="d-none d-md-block table table-hover">
<tr>
<th>Evenement</th>
<th>Beschikbare plaatsen</th>
<th>Start op</th>
<th>Eindigt op</th>
<th></th>
</tr>
{% for page in block.reservables %}
{% set spacesLeft = get_spaces_left(page.reservable.id) %}
<tr>
<td>{{ page.title }}</td>
<td>{{ spacesLeft }}</td>
<td>{{ page.reservable.start|date("d/m/Y") }}</td>
<td>{{ page.reservable.end|date("d/m/Y") }}</td>
<td>
{% if spacesLeft == 0 %}
<button type="button" class="btn btn-danger">VOLZET</button>
{% else %}
<a href="{{ sulu_content_path(page.url) }}" class="btn btn-primary">RESERVEER NU</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% for page in block.reservables %}
<li class="col-12 d-md-none">
<div class="reservable-wrapper">
<h3>{{ page.title }}</h3>
<div class="limit">
{% set spacesLeft = get_spaces_left(page.reservable.id) %}
<i class="fas fa-user {% if spacesLeft < 5 %} text-danger {% endif %}"></i><span {% if spacesLeft < 5 %} class="text-danger" {% endif %}>{{ spacesLeft }}</span>
</div>
<div class="button-wrapper">
{% if spacesLeft == 0 %}
<button type="button" class="btn btn-danger">VOLZET</button>
{% else %}
<a href="{{ sulu_content_path(page.url) }}" class="btn btn-primary">RESERVEER NU</a>
{% endif %}
</div>
</div>
</li>
{% endfor %}
</ul>
</section>
SCSS
.reservables {
margin-left: 0;
margin-top: 2rem;
padding: 0;
width: 100%;
h3 {
color: $black;
}
ul {
list-style-type: none;
padding: 0;
}
.reservable-wrapper {
align-items: center;
border: solid 1px $gray-500;
display: flex;
justify-content: center;
margin-bottom: 2rem;
min-height: 14rem;
padding: 1.5rem;
width: 100%;
.button-wrapper {
bottom: 0;
position: absolute;
transform: translateY(-50%);
}
.limit {
border: 1px solid $gray-500;
border-right: 0;
border-top: 0;
min-height: 2rem;
min-width: 3rem;
position: absolute;
right: 0;
text-align: center;
top: 0;
transform: translateX(-35%);
}
svg {
margin-right: 0.5rem;
}
}
}
在 config/packages/doctrine.yaml 中
doctrine:
orm:
mappings:
SuluReservationsBundle:
is_bundle: true
type: attribute
dir: 'Entity'
prefix: 'Comsa\SuluReservations\Entity'
alias: SuluReservations
使用 php bin/console doctrine:schema:update -f
更新您的数据库以添加所需的表
加载默认设置
<?php
namespace App\DataFixtures;
use Comsa\SuluReservations\DataFixtures\AppSeed;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
class AppFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager)
{
}
public function getDependencies()
{
return[
AppSeed::class
];
}
}
将以下内容复制并粘贴到您的应用程序的 DataFixtures 中。运行 Symfony console doctrine:fixtures:load --append