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

Recording Activities ​

Introduction ​

An activity records a verb and its participants. Publish it from the code that handles the action, such as a controller, observer, or event listener.

Publishing Activities ​

Assign the activity's roles and call the publish method:

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

namespace App\Http\Controllers;

use App\Http\Requests\PlaceOrderRequest;
use App\Models\Shop;
use Illuminate\Http\RedirectResponse;
use Storyfeed\Facades\Storyfeed;

class OrderController extends Controller
{
    public function store(
        PlaceOrderRequest $request,
        Shop $shop,
    ): RedirectResponse {
        $order = $shop->orders()->create($request->validated());

        Storyfeed::activity()
            ->by($request->user())
            ->action('place', $order)
            ->to($shop)
            ->publish();

        return to_route('orders.show', $order);
    }
}
php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\PlaceOrderRequest;
use App\Models\Shop;
use Illuminate\Http\RedirectResponse;
use Storyfeed\Facades\Storyfeed;

class OrderController extends Controller
{
    public function store(
        PlaceOrderRequest $request,
        Shop $shop,
    ): RedirectResponse {
        $order = $shop->orders()->create($request->validated());

        Storyfeed::record(
            verb: 'place',
            object: $order,
            actor: $request->user(),
            target: $shop,
        );

        return to_route('orders.show', $order);
    }
}

The first argument to the action method is the verb, a string describing the action. This example records place. Define its headline in The Feed File.

You may also call the record method on the Storyfeed facade, passing each role as a named argument.

Assigning Roles ​

RoleMeaningExample
actorwho performed the actionthe customer
objectthe entity acted onthe order
targetthe entity the action was directed atthe shop
contextthe containing entitythe shop where the action occurred
originthe sourcethe source of an accepted invitation
resultthe entity produceda receipt or generated file
instrumentthe tool or service usedthe device used to take an order

Choose the role based on the entity's involvement. A tablet is a target when an order is sent to it, or an instrument when used to take the order.

These roles come from W3C Activity Streams 2.0.

Role Aliases ​

Each role has a method with the same name, such as actor or object. The verb method sets the verb. You may also use these aliases:

AliasSetsMeaning
->by()actorwho performed the action
->action()verb and objectthe action and affected entity
->using()instrumentthe tool or service used
->resulting()resultthe entity produced
->to() ->for() ->on() ->with() ->into() ->in() ->from()targetthe entity the action was directed at

These aliases let you compose activities expressively, like a natural-language sentence.

Assigning the Actor ​

The actor is the user or model that performed the activity. You may specify the actor using the by method:

php
Storyfeed::activity()
    ->by($order->customer) 
    ->action('place', $order)
    ->to($order->shop)
    ->publish();
php
Storyfeed::record(
    verb: 'place',
    object: $order,
    actor: $order->customer, 
    target: $order->shop,
);

With the default configuration and no scoped or verb-specific actor, omitting by records the authenticated user, or no actor when nobody is signed in. Scopes, carried job identity, verb actors, custom resolvers, and a configured fallback party can change that selection. See Role Precedence.

Use by(null) to bypass default actor selection explicitly. An anonymous activity has no recorded actor.

Adding Activity Data ​

Define each new verb and its headline before publishing. By default, local and testing environments throw UnknownVerb for an unregistered verb and UnauthoredActivity for an object-type/verb pair without a headline. A concrete Story definition satisfies both checks:

routes/feed.php
php
use App\Models\MenuItem;
use Storyfeed\Facades\Story;

Story::for(MenuItem::class)->verb('reprice')
    ->headline(':actor changed the price of :object');

Here $product is a MenuItem.

Use the data method to store arbitrary values on an activity. Storyfeed returns them in the activity's data field:

php
$from = $product->price;

$product->update(['price' => $request->integer('price')]);

Storyfeed::activity()
    ->by($request->user())
    ->action('reprice', $product)
    ->data(['from' => $from, 'to' => $product->price]) 
    ->publish();
php
$from = $product->price;

$product->update(['price' => $request->integer('price')]);

Storyfeed::record(
    verb: 'reprice',
    object: $product,
    actor: $request->user(),
    data: ['from' => $from, 'to' => $product->price], 
);
SH
Steve Harrington changed the price of USS Butterscotch
[
  {
    "kind": "activity",
    "id": "a-price",
    "verb": "reprice",
    "published_at": "1985-07-02T11:00:00.000000Z",
    "headline_template": ":actor changed the price of :object",
    "headline": null,
    "glyph": "tag",
    "glyph_intent": null,
    "actor": {
      "type": "user",
      "id": "101",
      "label": "Steve Harrington",
      "url": "/users/101",
      "attributes": {},
      "modal": false,
      "data": {},
      "media": null,
      "body": null,
      "tombstone": null
    },
    "object": {
      "type": "menu_item",
      "id": "101",
      "label": "USS Butterscotch",
      "url": "/menu/butterscotch",
      "attributes": {},
      "modal": false,
      "data": {},
      "media": null,
      "body": null,
      "tombstone": null
    },
    "target": {
      "type": "venue",
      "id": "101",
      "label": "Scoops Ahoy",
      "url": "/venues/scoops",
      "attributes": {},
      "modal": false,
      "data": {},
      "media": {
        "icon": null,
        "image": null,
        "files": [],
        "preview": {
          "src": "/media/worlds/stranger-things/parlour.jpg",
          "mediaType": "image/jpeg",
          "width": 960,
          "height": 720,
          "alt": "An ice cream counter"
        },
        "url": null
      },
      "body": [
        {
          "$body": "Storyfeed/Body/Image",
          "$v": 1,
          "caption": "Scoops Ahoy",
          "alt": "Scoops Ahoy",
          "width": null,
          "height": null,
          "image": "preview"
        }
      ],
      "tombstone": null
    },
    "context": null,
    "origin": null,
    "result": null,
    "instrument": null,
    "data": {
      "from": 275,
      "to": 295
    },
    "tombstoned": [],
    "redundant": false,
    "missing_headline_template": null,
    "missing_headline": null
  }
]

Setting the Publication Time ​

To set an earlier publication time, such as when importing records, call the publishedAt method:

php
use App\Models\MenuItem;
use App\Models\User;
use Storyfeed\Facades\Storyfeed;

foreach ($rows as $row) {
    Storyfeed::activity()
        ->by(User::findOrFail($row['user_id']))
        ->action('reprice', MenuItem::findOrFail($row['menu_item_id']))
        ->data(['from' => $row['from'], 'to' => $row['to']])
        ->publishedAt($row['changed_at']) 
        ->publish();
}
php
use App\Models\MenuItem;
use App\Models\User;
use Storyfeed\Facades\Storyfeed;

foreach ($rows as $row) {
    Storyfeed::record(
        verb: 'reprice',
        object: MenuItem::findOrFail($row['menu_item_id']),
        actor: User::findOrFail($row['user_id']),
        data: ['from' => $row['from'], 'to' => $row['to']],
        publishedAt: $row['changed_at'], 
    );
}
SH
Steve Harrington changed the price of USS Butterscotch

Recording Multiple Objects ​

To record an activity involving multiple objects, call the objects method. Storyfeed stores a parent activity and one activity per object. Define the upload verb before recording the photos. This verb-wide definition also covers the parent activity:

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

Story::verb('upload')->headline(':actor uploaded :object');

Then publish the photos:

php
Storyfeed::activity()
    ->by($request->user())
    ->verb('upload')
    ->objects($photos) 
    ->publish();
php
Storyfeed::record(
    verb: 'upload',
    objects: $photos, 
    actor: $request->user(),
);

See Composites for how these activities appear in the feed and how to define their headlines.

Released under the MIT License.