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

Isbn Laravel Package

fale/isbn

A PHP package for working with ISBNs. Validate ISBN-10 and ISBN-13 numbers, convert between formats, produce hyphenated or compact output, and check registration group ranges. Simple, framework-agnostic API with optional Laravel integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and confirm what actually got vendored — the package's docs are thin and predate modern PHP, so treat the source as the spec:

composer require fale/isbn

If Composer refuses on PHP 8.x due to the package's php constraint, check vendor/fale/isbn/composer.json first. A fork pinned in repositories is usually cleaner than --ignore-platform-reqs, because you will likely need to patch the constraint anyway.

Where to look first:

  • vendor/fale/isbn/src/Isbn/Isbn.php — the entire public API (validate, convert, getFormat, getComponent, getComponents).
  • vendor/fale/isbn/data/RangeMessage.xml (or similar) — the ISBN range table used to split group/publisher/title. This file's age is the main correctness risk.
  • The RangeMessage classes — if present, they let you inject your own XML instead of relying on the bundled snapshot.

Minimal first use case — validate and canonicalize an ISBN at the boundary of your app:

use Isbn\Isbn;

$isbn = new Isbn();

$raw = '978-3-16-148410-0';

if ($isbn->validate($raw)) {
    $canonical = preg_replace('/[^0-9X]/', '', $isbn->convert($raw, Isbn::ISBN_13));
    // "9783161484100" — store this
}

In Laravel, resolve it from the container rather than new-ing it in controllers:

$this->app->singleton(Isbn::class, fn () => new Isbn());

That's enough to start. Everything below is about not regretting it.

Implementation Patterns

Wrap it in one application service. Never call Isbn directly from controllers, models, or jobs. A single App\Services\IsbnService gives you a seam to swap the library later (you probably will — see the license section) and one place to handle exceptions and formatting quirks.

final class IsbnService
{
    public function __construct(private Isbn $isbn) {}

    public function normalize(?string $raw): ?string
    {
        if (blank($raw)) {
            return null;
        }

        try {
            if (! $this->isbn->validate($raw)) {
                return null;
            }

            return preg_replace('/[^0-9X]/', '', $this->isbn->convert($raw, Isbn::ISBN_13));
        } catch (\Throwable) {
            return null;
        }
    }

    public function format(string $canonical): string
    {
        return $this->isbn->convert($canonical, Isbn::ISBN_13);
    }
}

Custom validation rule. This is the highest-leverage integration for any catalogue/bookstore app:

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Isbn\Isbn;

class IsbnRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        try {
            $valid = is_string($value) && app(Isbn::class)->validate($value);
        } catch (\Throwable) {
            $valid = false;
        }

        if (! $valid) {
            $fail('The :attribute must be a valid ISBN-10 or ISBN-13.');
        }
    }
}

Use it in a FormRequest, and normalize in prepareForValidation so the rest of the request sees canonical data:

protected function prepareForValidation(): void
{
    $this->merge([
        'isbn' => app(IsbnService::class)->normalize($this->input('isbn')),
    ]);
}

public function rules(): array
{
    return ['isbn' => ['required', new IsbnRule(), 'unique:books,isbn']];
}

Normalize on write, hyphenate on read. Store 9783161484100 (digits only, ISBN-13) with a unique index. On display, run the stored value back through the library. This makes matching, deduplication, and imports trivial and keeps the check digit as the single source of truth.

Backfill/migration pattern. For legacy ISBN-10 data, a queued job that reads a chunk, converts to ISBN-13, and writes back is safer than an in-place SQL update — ISBN-10 check digits can be X, and the conversion has edge cases. Keep the original column until the migration is verified.

Import pipelines. CSV imports are where this library earns its keep: validate, normalize, reject with row numbers, and store the canonical form. Because validate() and convert() are cheap, validate every row before touching the database, then bulk insert.

Testing. Write a factory/helper that produces valid ISBNs and a fixture set covering: valid ISBN-10, valid ISBN-13, hyphenated input, input with spaces, lowercase x check digit, invalid check digit, and the 979 prefix.

Gotchas and Tips

The license is the headline risk. AGPL-3.0 is strong copyleft with a network clause. If you expose a Laravel SaaS that uses this library, you may be obligated to release the corresponding source. For anything commercial, get legal review before adopting, or write the validator yourself — ISBN validation is a checksum plus length check, roughly 30 lines, and you can bundle a range table from isbn-international.org under your own terms.

It is effectively unmaintained. Last release in 2015 means no PHP 8 compatibility guarantees, no security patches, and no fixes for new registration ranges. Pin the exact version ("fale/isbn": "3.*") rather than using a caret range that could resolve unpredictably, and vendor-fork if you need to patch the PHP constraint.

Stale RangeMessage.xml. ISBN registration group and publisher ranges change over time (new 979 ranges, new publisher prefixes). Validation (check digit) will still be correct, but getComponent/getComponents can misattribute group or publisher for newer ISBNs. If the package exposes a RangeMessage implementation, inject your own periodically refreshed XML file from the ISBN International agency; otherwise treat components as advisory only and don't build business logic (pricing, routing) on them.

Sanitize before you validate. Users paste ISBNs with hyphens, spaces, and lowercase x. Strip non-[0-9Xx] characters and uppercase the X before calling the library, or you'll get false negatives and chase phantom bugs. Do this once in IsbnService, not at every call site.

979 has no ISBN-10. convert($isbn, Isbn::ISBN_10) on a 979-prefixed ISBN-13 cannot produce a valid result. Guard conversion with a prefix check, or always convert to ISBN-13 and never the other way except for display of legacy data.

Bool vs exception. Depending on version and input (non-string, empty string, extremely malformed values), the library may throw \InvalidArgumentException or similar instead of returning false. Wrap calls in try/catch and treat both outcomes as "invalid" at the boundary. Never let a validation rule throw a 500.

Don't fetch remote range data per request. If you're using a Remote range message implementation, it does network I/O. Cache the XML to disk (or a scheduled job refreshes it) and inject the local file — a per-request HTTP call to isbn-international.org will be the slowest thing in your app.

Formatting output is not guaranteed stable. Whether convert() returns a hyphenated or unformatted string depends on version. Always run the result through preg_replace('/[^0-9X]/', '', ...) before storing, and snapshot-test your formatter so a library update can't silently change displayed ISBNs.

Extension point: the RangeMessage seam. If available, this is the one place worth customizing — a service-provider binding that points at storage/app/isbn/RangeMessage.xml lets you refresh range data without touching vendor code or waiting for an abandoned package to release.

Keep the abstraction, not the dependency. Given the license and maintenance profile, the realistic long-term plan is to replace fale/isbn with an in-house validator behind IsbnService. Design that interface now (normalize, format, components) so the swap is a one-file change.

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.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics