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

The Feed File ​

Define activity headlines, icons, and group headlines in routes/feed.php. The installer creates this file.

Basic Definitions ​

Defining a Headline ​

A headline describes an activity using a template:

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

Story::for(Order::class)
    ->verb('place')
    ->headline(':actor placed :object with :target');

The for method specifies the object type, and the verb method specifies the recorded verb. This headline applies to place activities involving an order.

Headline Templates ​

Role Tokens ​

TokenEntity
:actorwho performed the action
:objectthe entity acted on
:targetthe entity the action was directed at
:contextthe containing entity
:originthe source
:resultthe entity produced
:instrumentthe tool or service used

Each token is replaced with the entity's label and linked when it has a URL.

Optional Segments ​

Enclose an optional phrase in square brackets to include it only when its referenced roles are filled:

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

Story::for(Order::class)
    ->verb('place')
    ->headline(':actor placed :object[ with :target]');

Storyfeed resolves optional segments before returning the payload. In this example, it includes with :target only when the activity has a target. Without brackets, an empty role leaves its token in the template.

Dynamic Headlines ​

To choose a headline for each activity, pass a closure that returns a template:

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

Story::for(Order::class)
    ->verb('place')
    ->headline(
        fn (ActivityContext $activity) => $activity->boolean('rush') 
            ? ':actor rushed :object to :target'
            : ':actor placed :object with :target',
    );

The closure receives an ActivityContext, which provides typed helpers for the activity’s data and accessors for its roles. It runs when Storyfeed retrieves the feed. Returned role tokens are rendered as entity labels and links. Text without role tokens is displayed unchanged.

Icons and Intents ​

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

Story::for(Order::class)
    ->verb('place')
    ->headline(':actor placed :object with :target')
    ->icon('shopping-bag');

Story::for(Order::class)
    ->verb('complete')
    ->headline(':actor completed :object')
    ->icon('receipt');

Use the intent method to assign an application-defined value, such as success or danger. Storyfeed returns it in the glyph_intent field:

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

Story::for(Order::class)->verb('complete')->icon('receipt')->intent('success');
SH
[
  {
    "kind": "activity",
    "id": "a-complete",
    "verb": "complete",
    "published_at": "1985-07-02T12:08:00.000000Z",
    "headline_template": ":actor completed :object",
    "headline": null,
    "glyph": "receipt",
    "glyph_intent": "success",
    "actor": {
      "type": "user",
      "id": "101",
      "label": "Steve Harrington",
      "url": "/users/101",
      "attributes": {},
      "modal": false,
      "data": {},
      "media": null,
      "body": null,
      "tombstone": null
    },
    "object": {
      "type": "order",
      "id": "1035",
      "label": "Order #1035",
      "url": "/orders/1035",
      "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": {},
    "tombstoned": [],
    "redundant": false,
    "missing_headline_template": null,
    "missing_headline": null
  }
]

See Rendering to display the icon and apply its intent.

Definition Groups ​

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

Story::for(Order::class)->group(function () {
    Story::verb('place')->headline(':actor placed :object with :target');
    Story::verb('complete')->headline(':actor completed :object');
});

All verb definitions inside the closure apply to orders.

Resource Definitions ​

The resource method on the Story facade defines create, update, delete, and restore for a model:

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

Story::resource(Order::class);
VerbHeadlineIcon
create:actor created :objectplus
update:actor updated :objectpencil
delete:actor deleted :objecttrash
restore:actor restored :objectrotate-ccw

Use the only or except methods to select resource verbs. Exclude a verb before defining it separately:

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

Story::resource(Order::class)->except('update');

Story::for(Order::class)->verb('update')->headline(':actor changed :object');

Defining the same verb in both places causes an error with both source locations.

Definition Precedence ​

Storyfeed applies definitions in this order, from most to least specific:

DeclarationMatches
Story::for(Order::class)->verb('place')that verb on that object type
Story::for(Order::class)->fallback()every verb on that object type
Story::verb('place')that verb on any object type
Story::fallback()everything with no more specific entry

This precedence applies to both headlines and intents. Use a fallback to define a headline for order verbs without their own definition:

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

Story::for(Order::class)->fallback()->headline(':actor updated :object');

You may also define group headlines and headlines for deleted models.

Listing Definitions ​

List the definitions loaded by your application:

bash
php artisan storyfeed:list

Use --type=order or --verb=place to filter definitions, and --json for JSON output. Each entry includes the headline, icon, and declaration location. See Commands for all options.

Caching Definitions ​

Cache definitions during deployment:

bash
php artisan storyfeed:cache

Storyfeed loads cached definitions without evaluating routes/feed.php. The optimize Artisan command also caches these definitions. Rebuild the cache after changing them. To clear it:

bash
php artisan storyfeed:clear

Storyfeed serializes closure headlines into the cache. If a closure cannot be serialized, the command fails and reports its source location. See Commands.

Released under the MIT License.