sonata-project/timeline-bundle
Symfony bundle integrating SpyTimelineBundle with Sonata, providing timeline/activity stream support in Sonata apps. Note: this repository is abandoned and not actively maintained; use at your own risk or consider helping to keep it up to date.
Installation:
composer require sonata-project/timeline-bundle
Ensure your project meets the requirements (PHP ≥7.2, Symfony ≥4.4).
Enable the Bundle:
Add to config/bundles.php:
SonataProject\TimelineBundle\SonataTimelineBundle::class => ['all' => true],
Basic Configuration:
Override default settings in config/packages/sonata_timeline.yaml:
sonata_timeline:
default_options:
timeline:
type: 'spy_timeline' # or 'spy_timeline_vertical'
First Use Case: Display a timeline in a Sonata Admin CRUD template:
{{ render(controller('SonataTimelineBundle:Block:timeline', {
'type': 'spy_timeline',
'subject': app.user,
'events': app.user.timelineEvents
})) }}
Event Modeling:
Extend Sonata\TimelineBundle\Model\TimelineEventInterface for custom events:
class UserActivityEvent implements TimelineEventInterface
{
// Implement required methods (getDate(), getSubject(), etc.)
}
Admin Integration:
Use AdminSpread to attach events to Sonata Admins:
use Sonata\TimelineBundle\Spread\AdminSpread;
class UserAdmin extends AbstractAdmin
{
protected function configureSpreads()
{
$this->addSpread(new AdminSpread(
$this,
'user_activity',
'SonataTimelineBundle:Event:timeline',
['type' => 'spy_timeline']
));
}
}
Twig Integration: Render timelines in templates:
{% block sonata_block %}
{{ render(controller('SonataTimelineBundle:Block:timeline', {
'type': 'spy_timeline_vertical',
'subject': entity,
'events': entity.timelineEvents
})) }}
{% endblock %}
Dynamic Data Fetching: Fetch events via Doctrine repositories or custom services:
// In a controller/service
$events = $this->getDoctrine()
->getRepository(UserActivityEvent::class)
->findBy(['user' => $user]);
Custom Event Types:
Extend SpyTimelineBundle's event classes (e.g., SpyTimelineBundle\Event\EventInterface) for specialized behaviors.
Event Grouping:
Use group_by option in timeline configuration to cluster events:
sonata_timeline:
default_options:
timeline:
group_by: 'year'
Translation Domains:
Override translations in translations/messages.en.yaml:
sonata_timeline:
block:
title: 'Activity Timeline'
Event Icons: Customize icons via Twig:
{% set icon = 'fa fa-comment' %}
{{ render(controller('SonataTimelineBundle:Block:timeline', {
'icon': icon,
'events': events
})) }}
Deprecated Dependencies:
SonataEasyExtendsBundle (deprecated in favor of SonataDoctrineBundle).SonataCoreBundle is not required (removed in v3.6.0).Template Paths:
Hardcoded paths in .xml.skeleton files (fixed in v3.1.1) may cause issues if not updated. Use Twig’s namespaced syntax:
{% extends '@SonataTimeline/Block/timeline.html.twig' %}
Event Data Validation:
Empty subject_text or malformed links (e.g., {{ subject|path('sonata_admin_edit', {'id': subject.id}) }}) break rendering. Validate in your event model:
public function getSubjectText(): string
{
return $this->subject ? $this->subject->getName() : '[No Subject]';
}
Symfony 5+ Compatibility:
ManagerRegistry (not RegistryInterface) for Doctrine access.Timeline Not Rendering:
Check if the spy_timeline bundle is installed (composer require spy-timeline-bundle).
Verify event data structure matches TimelineEventInterface.
CSS/JS Conflicts: Load Bootstrap 4+ (required for timeline panels). Override styles in your assets:
.timeline-panel { border-left: 4px solid #3498db; }
Performance: Lazy-load events with pagination:
// In a custom repository
public function findByUserWithPagination(User $user, int $limit = 20)
{
return $this->createQueryBuilder('e')
->where('e.user = :user')
->setParameter('user', $user)
->setMaxResults($limit)
->getQuery()
->getResult();
}
Custom Timeline Types:
Extend SpyTimelineBundle's timeline services (e.g., spy.timeline.type) for non-standard layouts.
Event Filters: Add filters via Twig:
{{ render(controller('SonataTimelineBundle:Block:timeline', {
'events': events|filter(filterEvent)
})) }}
// Custom JS filter
function filterEvent(event) {
return event.type === 'important';
}
Event Actions: Attach click handlers to timeline items:
<div class="timeline-item" onclick="window.location='{{ path('edit_event', {id: event.id}) }}'">
{{ event.title }}
</div>
Doctrine Mappings:
Use SonataDoctrineBundle for metadata (preferred over SonataEasyExtendsBundle):
# config/doctrine/UserActivityEvent.orm.yml
Sonata\TimelineBundle\Model\TimelineEventInterface:
mappedBy: [user, event]
How can I help you explore Laravel packages today?