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

Statamic Products Laravel Package

goldnead/statamic-products

goldnead/statamic-products is a Statamic addon for building and managing product listings within your site. It provides product data structures and publishing tools to help you create organized, searchable catalogs in Laravel/Statamic projects.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Upgrade with composer update to ^1.12.0. This is a minor release: no breaking changes, no deprecations, no migration step. It builds on 1.11.1, so a single upgrade covers both the file-picker boundary fix and the new credit-line timestamps.
  • Two entry points to know, depending on what you touch day to day:
    • Picker/scoping work: wherever you register access containers (service provider, policy, middleware) — AccessContainers::allow([...]).
    • Credit/balance work (new): wherever you read credit lines — Access::creditLines(...), Access::creditLinesAt($at), and AccessView::creditLines(includeEnded: …, at: $at).
  • Minimal first use case — answer "what did this access look like when it was granted?" instead of "what does it look like now?":
use Access;

// Lines that existed at the moment the grant was issued.
$linesAtGrant = Access::creditLinesAt($grant->granted_at);

// Current behaviour is untouched:
$linesNow = Access::creditLines();
  • Sanity-check the upgrade in two moves: (1) open the picker against a folder that contains only a subfolder (downloads/baraye/) — the 1.10.x regression symptom; (2) create a new credit line, then call creditLinesAt() with a timestamp before that line was added and confirm it is excluded.

Implementation Patterns

Replay grants with at, don't re-derive history by hand. The point of added_at is that a line added to an already-granted access neither appears as "missing" to earlier purchases nor gets re-issued to them. Anywhere you currently compute "what was included", pass the grant time:

use AccessView;

$included = AccessView::creditLines(
    includeEnded: false,
    at: $grant->granted_at,
);

Keep the "current" and "as-of" paths visibly separate. creditLines() without at behaves exactly as before, so leaving existing call sites alone is safe. Introduce a small helper or query object so new code has to make a deliberate choice, rather than sprinkling at: arguments through controllers:

final class GrantSnapshot
{
    public static function lines(Grant $grant): Collection
    {
        return AccessView::creditLines(
            includeEnded: false,
            at: $grant->granted_at,
        );
    }
}

Let added_at be set by the model, never by the caller. The model stamps it when it numbers the line. A form, importer, or API payload that carries added_at will be ignored on the way back out (the stored value wins), so don't build UI fields for it and don't write to it from imports.

Backfill deliberately, if at all. Lines from the takeover (which bring their own number) and anything saved before 1.12.0 have no added_at, meaning "has always been there". Decide a policy per environment: either accept that pre-upgrade lines are treated as infinitely old, or backfill once via a direct write (a normal save will not let you change it) and record the cutoff date in your release notes.

Point the picker at the parent, not the leaf. Subfolders are visible again since 1.11.1, so scope to clients/42 and let users drill into clients/42/invoices. Before that patch you had to guess the exact leaf folder, which made dynamic folder structures nearly unusable.

Treat the server boundary as the contract. The container is enforced server-side on every file operation and rejects anything outside the folder, including ../ traversal. The container is a path boundary, not an authorization system — keep your Gate alongside it:

Gate::define('use-file-picker', fn (User $u, Client $c) => $u->can('view', $c));

$this->authorize('use-file-picker', $client);
AccessContainers::allow(['container' => "clients/{$client->id}"]);

Wire rejections back into the UI. The picker no longer prevents navigation out of the container, so a user can browse to a sibling folder and try to select from it. Catch the rejection around the store/attach call and surface "That file is outside your folder" instead of a raw 403 page.

Testing. Two suites now:

  • Picker: create clients/1/downloads/baraye/file.pdf, point the container at clients/1/downloads, assert the subfolder lists and a selection inside it succeeds; then assert clients/2/... and ../../../etc/passwd are rejected.
  • Credit lines: create a line, travel past it with Carbon::setTestNow(), create a second, and assert creditLinesAt() around both boundaries — including the exact instant ($at === $added_at) so the inclusive/exclusive edge is pinned by a test, not by assumption. Also assert a legacy row with added_at = null is returned for very old timestamps.

Gotchas and Tips

  • null added_at means "always existed", not "never existed". Legacy and takeover lines are included in every creditLinesAt() query. If you need historically accurate sums, that's a conscious policy decision, not something the package decides for you.
  • added_at is immutable once set. A form or import that drops or changes it gets the stored value back. Correcting a bad value requires a direct DB update; don't plan on fixing it through the model.
  • Timezone and boundary semantics. Values are ISO 8601 — store and compare in UTC, and parse $at through Carbon rather than string-comparing. Pin the boundary (line added exactly at $at) with a test in your app; don't infer it from a report.
  • ended_at is unchanged. at: filters when a line started existing; includeEnded still governs whether ended lines are returned. Combine them explicitly rather than assuming at: also rewinds expiries.
  • The 1.10.x picker regression you must know about. The picker started in the folder and set core's "restrict to folder", hiding subfolders; a folder holding only a subfolder rendered as "1–2 of 2" with zero visible rows. If you have a support ticket matching that symptom, the fix is the upgrade — not a config change.
  • The picker UI no longer enforces the boundary. It only starts in the folder. Files outside the container are refused server-side, but the UX is "I picked a file and it errored". Don't assume the picker's visible scope equals the permitted scope.
  • Don't rely on the picker for security. Anyone can POST a path directly. The server-side check (including ../ traversal, unchanged since 1.10.0) is the guarantee to lean on.
  • Debugging an empty picker. Check in order: (1) the container path exists on the configured disk; (2) permissions on the parent directory, not just the target; (3) the package version — a stale composer.lock pinned to 1.10.x reproduces the old bug exactly.
  • Cache keys now need at in them. Any cached balances or line lists computed with creditLinesAt() must include the timestamp (and includeEnded) in the key. Same class of bug as a stale container path: a cached "as-of" result silently serves the wrong moment.
  • Extension point. Keep container construction in one helper/factory and as-of reads in one GrantSnapshot-style class. When the next boundary- or history-related patch lands, you'll have a single place to test and adjust instead of a scattering of inline allow() and at: calls.
  • Upgrade risk is low but non-zero. 1.12.0 only adds a column and new query methods; the visible change is that new lines carry a start timestamp. Update internal docs if they describe credit lines as "always dated by creation" or claim users can only see files in their folder — the security posture is unchanged, the observable behaviour is not.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums