Pre-1.0. Not intended for public use yet. The API is subject to undocumented change, and these pages may already be wrong about it.

Skip to content

Composites ​

Introduction ​

A composite is one activity whose object is a collection, such as several tasks completed together.

Recording Composites ​

app/Http/Controllers/CompleteTasksController.php
php
<?php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Storyfeed\Facades\Storyfeed;

class CompleteTasksController extends Controller
{
    public function __invoke(Request $request): RedirectResponse
    {
        $tasks = Task::whereIn('id', $request->input('tasks'))->get();

        $tasks->each->update(['completed_at' => now()]);

        Storyfeed::activity()
            ->by($request->user())
            ->action('complete')
            ->objects($tasks)
            ->publish();

        return back();
    }
}
php
<?php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Storyfeed\Facades\Storyfeed;

class CompleteTasksController extends Controller
{
    public function __invoke(Request $request): RedirectResponse
    {
        $tasks = Task::whereIn('id', $request->input('tasks'))->get();

        $tasks->each->update(['completed_at' => now()]);

        Storyfeed::record(
            verb: 'complete',
            objects: $tasks,
            actor: $request->user(),
        );

        return back();
    }
}
RB
Robin Buckley completed 2 tasks

This writes a parent activity and one activity per task. log() returns each task separately. live() returns the parent as one item with axis: 'composite'. In summary(), it contributes a phrase to its actor's summary row. keepLatest() never replaces a composite.

Defining Composite Headlines ​

Define two headlines on the verb: one for the group of tasks and one for the composite activity. Both describe the collection, so neither belongs on a single task type:

routes/feed.php
php
use Storyfeed\Facades\Story;
use Storyfeed\Grouping\GroupBuilder;

Story::verb('complete')->grouped(
    fn (GroupBuilder $group) => $group->composite(
        ':actor completed :count tasks', // the group
        ':actor completed tasks',        // the activity itself; required
    ),
);
RB
Robin Buckley completed 2 tasks

Bundling Activities Automatically ​

Marking Models Bundleable ​

Implement Bundleable to combine batched activities on a model into a composite:

app/Models/Task.php
php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Storyfeed\Concerns\InteractsWithFeed;
use Storyfeed\Contracts\Bundleable;
use Storyfeed\Contracts\Feedable;

class Task extends Model implements Feedable, Bundleable
{
    use InteractsWithFeed;
}

For a model you cannot edit, register its morph alias:

app/Providers/AppServiceProvider.phpboot()
php
use Storyfeed\Facades\Storyfeed;

Storyfeed::bundleables(['task']);

Within a batch, automatic bundling considers unclaimed activities with a bundleable object. Activities must share the verb, object type, target, context, and publication date. The minimum counts distinct objects within each such set; a burst spanning those boundaries can remain separate.

Automatic bundling is enabled by default. Configure it in config/storyfeed.php:

KeyDefaultMeaning
grouping.composite.autotruecombine batched Bundleable activities into composites
grouping.composite.min_objects2minimum distinct objects per composite

Closing Batches ​

Bundling runs when the actor's batch closes, so leave batching enabled. The batch closes on the actor's next publication after the window expires. Schedule storyfeed:close-batches to close it on time without another publication.

Bundling Existing Activities ​

Bundleable applies only to new activities. To bundle existing activities:

bash
php artisan storyfeed:bundle
php artisan storyfeed:bundle --window=30   # only batches closed in the last 30 days

You may run this command more than once. Creating a composite changes the sync_token, so clients must discard accumulated items and fetch the feed from the start. See the sync token rule.

Released under the MIT License.