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

Grabzit Bundle Laravel Package

tlamedia/grabzit-bundle

Symfony bundle integrating the GrabzIt screenshot and PDF API. Capture website screenshots, generate PDFs, and create other media from your PHP application through configurable services and simple dependency injection.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: tlamedia/grabzit-bundle is a Symfony bundle, not a Laravel package. It exists to (a) register the GrabzItClient service in Symfony's container and (b) wire your API key/secret from config. Underneath it is just grabzit/grabzit — the official PHP SDK. In a Laravel app the bundle's auto-wiring, config/packages/*.yaml, and bundles.php registration do not apply, so the practical path is to depend on the SDK directly and reproduce the (tiny) wiring yourself:

composer require grabzit/grabzit

Then in a service provider:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(\GrabzItClient::class, fn () => new \GrabzItClient(
        config('services.grabzit.key'),
        config('services.grabzit.secret')
    ));
}

Add GRABZIT_KEY / GRABZIT_SECRET to .env and config/services.php. Grab both from the GrabzIt dashboard (Account → API). Note that GrabzIt is a paid, credit-metered external API — you cannot test it without a real key.

First use case to validate end-to-end: capture a screenshot of a URL and save it to disk.

$grabzIt = app(\GrabzItClient::class);
$grabzIt->URLToImage('https://example.com');
$grabzIt->SaveTo(storage_path('app/shots/example.png'));

If that file exists and is a valid PNG, your wiring is correct. A zero-byte file or null return almost always means a bad key/secret or exhausted credits — check the dashboard before debugging code.

If you are genuinely in a Symfony app: composer require tlamedia/grabzit-bundle, enable TlaMedia\GrabzitBundle\GrabzitBundle in config/bundles.php, then set tlamedia_grabzit.key and tlamedia_grabzit.secret in config/packages/tlamedia_grabzit.yaml and inject GrabzItClient normally.

Implementation Patterns

Synchronous (blocking) capture — fine for CLI/queue jobs, never in a request. SaveTo($path) blocks until GrabzIt finishes, which can be several seconds. Use it inside a queued job, not a controller action:

class CaptureScreenshot implements ShouldQueue
{
    public function __construct(public string $url, public string $path) {}

    public function handle(\GrabzItClient $grabzIt): void
    {
        $grabzIt->URLToImage($this->url, ['width' => 1280, 'height' => 800]);
        $grabzIt->SaveTo(storage_path("app/{$this->path}"));
    }
}

Asynchronous (callback) capture — the pattern GrabzIt is actually designed around. Save($callbackUrl) fires the request and returns immediately; GrabzIt later POSTs the result back to your route. This is the only pattern that scales to user-facing features:

// dispatch
$grabzIt->URLToImage($url);
$grabzIt->Save(route('grabzit.callback', ['capture' => $capture->id]));

// routes/api.php — must be publicly reachable
Route::post('/grabzit/callback/{capture}', GrabzItCallbackController::class)
    ->withoutMiddleware(['csrf']);

In the callback controller, read GrabzIt's id and customId off the request, then re-hydrate a client and pull the result:

$grabzIt = app(\GrabzItClient::class);
$grabzIt->GetResult($request->input('id')); // writes to configured destination

Persist the GrabzIt id (and your own customId correlating to a model) at dispatch time so the callback can join back.

HTML/source capture variants. The same client exposes HTMLToPDF($html, $options), HTMLToImage, URLToPDF, URLToTable, and FileToPDF. Treat them uniformly: configure → Save() (async) or SaveTo() (sync). The configuration call and the save call are two separate steps — a common source of "nothing happened" bugs.

Laravel integration tips:

  • Wrap the client in a small CaptureService rather than injecting \GrabzItClient all over — the fluent configure-then-save API is stateful on the client, so sharing a singleton across concurrent calls in one request is dangerous. Either resolve a fresh instance per capture (app()->make(...) with bind not singleton) or serialize.
  • Store results on Storage::disk('s3') / local disk rather than in DB blobs. GrabzIt can save to S3 directly via SaveToCloud, but a local SaveTo + Storage::put gives you more control and easier retries.
  • Fire an event (ScreenshotReady) from the callback controller so downstream logic (OCR, thumbnailing, notifications) is decoupled.

Gotchas and Tips

  • This bundle is nearly unmaintained (3 stars, 0 dependents, last release Feb 2023). For a Laravel codebase, do not adopt it — install grabzit/grabzit directly. The bundle adds ~two files of config glue and a hard Symfony dependency you don't want.
  • Save() vs SaveTo() is the classic footgun. Save($url) triggers an async job and returns null; SaveTo($file) blocks and writes. Passing a filesystem path to Save() silently does nothing useful, and passing a URL to SaveTo() won't schedule a callback. Match the method to the intent.
  • Configure-then-act, not two calls. $grabzIt->URLToImage($url) only sets up the capture; it makes no network request. The network call is Save/SaveTo. Call them in the same scope; don't stash the configured client in a service property between requests.
  • No CSRF on the callback route. GrabzIt POSTs from its servers. Exclude the route from VerifyCsrfToken (Laravel 10+) and make it reachable from the public internet — localhost dev will not receive callbacks. Use ngrok/Expose while developing.
  • Credits and rate limits are the real failure mode. Errors often surface as empty files or null rather than exceptions. On any unexpected result, first check the GrabzIt dashboard's request log — it shows status (processing, failed, successful) per capture ID, which is faster than instrumenting your code.
  • No first-class error type. The SDK is thin and mostly returns booleans/void; wrap every SaveTo in a try/catch, verify file_exists and filesize > 0, and throw your own exception so queued jobs actually retry. Consider a failed() handler that marks the capture record as errored.
  • Options are passed as an array with magic keys ('width', 'height', 'format', 'delay', 'country', 'customId'). They are not validated client-side — a typo silently falls back to defaults. Keep a constants class or a typed DTO per capture type rather than scattering string keys.
  • customId is your only safe correlation on callbacks; GrabzIt's own id is opaque. Set it at dispatch and read it back — it survives the async round-trip.
  • PHP version drift. If you inherit a Symfony app using this bundle, pin PHP/composer constraints in CI; a 2023 bundle can lag behind PHP 8.3+ typed-property and deprecation changes in the SDK's dependencies.
  • Extension point: if you must support both frameworks from one codebase, keep a single CaptureService interface with a Laravel binding and a Symfony service alias, and let both resolve the same GrabzItClient — the bundle is just a service registration, replaceable in either direction.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package