定制 comsa/sulu-reservations 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

comsa/sulu-reservations

Composer 安装命令:

composer require comsa/sulu-reservations

包简介

Reservation bundle for Sulu

README 文档

README

How it works

What is this bundle?

  • Reservation system
  • Integerated with Sulu FormBundle

The bundles makes use of reservables that you can create. These can be linked to reservable options that you can create as well. The bundle supports single payment and payments per person. Each reservable can have different price groups that have their own price (e.g. adults => €5, children => €3). Payments are configured inside the settings tab of the module. There are 3 types supported which can have their own extra cost (bank, cash or mollie).

Installation

Add to assets/admin/package.json:

"sulu-reservations-bundle": "file:node_modules/@sulu/vendor/comsa/sulu-reservations/Resources/js"

Add to assets/website/package.json:

"sulu-reservations-bundle": "file:../../vendor/comsa/sulu-reservations/Resources/js",

Run npm install in both

Add it to assets/admin/index.js:

import 'sulu-reservations-bundle/admin'

Add it to assets/website/index.js:

import 'sulu-reservations-bundle/website'

And build both it using npm run build, this might take a while :)

Add routes to both routes_admin.yaml and routes_website.yaml

In: config/routes_admin.yaml

sulu_reservations_admin:
  type: rest
  resource: "@SuluReservationsBundle/Resources/config/routes/admin.yaml"
  prefix: /admin/reservations/api

In: config/routes_website.yaml

sulu_reservations_website:
  resource: "@SuluReservationsBundle/Resources/config/routes/website.yaml"
  prefix: /sulu-reservations

Create the following parameter in config/services.yaml and give the correct value:

    comsa_sulu_reservations_mollie_api_key: '%env(COMSA_RE_MOLLIE_API_KEY)%'

Create a template named comsa_reservable (this key is used to generate pages when adding a reservable) and add the following field:

<property name="reservable" type="single_reservable_selection">
    <meta>
        <title lang="en">Reservable</title>
        <title lang="nl">Reserveerbaar Item</title>
    </meta>
</property>

Add the following code in 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' }

This code overwrites the standard RequestListener from the sulu/form-bundle. Create the following file:

<?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);
        }
    }
}

Add the following code to the twig file to render the reservation form written in Vue.js

{% extends "base.html.twig" %}

{% block content %}
  {% include '@SuluReservations/reservable.html.twig' %}
{% endblock %}

The following is example code to create a reservable-overview:

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;
    }
  }
}

In config/packages/doctrine.yaml:

doctrine:
  orm:
    mappings:
      SuluReservationsBundle:
        is_bundle: true
        type: attribute
        dir: 'Entity'
        prefix: 'Comsa\SuluReservations\Entity'
        alias: SuluReservations

Update your database to add the needed tables using php bin/console doctrine:schema:update -f

Load default settings:

<?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
        ];
    }

}

Copy and paste this into the DataFixtures of your app. Run Symfony console doctrine:fixtures:load --append

comsa/sulu-reservations 适用场景与选型建议

comsa/sulu-reservations 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 157 次下载、GitHub Stars 达 1, 最近一次更新时间为 2020 年 12 月 04 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 comsa/sulu-reservations 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 comsa/sulu-reservations 我们能提供哪些服务?
定制开发 / 二次开发

基于 comsa/sulu-reservations 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 157
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 13
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-12-04