Download the PHP package madebyclowd/laravel-documentable without Composer

On this page you can find all versions of the php package madebyclowd/laravel-documentable. 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-documentable

Laravel Documentable

Latest Version on Packagist Total Downloads

Customizable, S3-compatible-first document storage for Laravel — content-addressable dedup, composable versioning, multipart uploads, and orphan cleanup, without forcing you to adopt one opinionated storage backend or admin UI.

In short: it's the file-upload/storage system your app needs, already built — so you don't have to reinvent versioning, deduplication, or secure uploads yourself.

New here? Start with this section

If you've never used a package like this before, read this section first — it explains what the package actually does, walks through your very first upload step by step, and defines every piece of jargon you'll see later in this README. Already comfortable with S3/multipart uploads? Skip straight to Features.

What does this package actually do?

Does your app need to let users upload files — invoices, photos, contracts, avatars — and reliably store, retrieve, and manage them afterward? That's what this package handles, so you don't have to write that plumbing yourself:

None of this requires you to already know how S3 or multipart uploads work — the quick start below walks through everything with copy-paste commands, and the Glossary explains every term before you hit it.

5-minute quick start

Follow these five steps in order. By the end you'll have uploaded your first file. Every step is a command or a small code snippet you can copy-paste as-is.

1. Install the package:

2. Run the installer. It asks a few plain-language questions and sets everything up for you — config file, database tables, and (optionally) a starter security file:

Not sure how to answer a question it asks? The first option shown is a safe default for most apps — just press Enter to accept it.

3. Let one of your models hold documents. Pick any model you already have — this example uses Invoice, but any model works the same way:

This adds one line (use Documentable;) to app/Models/Invoice.php for you — no manual editing needed. Prefer to do it yourself? It's just this:

4. Tell the package what kind of file you'll be uploading. Open config/documentable.php, find the 'types' array near the top, and add an entry:

Then save that type into the database (safe to run again any time you change this array):

5. Upload your first file, from any controller action:

Done — $document is saved, $invoice->documents lists it, and you can get a temporary link to view/download it whenever you need one:

What's next? The five steps above happen entirely in PHP code you write (a controller). If your frontend (React, Vue, a mobile app) should talk to this package directly over HTTP instead — including for large files, without routing bytes through your Laravel server — see "Uploading via the shipped HTTP API" further down.

Glossary

Terms used throughout the rest of this README, explained in plain language before you hit them:

Term Plain-English meaning
Disk Laravel's name for "where files are stored" (config/filesystems.php). This package needs an S3-compatible disk — real AWS S3, or a compatible alternative like Cloudflare R2, MinIO, or DigitalOcean Spaces.
DocumentType A named "kind" of upload (e.g. "invoice", "profile-photo") with its own rules — max size, allowed formats, whether old versions are kept.
Documentable "The model a document belongs to" — e.g. an Invoice, a User, a Project. Any Eloquent model becomes documentable by adding the Documentable trait to it.
Dedup (deduplication) If the exact same file is uploaded twice, the package stores the bytes once and links both uploads to that same underlying file — no wasted storage.
Versioning Keeping the history of past uploads for the same "slot" instead of throwing the old file away the moment a new one replaces it.
Presigned URL / direct-PUT upload A short-lived, temporary link that lets the browser send file bytes straight to your S3 bucket — the file never passes through your Laravel app server. Used for smaller files.
Multipart upload The technique used for large files: the file is split into pieces ("parts"), each uploaded separately (often in parallel), then assembled on the bucket. Handles slow or flaky connections far better than one giant upload.
ETag A checksum the storage provider returns for each uploaded part, used to confirm a multipart upload assembled correctly.
Morph type / documentable_type Laravel's internal name for "which kind of model" a document is attached to (e.g. App\Models\Invoice) — you'll see this field in the HTTP request examples below.
Authorizer The piece of code that decides who's allowed to upload, view, or delete a given document. The package ships a permissive default (allows everyone) plus a command to generate a real one — see Security.

Features

Installation

documents:install publishes the config and migrations, offers to run migrations, and walks you through the app-shape, etag_strategy, and DocumentType-catalog choices (writing the answers into your published config instead of leaving unconsidered defaults in place).

Scripted/CI install — pass --shape/--etag-strategy/--types to skip the interactive prompts:

Running --no-interaction without --shape prints a loud warning and falls back to separate-api (middleware => ['api'], no session/auth — $request->user() stays null) rather than silently keeping that default with no indication it happened. --etag-strategy/--types have no comparable security consequence, so they default silently under --no-interaction when omitted.

Manual, fully non-interactive equivalent (no installer at all):

Uploading via the shipped HTTP API

Everything above calls DocumentService directly (your own controller, same request). The package also ships routes under /documents (config('documentable.load_routes'), default true) if you'd rather not write that controller yourself. Both the direct-PUT and multipart flows below assume the client (browser/mobile app) talks to your bucket directly for the actual bytes — your app server only ever handles small JSON requests, never the file body.

Middleware: these routes mount under config('documentable.middleware') (default ['api']) — no session, no auth, $request->user() is null. php artisan documents:install asks whether your app is a session-based monolith (sets ['web', 'auth']) or a separate API (stays ['api'], wire your own token guard in). If $request->user() is unexpectedly null in your authorizer or created_by/deleted_by, this is why — see the Troubleshooting section of this package's Boost skill, or re-run documents:install and pick "monolith".

Listing document types

Read-only catalog of active DocumentTypes — id/code/name/max_size_mb/allowed_mimes/ allows_multiple/requires_versioning, no pagination. Gives the frontend a way to fetch a document_type_id without direct DB access or php artisan documents:list (CLI-only). Soft-deleted (deactivated) types are excluded.

Listing an owner's documents

Returns every latest-per-slot document for that owner, grouped {document_type_id: {document_group_id: document}} (storageFile eager-loaded, same shape as the upload endpoints). canView() is applied per document after the query — a custom authorizer denying some documents excludes just those, not the whole set — so pagination (config('documentable.listing.per_page'), default 50) runs over the filtered result:

Small files — presigned direct PUT (files under multipart.threshold_bytes, default 10MB)

  1. Ask your server for a presigned URL:

  2. Client PUTs the raw file bytes straight to url (with headers if any) — no auth, doesn't touch your app server.

  3. Client tells your server it's done, including the sha256 it computed client-side:

    The server re-downloads and re-hashes the object and compares it to expected_hash; on a size/mime/hash mismatch it deletes the object and returns a validation error — you never end up with an orphaned blob.

Large files — multipart (files at/above multipart.threshold_bytes)

1. Initiate. Creates the multipart session on the bucket plus a DB row scoped to user_id — every later call for this session must supply the same user_id or it's rejected (this is the ownership check, not decorative). If you omit user_id, the controller falls back to $request->user()->getAuthIdentifier().

2. Upload each part directly to the bucket. For every part (1-indexed; S3 requires each part ≥5MB except the last):

Client PUTs that part's bytes to url. Only if etag_strategy = client, capture the ETag response header from that PUT — you'll need it in step 3. Under the default etag_strategy = server-authoritative, don't bother capturing it; the server re-derives everything from the bucket's own ListParts at completion time. Repeat this step for every part.

3. Complete.

parts is only read when etag_strategy = client; omit it under server-authoritative. The server assembles the object, verifies integrity (native-checksum fast path or a full re-hash — transparent either way), and creates the Document. Any failure deletes the assembled object server-side first.

Abort, if the client gives up partway (closed tab, network drop):

If nobody calls abort, documents:clean-orphaned (auto-scheduled) sweeps the session after multipart.session_ttl_hours and aborts it on the bucket too — not just deleting the DB row.

Resuming after a dropped connection. Before re-uploading anything, check what actually landed:

exists: false (still a 200, not an error) means the session is gone — reaped, aborted, or never existed — start a new initiate instead of retrying against it. If it exists, diff your local part-tracking against the bucket's own record before resuming:

Re-upload only the part numbers missing from this list — re-uploading a part that's already there is always safe (last-write-wins across S3/R2/MinIO/Spaces — see the MultipartUploadDriver interface docblock for the exact provider-by-provider guarantee, including one R2 edge case), but diffing first avoids the wasted transfer.

Same two flows without the shipped routes — direct service calls

Frontend integration notes

The HTTP flows above are transport-agnostic, but a few things are the frontend's responsibility — the package can't enforce them from the browser:

Advanced usage

Multiple independently-versioned slots per owner (allows_multiple = true, requires_versioning = true on the type):

Detached upload, reassociated once the real owner exists:

Choosing an etag_strategy: server-authoritative (default) needs no bucket CORS configuration and works everywhere, at the cost of one extra ListParts call per multipart completion. client saves that round trip but requires ExposeHeaders: ["ETag"] on your bucket's CORS policy — only pick it if you control the bucket.

Bucket CORS: both the direct-PUT and multipart part-upload flows have the browser PUT bytes straight to the bucket, cross-origin from your app — this requires the bucket to allow PUT (and, under etag_strategy = client, ExposeHeaders: ["ETag"]) from your app's origin. php artisan documents:configure-bucket-cors {disk} --origin=https://your-app.example.com applies this for you; --verify runs a live presigned-PUT smoke test afterward.

Scoping dedup per tenant instead of the default global-by-hash:

Listening for events:

Artisan commands

Command Purpose
documents:install Interactive installer (publish + configure).
documents:make-authorizer {name=AppDocumentAuthorizer} Scaffold a starter AuthorizesDocumentAccess implementation in app/Documentable.
documents:attach-model {model} Add use Documentable; (and its import) to an existing model.
documents:sync-types [--prune] Upsert config('documentable.types') into document_types.
documents:list Table of registered types with usage counts.
documents:verify [--repair] Detect (and optionally fix) latest_marker/is_latest drift.
documents:clean-orphaned [--hours=N] Reaper — purges expired pending documents, aborts stale multipart sessions. Auto-scheduled.
documents:configure-bucket-lifecycle {disk} [--days=3] Optional bucket-native AbortIncompleteMultipartUpload backstop.
documents:configure-bucket-cors {disk} --origin=... [--verify] Apply/verify the bucket CORS policy direct-PUT and multipart part uploads need.

AI agent context (Laravel Boost)

The package ships resources/boost/guidelines/core.blade.php (upfront package overview) and resources/boost/skills/laravel-documentable/SKILL.md (detailed reference — upload transports, versioning semantics, security checklist, every artisan command). If you have Laravel Boost installed (composer require laravel/boost --dev

Without Boost, read the files directly from vendor/madebyclowd/laravel-documentable/resources/boost/ and copy the skill into whatever your AI tool expects (Claude Code: .claude/skills/laravel-documentable/SKILL.md project-level, or ~/.claude/skills/ for personal use across projects).

Configuration

Full annotated file lives at config/documentable.php. Key sections:

Security

Two things you should do before going to production — the out-of-the-box defaults are intentionally permissive so the package works immediately, not because they're safe to ship as-is:

  1. documentable_type allowlist. documentable_type on every upload/finalize/complete request is resolved via Relation::getMorphedModel(). If your app hasn't called Relation::enforceMorphMap() (or Relation::morphMap()), an unmapped type is rejected by default — set config('documentable.security.allowed_documentable_types') to an explicit array of allowed FQCNs only if you need to accept unmapped types, and prefer a morph map instead where possible.

    Once a morph map is registered, send the alias, not the FQCN, as documentable_type:

    Sending "App\\Models\\User" here is rejected once enforceMorphMap() is active, unless it's also present in allowed_documentable_types.

  2. AuthorizesDocumentAccess. The default is PermissiveDocumentAuthorizer (allows everything). Bind a real implementation via config('documentable.authorization.resolver') before production use — php artisan documents:make-authorizer scaffolds a starting point. Note canUpload() receives a null $documentable from storeDetached()/multipart initiate() (no owner attached yet) — the generated stub branches on this explicitly rather than falling through to an ownership check that can never pass for null.

Neither of these is optional in combination: an allowlisted/morph-mapped type with a permissive authorizer still lets any caller attach/view/delete documents against any instance of that type.

License

The MIT License (MIT). Please see the LICENSE file for more information.


All versions of laravel-documentable with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
illuminate/support Version ^11.0|^12.0|^13.0
illuminate/database Version ^11.0|^12.0|^13.0
illuminate/http Version ^11.0|^12.0|^13.0
illuminate/routing Version ^11.0|^12.0|^13.0
illuminate/auth Version ^11.0|^12.0|^13.0
illuminate/console Version ^11.0|^12.0|^13.0
illuminate/cache Version ^11.0|^12.0|^13.0
league/flysystem-aws-s3-v3 Version ^3.0
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 madebyclowd/laravel-documentable contains the following files

Loading the files please wait ...