Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Activity Bundle Laravel Package

kitpages/activity-bundle

Kitpages ActivityBundle is a Symfony bundle for tracking and displaying user activity streams. It provides Doctrine-backed activity entities, services, and templates to log actions and render feeds, making it easier to add timeline or notification-style activity lists to an application.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: kitpages/activity-bundle is a Symfony bundle, not a Laravel package. It depends on Symfony DI, Doctrine ORM, and Twig. In a Laravel codebase you cannot use it idiomatically — you would need doctrine/orm plus the Symfony container, which is a bad trade. The Laravel equivalent is spatie/laravel-activitylog, or a hand-rolled activities table with a model observer. The rest of this assessment assumes you are working in a Symfony 3.4/4.x app (the era of the last release, September 2018) and treating the bundle as legacy-but-workable infrastructure.

Minimal steps for a Symfony app:

  1. composer require kitpages/activity-bundle — pin the exact tag that resolves. Expect the constraints to be php: ^7.x / symfony: ^3.x|^4.x; check with composer why-not php 8.1 before you commit.
  2. Register the bundle in app/AppKernel.php (or config/bundles.php on Symfony 4 Flex):
    new Kitpages\ActivityBundle\KitpagesActivityBundle(),
    
  3. Create the activity table: bin/console doctrine:schema:update --force (or generate a migration and inspect the SQL — the bundle ships its own mapping, so let Doctrine diff it).
  4. Declare at least one activity definition in config so the manager knows what a "type" is:
    kitpages_activity:
        activity_definition:
            comment_created:
                class: AppBundle\Activity\CommentCreatedActivity
                template: activity/comment_created.html.twig
    
  5. Verify the wiring: bin/console debug:container | grep kitpages_activity to find the manager service id, then call it once from a controller to see a row land in the table.

Where to look first: the bundle's Activity/ directory (base class / interface you extend for each activity type), the ActivityManager service (the only public API you should call), and Resources/config/services.yml to see what is actually registered. The README covers the config shape; it does not cover upgrades, which is where you will spend your time.

First use case: "user commented on a page." Write a domain service method that persists the comment and calls the activity manager with the type comment_created and a payload of ['comment_id' => ..., 'page_id' => ..., 'user_id' => ...]. Render it on the profile page as a one-line feed item. That end-to-end slice exercises the config, the manager, the Doctrine mapping, and the template — everything that can break.

Implementation Patterns

One class + one Twig template per activity type. The bundle's model is a registry of named activity definitions, each pointing at a class and a template. Treat that config block as your activity taxonomy: comment_created, page_published, user_followed. Resist the urge to overload a single generic type with a subtype field — the template lookup is the whole point.

Record from a single choke point. Two good patterns:

  • Explicit, in the domain service — CommentService::post() writes the comment and the activity in the same transaction. Best for correctness: if the comment rolls back, so does the activity.
  • Event subscriber — a kernel.event_subscriber (or Doctrine postPersist listener) that reacts to your domain events and converts them into activities. Best when activities are cross-cutting and you don't want domain services depending on the activity manager.

Do the first one when the activity is a product feature; do the second when it's an audit trail.

public function post(Page $page, User $user, string $body): Comment
{
    $comment = new Comment($page, $user, $body);
    $this->em->persist($comment);

    $this->activityManager->createActivity('comment_created', [
        'comment_id' => $comment->getId(),
        'page_id'    => $page->getId(),
        'user_id'    => $user->getId(),
    ]);

    $this->em->flush();
    return $comment;
}

(Confirm the exact createActivity signature against the installed tag — it is the one thing the README does document, and it changed between minor versions.)

Payload = ids and scalars only, not entities. The activity row is a historical record. If you serialize whole entities into the data column, changing a Comment property later breaks every old row. Store ids, resolve at render time through a repository, and accept that a deleted entity renders as "[deleted]".

Render in the feed loop. Each activity's configured template gets the decoded payload, so the item template stays tiny and type-specific:

{% for activity in feed %}
    <li class="activity-item">
        {{ include(activity.template, { activity: activity, payload: activity.data }) }}
    </li>
{% endfor %}

Feed queries should be your own. Use the manager to write, but write a repository method to read: findByFilters(array $types, ?User $user, \DateTime $since, $limit, $offset). The bundle's list helpers are thin; once you need "only my follows, excluding my own actions, last 30 days," you are writing the DQL anyway.

Same table, two audiences. A user-facing feed and a back-office audit log can share the table and differ only by filter and template. That is the cheapest way to get audit value out of this bundle.

Gotchas and Tips

  • Effectively unmaintained. 4 stars, 0 dependents, no release since 2018, no security policy. Treat it as vendored code: pin the exact version in composer.json, and expect to patch the package in place (or fork it) the first time you move to PHP 8 or Symfony 5+.
  • It will not install cleanly on modern stacks. The composer constraints are the blocker, not the code. Check with composer why-not, then either --ignore-platform-reqs (risky), fork with widened constraints, or vendor the ~10 classes you actually use. Vendoring is usually the right call.
  • Not Laravel. No service provider, no Eloquent, no facade, no queue integration. If the question is "can we drop this into our Laravel app," the answer is no — use spatie/laravel-activitylog and save a week.
  • Check the data column encoding. If the payload is PHP-serialized you inherit a class-rename landmine; convert to JSON in your own manager wrapper if the bundle allows it, or write a migration to normalize old rows.
  • Nullable user. Activities outlive users. Every template must guard {% if activity.user is not null %} or you get 500s on deleted accounts — this is the single most common production failure for activity feeds.
  • XSS via |raw. Activity payloads originate from user input (comment excerpts, page titles). Escape on write or strictly escape in the template; do not |raw a payload field.
  • N+1 on render. Each activity dereferences a user and possibly a target entity. Fetch-join the user in your feed query, or batch-load the targets by id before the loop.
  • Unbounded growth. There is no retention policy. Add a console command that deletes activities older than N days (or beyond a per-user cap) and run it from cron.
  • Transaction discipline. createActivity flushes on its own in some versions, which means an activity can survive a rolled-back parent write. Wrap parent + activity in one beginTransaction/commit, or accept eventual inconsistency deliberately.
  • Override templates the Symfony way: app/Resources/KitpagesActivityBundle/views/... (Symfony 3) or templates/bundles/KitpagesActivityBundle/... (Symfony 4), rather than editing the vendor directory.
  • Debugging: bin/console debug:container kitpages_activity for the service id, debug:config kitpages_activity to confirm your definitions are actually loaded after a cache clear. A missing definition usually fails at write time with an unhelpful manager-level exception — put a try/catch with the activity type name in the message.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect