Download the PHP package byrcsc/laravel-hold without Composer

On this page you can find all versions of the php package byrcsc/laravel-hold. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-hold

Laravel Hold

Latest Version on Packagist GitHub Tests Action Status GitHub PHPStan Action Status Total Downloads

Temporary resource holds for Eloquent models: any model can be held, any model can hold, with capacity-aware atomic acquisition, expiring or indefinite claims, and lifecycle events.

The package provides the hold engine. Your application keeps ownership of its UI, users, checkout flow, and what being held actually means.

Laravel Tested PHP versions
12.x 8.3, 8.4
13.x 8.3, 8.4

Installation

Install the package and publish its migration:

Publish the configuration before the migration when you need a custom table name or non-integer model keys:

The published config/hold.php has three keys:

Key Default What it decides
table holds The table the Hold model and the migration both read
holdable_key_type int The key type of the resource side
holder_key_type int The key type of the holder side, and of released_by

Set HOLD_HOLDABLE_KEY_TYPE and HOLD_HOLDER_KEY_TYPE to uuid, ulid, or string when the models on either side of a hold do not use integer keys. Both shape the identity columns, so set them before you migrate.

What a hold is

A hold is a temporary claim by one holder (any model: a user, a cart, a session) on one holdable (any model: a seat, a domain name, a rental unit). The rules are small and strict:

The package controls hold state and nothing else. It does not decide what a held resource means for your application: it does not hide it, price it, reserve payment for it, or queue the next person in line. Your application reads hold state and decides.

Quick start

Add Holdable to the model that can be held:

Acquire, extend, and release a hold:

That is the whole core loop. Everything below is detail.

Capacity

Capacity is a fact about the resource, declared on the model:

The default implementation returns 1. Capacity is deliberately not an argument to acquireHold(): every concurrent acquirer must agree on the same limit for atomic acquisition to mean anything.

Acquiring

acquireHold() returns the new Hold, or null when no slot is free:

acquireHoldOrFail() does the same but throws ByRcsc\LaravelHold\Exceptions\NoAvailableSlotsException instead of returning null. The exception carries the resource it refused as $exception->holdable.

Both parameters are optional. Omit expiresAt for an indefinite hold; omit metadata when you have no context to attach.

acquireHold() is always an attempt to create a new hold. It never silently returns an existing one, and the same holder may hold the same resource more than once when capacity allows (that is how you hold three seats). For double-submit safety, check first:

Acquisition takes a row lock on the holdable and counts inside a transaction, so concurrent acquirers are serialized per resource, and never against unrelated ones. SQLite has no row locks and serializes writers itself, which is correct but can surface a "database is locked" error instead of a clean null. Concurrency and databases covers the connection settings that avoid it.

Expiration

Expiry is lazy. Every check the package performs, availableSlots(), isFullyHeld(), the active() scope, the acquisition count, reads the clock, never a stored status. An expired hold stops blocking its slot at the moment expires_at passes, whether or not any command has run.

The hold:expire command exists only to tell you about it. It stamps holds that have passed their expiry and fires a HoldExpired event for each, exactly once, including across overlapping runs. A hold released after its expiry passed but before the command reached it is left alone: the release is what happened to it. Schedule it if you listen for that event:

If you do not listen for HoldExpired, you do not need the scheduler at all.

Note that HoldExpired fires when the command notices, not at the expiry instant. A hold can expire and its slot be re-acquired by someone else before the event fires. Availability is exact; the event is best effort.

Extending and releasing

extend() pushes expires_at further out from its current value, not from now:

It throws ByRcsc\LaravelHold\Exceptions\CannotExtendHoldException when the hold is released, already expired, or indefinite. The message says which of the three it was, and $exception->hold carries the hold itself. An expired hold cannot be revived by extension: the slot may already belong to someone else. Re-acquire instead.

Releasing stamps released_at, optionally records who released it (released_by, polymorphic, any model), and merges any metadata you pass into the hold's metadata. Released holds stay in the table as history until you prune them.

Releasing twice is a no-op. The first stamp, the first releaser, and the one HoldReleased event all stand, so a double-submitted cancel button cannot rewrite who released a hold or when. Releasing and extending covers the merge depth and the expired-then-released case.

Reading holds

Add HasHolds to holder models to read from the other direction:

A model can be both. Each trait names its relations holds and activeHolds for its own side of the table, so composing them needs one resolution block telling PHP which side keeps the plain names, after which holdsAsHolder and activeHoldsAsHolder reach the other side. Holdables and holders carries that block verbatim.

The whole read surface:

Member Returns
$seat->holdCapacity() int, default 1
$seat->availableSlots() int
$seat->isFullyHeld() bool
$seat->activeHoldFor($user) ?Hold, the newest active hold by that holder
$seat->holds, $user->holds every hold, including released and expired
$seat->activeHolds, $user->activeHolds currently blocking holds only
Hold::query()->active() scope, with released() and expired() alongside
$hold->status HoldStatus::Active, Released, or Expired
$hold->isActive() bool, with isReleased(), isExpired(), isIndefinite()
$hold->metadata ?array
$hold->holdable the resource being held
$hold->holder whoever holds it
$hold->releasedBy whoever released it, or null

Status is computed from the timestamps at read time. There is no stored status column to drift out of date between scheduler runs.

Events

Event Fires
HoldAcquired After a hold is acquired
HoldExtended After extend() succeeds
HoldReleased After release()
HoldExpired From hold:expire, once per expired hold

Each event exposes the Hold as a public readonly property. Events are the extension point for anything user-facing. Holders are polymorphic and may not be notifiable, so the package ships no notifications; a listener reads $event->hold->holder and decides who to tell. Events and listeners has a worked listener.

Pruning

Released and expired holds accumulate as history. Delete old ones on your schedule:

This removes holds that were released, or passed their expiry, more than the given number of days ago. Active and indefinite holds are never pruned.

--days must be a whole number, zero or more; anything else exits non-zero without deleting a row.

Documentation

Full documentation is at docs.rcsc.dev/laravel-hold.

Out of scope

These are decisions, not omissions. The default answer to a feature request in this list is this section:

Versioning

The package follows semantic versioning.

Bug fixes go into the newest version only. To get a fix, upgrade to it.

Questions and issues

This package is maintained by one person, so replies can take a while. Everything gets read.

License

MIT. See CHANGELOG.md.


All versions of laravel-hold with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
illuminate/console Version ^12.0||^13.0
illuminate/contracts Version ^12.0||^13.0
illuminate/database Version ^12.0||^13.0
illuminate/events Version ^12.0||^13.0
illuminate/support Version ^12.0||^13.0
spatie/laravel-package-tools Version ^1.16
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package byrcsc/laravel-hold contains the following files

Loading the files please wait ...