jean-sebastien-christophe/ux-calendar-bundle 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

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

jean-sebastien-christophe/ux-calendar-bundle

Composer 安装命令:

composer require jean-sebastien-christophe/ux-calendar-bundle

包简介

A modern calendar bundle for Symfony 8 with Turbo and Stimulus, without FullCalendar

README 文档

README

Tests PHPStan

A lightweight calendar bundle for Symfony 8, built on Turbo, Stimulus and AssetMapper. It provides month, week and day views, event management forms and EasyAdmin helpers, without a heavy JavaScript dependency such as FullCalendar. No third-party CDNs are loaded by default.

Compatibility

  • PHP >= 8.4
  • Symfony FrameworkBundle, Form, Validator, TwigBundle, Console, Translation and AssetMapper ^8.0
  • Symfony UX Turbo and Stimulus Bundle ^2.0|^3.0
  • Doctrine ORM ^2.0|^3.0 and DoctrineBundle ^2.0|^3.0
  • EasyAdmin ^4.0|^5.0, optional, for the admin panel
  • Symfony UX ChartJS ^2.0|^3.0, optional, for the dashboard charts

Features

  • Month, week and day views with a built-in switcher and Turbo Streams updates
  • Week and day views rendered as an hourly grid (0:00–23:00 slots), plus an "all-day" row
  • Create, edit, delete and one-off date exclusion
  • Ready-to-use Event entity
  • CalendarEventInterface and CalendarEventRepositoryInterface contracts for custom entities
  • CalendarEventTrait to reuse the common Doctrine mapping
  • Bootstrap theme by default, with default and tailwind variants and optional automatic detection
  • Optional EasyAdmin CRUD, calendar field and dashboard widget

Installation

composer require jean-sebastien-christophe/ux-calendar-bundle

Register the bundle in config/bundles.php:

JeanSebastienChristophe\CalendarBundle\CalendarBundle::class => ['all' => true],

Declare the routes in config/routes/calendar.yaml:

calendar_bundle:
    resource: '@CalendarBundle/src/Controller/'
    type: attribute

The default route is /events. To use /calendar instead, create config/packages/calendar.yaml:

calendar:
    theme: bootstrap
    assets:
        include_cdn: false
    route_prefix: /calendar
    views:
        enabled: [month, week, day]
        default: month
    features:
        all_day_events: true
        colors: true

Create and apply the Doctrine migration:

php bin/console make:migration
php bin/console doctrine:migrations:migrate
php bin/console cache:clear

The CSS assets are exposed through AssetMapper. No assets:install command is required.

The default theme is bootstrap, to stay consistent with EasyAdmin and the classes used by the templates. The bootstrap.css theme only maps the --bs-* variables: Bootstrap itself must therefore be loaded, otherwise the classes (btn, container, alert, …) used in the templates are left unstyled. There are two ways to provide it:

  1. Through your application's AssetMapper (recommended). The calendar's standalone pages automatically render importmap('app') (see the Stimulus section). If your importmap.php imports Bootstrap (for example import 'bootstrap/dist/css/bootstrap.min.css' in assets/app.js), it is loaded on /events with nothing else to do.

  2. Through the Bootstrap CDN, useful for a standalone rendering when the application does not embed Bootstrap:

calendar:
    theme: bootstrap
    assets:
        include_cdn: true

The tailwind, default and auto themes remain available through calendar.theme.

Stimulus

The Stimulus controller is exposed as a Symfony UX controller. Enable it in assets/controllers.json:

{
    "controllers": {
        "@jean-sebastien-christophe/ux-calendar-bundle": {
            "calendar": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    }
}

Your application must start StimulusBundle, for example in assets/bootstrap.js:

import { startStimulusApp } from '@symfony/stimulus-bundle';

startStimulusApp();

The calendar's standalone pages (the @Calendar/calendar/base.html.twig layout) automatically render the importmap('app') entrypoint. This is what loads, on /events, both the calendar Stimulus controller and your application's assets (including Bootstrap if it is in your importmap.php). Your application must therefore expose an entrypoint named app (the Symfony default).

If your entrypoint has a different name, override the importmap block by creating templates/bundles/CalendarBundle/calendar/base.html.twig:

{% extends '@Calendar/calendar/base.html.twig' %}

{% block importmap %}
    {{ importmap('my_entrypoint') }}
{% endblock %}

To embed the calendar in your own layout (instead of the standalone page), override the same template so that it extends your application's layout:

{# templates/bundles/CalendarBundle/calendar/base.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    {{ calendar_theme_css()|raw }}
    {% block calendar_body %}{% endblock %}
{% endblock %}

Then open /events, or /calendar if you configured route_prefix: /calendar.

Exposed routes

{prefix} defaults to /events.

Method Route Name Description
GET {prefix} calendar_index Redirects to the default view (views.default)
GET {prefix}/{year}/{month} calendar_month Renders the monthly calendar
GET {prefix}/week/{date} calendar_week Renders the week containing {date} (Y-m-d)
GET {prefix}/day/{date} calendar_day Renders the {date} day (Y-m-d)
GET, POST {prefix}/new calendar_event_new Renders the form and creates the event
GET, POST {prefix}/{id}/edit calendar_event_edit Renders the form and updates the event
POST {prefix}/{id}/exclude/{date} calendar_event_exclude_date Excludes a date for an event
POST, DELETE {prefix}/{id} calendar_event_delete Deletes the event

Custom entity

The default entity is JeanSebastienChristophe\CalendarBundle\Entity\Event. To use your own entity, it must implement CalendarEventInterface. The CalendarEventTrait provides the common Doctrine mapping.

You can configure the entity with the install command:

php bin/console ux-calendar:install --event-class='App\Entity\MyEvent'
<?php

namespace App\Entity;

use App\Repository\MyEventRepository;
use Doctrine\ORM\Mapping as ORM;
use JeanSebastienChristophe\CalendarBundle\Contract\CalendarEventInterface;
use JeanSebastienChristophe\CalendarBundle\Trait\CalendarEventTrait;

#[ORM\Entity(repositoryClass: MyEventRepository::class)]
#[ORM\HasLifecycleCallbacks]
class MyEvent implements CalendarEventInterface
{
    use CalendarEventTrait;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    public function __construct()
    {
        $this->createdAt = new \DateTime();
        $this->updatedAt = new \DateTime();
    }

    public function getId(): ?int
    {
        return $this->id;
    }
}

The associated repository must implement CalendarEventRepositoryInterface, because the bundle's controller loads the monthly events through findByMonth().

<?php

namespace App\Repository;

use App\Entity\MyEvent;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use JeanSebastienChristophe\CalendarBundle\Contract\CalendarEventRepositoryInterface;

/**
 * @extends ServiceEntityRepository<MyEvent>
 */
final class MyEventRepository extends ServiceEntityRepository implements CalendarEventRepositoryInterface
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, MyEvent::class);
    }

    public function findByMonth(int $year, int $month): array
    {
        $start = new \DateTime(sprintf('%d-%02d-01 00:00:00', $year, $month));
        $end = (clone $start)->modify('last day of this month')->setTime(23, 59, 59);

        return $this->createQueryBuilder('e')
            ->where('e.startDate BETWEEN :start AND :end')
            ->orWhere('e.endDate BETWEEN :start AND :end')
            ->orWhere('e.startDate <= :start AND e.endDate >= :end')
            ->setParameter('start', $start)
            ->setParameter('end', $end)
            ->orderBy('e.startDate', 'ASC')
            ->getQuery()
            ->getResult();
    }
}

Week and day views (optional interface)

The week and day views work out of the box: the controller falls back to findByMonth() for the months covered. For a single query optimized over an arbitrary range, also implement CalendarEventRangeRepositoryInterface:

use JeanSebastienChristophe\CalendarBundle\Contract\CalendarEventRangeRepositoryInterface;
use JeanSebastienChristophe\CalendarBundle\Contract\CalendarEventRepositoryInterface;

final class MyEventRepository extends ServiceEntityRepository implements
    CalendarEventRepositoryInterface,
    CalendarEventRangeRepositoryInterface
{
    // ... findByMonth() ...

    public function findByDateRange(\DateTimeInterface $start, \DateTimeInterface $end): array
    {
        return $this->createQueryBuilder('e')
            ->where('e.startDate BETWEEN :start AND :end')
            ->orWhere('e.endDate BETWEEN :start AND :end')
            ->orWhere('e.startDate <= :start AND e.endDate >= :end')
            ->setParameter('start', $start)
            ->setParameter('end', $end)
            ->orderBy('e.startDate', 'ASC')
            ->getQuery()
            ->getResult();
    }
}

Then configure the bundle:

calendar:
    event_class: App\Entity\MyEvent

This value is used by the controllers, the argument resolver and EventType. A createForm(EventType::class, $event) call therefore expects the configured entity, not the bundle's default entity.

EasyAdmin

The EasyAdmin helpers are optional. Install EasyAdmin if needed:

composer require easycorp/easyadmin-bundle

Then reference the provided CRUD in your dashboard:

use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use JeanSebastienChristophe\CalendarBundle\Admin\EventCrudController;
use JeanSebastienChristophe\CalendarBundle\Entity\Event;

yield MenuItem::linkToCrud('Events', 'fa fa-calendar', Event::class)
    ->setController(EventCrudController::class);

See also:

Quality

The repository does not version vendor/. Install the dependencies with Composer:

composer install

Useful commands before a PR or a tag:

composer validate --strict
composer analyse
composer test

composer analyse runs PHPStan at level 5 with the Symfony and Doctrine extensions.

Roadmap

  • Drag and drop to move events
  • Full recurring events
  • iCal export
  • Event categories
  • REST API

Contributing

  1. Fork the project
  2. Create a branch (git checkout -b feature/amazing-feature)
  3. Install the dependencies (composer install)
  4. Run composer analyse and composer test
  5. Push the branch and open a Pull Request

License

MIT

Support

For any question or issue, open an issue on GitHub: https://github.com/JsD3v/ux-calendar-bundle/issues

jean-sebastien-christophe/ux-calendar-bundle 适用场景与选型建议

jean-sebastien-christophe/ux-calendar-bundle 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 9 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 06 月 01 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「symfony」 「event」 「calendar」 「turbo」 「stimulus」 「hotwire」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

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

围绕 jean-sebastien-christophe/ux-calendar-bundle 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-01