Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3352242
Fix getOption() to return null for missing keys
tattali Jan 7, 2026
95aaa1b
Fix DateTime mutation side effect in setStart()
tattali Jan 7, 2026
b740e48
Refactor Event class to reset allDay property based on end date and n…
tattali Jan 7, 2026
4c100f3
Introduce custom exceptions for invalid date and JSON parameters in C…
tattali Jan 7, 2026
e4bf281
Add getEventCount method to SetDataEvent and update tests
tattali Jan 7, 2026
914c256
Add PHPStan configuration file for static analysis
tattali Jan 7, 2026
9e9dcb0
Add RequestParser and QueryParameters for improved request handling i…
tattali Jan 7, 2026
2cbb670
Enhance Event class with fluent interface for setter methods and upda…
tattali Jan 7, 2026
6c6d7ca
Implement caching in load method of CalendarController with ETag supp…
tattali Jan 7, 2026
be2cb94
Add depth limit for JSON filters in RequestParser and enhance tests
tattali Jan 7, 2026
8ba091a
Refactor Event class to return non-nullable types and optimize toArra…
tattali Jan 7, 2026
44b0796
Refactor EventTest to remove unnecessary null checks and streamline a…
tattali Jan 7, 2026
68235dc
Add configuration options for cache max age and JSON max depth in Cal…
tattali Jan 7, 2026
070deb5
Add configuration documentation for caching and JSON depth limits
tattali Jan 7, 2026
be6e39d
Refactor code style in RequestParser and EventTest for consistency
tattali Jan 7, 2026
1f2aceb
Remove specific paths from PHPStan analysis command for broader coverage
tattali Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/code_checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:

- name: PHPStan
continue-on-error: ${{ !matrix.is-current }}
run: vendor/bin/phpstan analyse src tests --level 9
run: vendor/bin/phpstan analyse

- name: Execute tests
env:
Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

'php_unit_internal_class' => false,
'php_unit_test_class_requires_covers' => false,
'method_chaining_indentation' => false,
])
->setFinder(
PhpCsFixer\Finder::create()
Expand Down
176 changes: 33 additions & 143 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,22 @@ Documentation

The source of the documentation is stored in the `docs/` folder in this bundle

- [Link the calendar to a CRUD and allow create, update, delete & show events](docs/doctrine-crud.md)
- [Webpack Encore and fullcalendar.js](docs/es6-encore.md)
- [Multi calendar](docs/multi-calendar.md)
- [Migrating to v8.1](docs/upgrade-to-81.md)

### Installation

1. [Download CalendarBundle using composer](#1-download-calendarbundle-using-composer)
2. [Create the subscriber](#2-create-the-subscriber)
3. [Add styles and scripts in your template](#3-add-styles-and-scripts-in-your-template)
4. [Security Configuration](#4-security-configuration)

#### 1. Download CalendarBundle using composer

```sh
composer require tattali/calendar-bundle
```
The recipe will import the routes for you

Check the existence of the file `config/routes/calendar.yaml` or create it
Import the routes:
```yaml
# config/routes/calendar.yaml
calendar:
resource: '@CalendarBundle/config/routing.yaml'
```

#### 2. Create the subscriber
You need to create a subscriber class to load your data into the calendar.

This subscriber must be registered **only if autoconfigure is false**.
```yaml
# config/services.yaml
services:
# ...

App\EventSubscriber\CalendarSubscriber:
```

Then, create the subscriber class to fill the calendar
### Quick Start

See the [doctrine subscriber example](docs/doctrine-crud.md#full-subscriber)
Create a subscriber to populate calendar data:

```php
// src/EventSubscriber/CalendarSubscriber.php
Expand All @@ -71,14 +46,14 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CalendarSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
public static function getSubscribedEvents(): array
{
return [
SetDataEvent::class => 'onCalendarSetData',
];
}

public function onCalendarSetData(SetDataEvent $setDataEvent)
public function onCalendarSetData(SetDataEvent $setDataEvent): void
{
$start = $setDataEvent->getStart();
$end = $setDataEvent->getEnd();
Expand All @@ -87,9 +62,9 @@ class CalendarSubscriber implements EventSubscriberInterface
// You may want to make a custom query from your database to fill the calendar

$setDataEvent->addEvent(new Event(
'Event 1',
'Event title',
new \DateTime('Tuesday this week'),
new \DateTime('Wednesdays this week')
new \DateTime('Wednesday this week')
));

// If the end date is null or not defined, it creates a all day event
Expand All @@ -101,131 +76,46 @@ class CalendarSubscriber implements EventSubscriberInterface
}
```

#### 3. Add styles and scripts in your template

Include the html template were you want to display the calendar:

Add the calendar to your template:
```twig
{% block body %}
<div id="calendar-holder"></div>
{% endblock %}
```

Add styles and js. Click [here](https://fullcalendar.io/download) to see other css and js download methods, you can also found the [plugins list](https://fullcalendar.io/docs/plugin-index)

```twig
{% block javascripts %}
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
new FullCalendar.Calendar(document.getElementById('calendar-holder'), {
initialView: 'dayGridMonth',
eventSources: [{
url: '/fc-load-events',
method: 'POST',
extraParams: { filters: JSON.stringify({}) }
}]
}).render();
});
</script>
{% endblock %}
```

#### 4. Security Configuration

If your application uses Symfony's security firewall, ensure the `/fc-load-events` route is accessible. Add it to your `access_control` configuration:

```yaml
# config/packages/security.yaml
security:
access_control:
- { path: ^/fc-load-events, roles: PUBLIC_ACCESS }
# Or restrict to authenticated users:
# - { path: ^/fc-load-events, roles: ROLE_USER }
```

You can also define your own route pointing directly to the bundle's controller by replacing `config/routes/calendar.yaml`:

```yaml
# config/routes/calendar.yaml
fc_load_events:
path: /fc-load-events
controller: CalendarBundle\Controller\CalendarController::load
```

```yaml
# config/packages/security.yaml
security:
access_control:
- { path: ^/fc-load-events, roles: ROLE_USER }
```

Alternatively, you can create your own controller that wraps the bundle's controller and add security attributes:

```php
// src/Controller/CalendarController.php
namespace App\Controller;

use CalendarBundle\Controller\CalendarController as BaseCalendarController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

class CalendarController
{
public function __construct(
private BaseCalendarController $calendarController,
) {}

#[Route('/fc-load-events', name: 'fc_load_events', methods: ['POST'])]
#[IsGranted('ROLE_USER')]
public function load(Request $request): JsonResponse
{
return $this->calendarController->load($request);
}
}
```

Then disable the bundle's route by removing or commenting out `config/routes/calendar.yaml`.

### Basic functionalities

You will probably want to customize the Calendar javascript to fit the needs of your application.
To do this, you can copy the following settings and modify them by consulting the [fullcalendar.js documentation](https://fullcalendar.io/docs).
```js
document.addEventListener('DOMContentLoaded', () => {
const calendarEl = document.getElementById('calendar-holder');

const calendar = new FullCalendar.Calendar(calendarEl, {
defaultView: 'dayGridMonth',
editable: true,
eventSources: [
{
url: '/fc-load-events',
method: 'POST',
extraParams: {
filters: JSON.stringify({})
},
failure: () => {
// alert('There was an error while fetching FullCalendar!');
},
},
],
headerToolbar: {
start: 'prev,next today',
center: 'title',
end: 'dayGridMonth,timeGridWeek,timeGridDay'
},
timeZone: 'UTC',
});

calendar.render();
});
```
### Documentation

You can use [Plugins](https://fullcalendar.io/docs/plugin-index) to reduce loadtime.
- [Configuration](docs/configuration.md) - Caching, JSON depth limit, routing
- [Doctrine CRUD](docs/doctrine-crud.md) - Create, update, delete events with Doctrine
- [Webpack Encore](docs/es6-encore.md) - ES6 module setup
- [Multi-calendar](docs/multi-calendar.md) - Multiple calendars on one page
- [Security](docs/configuration.md#security) - Securing the endpoint

## Troubleshoot AJAX requests
#### Upgrade Guides

* To debug AJAX requests, show the Network monitor, then reload the page. Finally click on `fc-load-events` and select the `Response` or `Preview` tab
- Firefox: `Ctrl + Shift + E` ( `Command + Option + E` on Mac )
- Chrome: `Ctrl + Shift + I` ( `Command + Option + I` on Mac )
- [Migrating to v8.2](docs/upgrade-to-82.md)
- [Migrating to v8.1](docs/upgrade-to-81.md)

Contribute and feedback
-----------------------
### Troubleshooting

Any feedback and contribution will be very appreciated.
Debug AJAX requests using browser Network monitor (`Ctrl+Shift+E` Firefox, `Ctrl+Shift+I` Chrome).

License
-------
### License

This bundle is under the MIT license. See the complete [license](LICENSE) in the bundle
MIT - See [LICENSE](LICENSE)
7 changes: 7 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ services:
calendar.serializer:
alias: CalendarBundle\Serializer\Serializer

CalendarBundle\Request\RequestParser:
class: CalendarBundle\Request\RequestParser

CalendarBundle\Request\RequestParserInterface:
alias: CalendarBundle\Request\RequestParser

CalendarBundle\Controller\CalendarController:
class: CalendarBundle\Controller\CalendarController
public: true
autowire: true
arguments:
$eventDispatcher: '@event_dispatcher'
$serializer: '@CalendarBundle\Serializer\Serializer'
$requestParser: '@CalendarBundle\Request\RequestParser'
tags:
- controller.service_arguments

Expand Down
Loading
Loading