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

Nova Imagic Laravel Package

ayvazyan10/nova-imagic

A Laravel Nova image field for uploading and manipulating images directly in the admin panel. Nova Imagic streamlines image management in Nova resources with an intuitive editing interface and flexible field configuration, making quick, consistent image adjustments easy without a separate media tool...

View on GitHub
Deep Wiki
Context7

Getting Started

1. Verify your stack before touching Composer. Imagic 2.0's supported baseline is PHP 8.2+, Laravel 12, Nova 5, and Intervention Image 3.11+. The wider PHP 8.1+/Laravel 9–11/Nova 4–5 matrix exists only as an EOL migration path — don't plan new work against it.

2. Install and create the catalog. The new media catalog table is not optional; fields will fail without it.

composer require ayvazyan10/nova-imagic:^2.0 --with-all-dependencies
php artisan migrate

If the upgrade guide calls for it, publish the config so disk, visibility, and limits are explicit in your repo rather than package defaults:

php artisan vendor:publish --tag=imagic-config

3. First use case — swap an existing Nova image field. Start with the least risky field (a single avatar or hero image) before converting repeaters or multi-image fields:

use Ayvazyan10\Imagic\Imagic;

Imagic::make('Avatar')
    ->disk('public')
    ->visibility('public')   // 2.0 is private by default
    ->crop(1, 1)              // ratio, enforced before upload
    ->maxSize(2048);          // KB, per-field override

4. Look first at: the upgrade guide (UPGRADE.md) and security policy (SECURITY.md) in the v2.0.0 tag, then the config file after publishing. The upgrade guide is the authoritative source for field-level API changes between 1.x and 2.0 — the release notes only summarize them.

5. Sanity check the pipeline. Upload one file through the Nova UI, confirm the DB row in the media catalog, confirm the object landed on the configured disk with a randomized name plus thumbnail, and confirm the field value persisted as media:<uuid>. That single loop exercises storage, catalog, and reference resolution at once.

Implementation Patterns

Single vs. multiple fields. Use the single field for avatars/logos and the multiple field wherever ordering matters. Reordering is keyboard- and drag-driven in 2.0, and order is persisted — so multiple fields are now safe for galleries and carousels, not just "a bag of images."

Media library as a reuse layer. The biggest day-to-day win: give editors a multiple field backed by the library and let them select previously uploaded assets instead of re-uploading. This pays off for sites with shared brand assets, author headshots, or any image reused across many resources. Pair it with folders and per-user ownership so editors only see their own uploads unless the workflow requires sharing.

Visibility strategy. 2.0 defaults to private. Pick one of two coherent approaches and document it:

  • Public-by-default — set visibility in the published config, and everything behaves like a classic image field. Simplest, but every asset is world-readable by URL.
  • Private-by-default — keep the default, and only add ->visibility('public') on fields that genuinely need a stable public URL (marketing pages, OG images, RSS).

Mixing both in one app is legitimate, but write it down or someone will "fix" a broken public image by flipping the global config.

Rendering front-end values. The stored value is a media:<uuid> reference, not a URL. That indirection is the point — private disk paths never leak into HTML — but it means front-end code needs a resolution step. Centralize it (a model accessor, a Blade component, or a small helper) rather than resolving ad hoc in views:

// app/Models/Post.php
public function coverUrl(): ?string
{
    return $this->cover?->toUrl() ?? $this->legacy_cover_url;
}

Keep one place that understands both reference formats so legacy rows and new rows render identically.

Storage configuration for S3 / S3-compatible. Point the field at a Laravel disk and let Storage do the work; the package handles randomized object names, originals + thumbnails, and rolls back the batch if a write fails. For MinIO, R2, DO Spaces, or similar, set endpoint, use_path_style_endpoint, and url on the disk, and verify both put and delete permissions — a read-only key uploads fine and fails silently on cleanup.

Validation as a policy, not a per-field chore. Set strict MIME, extension, byte-size, dimension, pixel-count, and batch limits centrally in config, then narrow per field only where the domain demands it (e.g. a 1:1 avatar caps at 2 MB, hero images allow 8 MB). Defaults that look generous to a developer often read as "too strict" to a marketer with a phone screenshot — check the real pixel-count ceiling against modern phone cameras before shipping.

Testing. Nova field tests are easiest with Storage::fake() and a fake disk in config. Assert on the catalog table and the persisted reference string, not on URLs — URLs are disk- and visibility-dependent and make brittle tests.

Gotchas and Tips

The migration is a hard dependency, and deployment order matters. Run php artisan migrate before the new code serves traffic. On a rolling deploy, old pods writing legacy values will still work (legacy reads are supported), but new pods without the catalog table will error on upload. Deploy migration first, then code.

media:<uuid> will break naive templates. Any Blade view, API resource, or sitemap doing asset($post->cover) or Storage::url($x) on a 2.0 value produces garbage. Grep the codebase for every consumer of the field before upgrading — controllers, API resources, mail templates, OG tags, PDF generators, and search indexes included. This is the single most common post-upgrade breakage.

Legacy values are readable but not imported. Existing URL/path and JSON values keep working, but nothing is backfilled into the catalog. That means you're running a permanently mixed-format system unless you write an importer. Decide explicitly: (a) leave legacy rows alone and keep the fallback branch in your renderer forever, or (b) script a one-off import that downloads each legacy URL, stores it, and rewrites the column to media:<uuid>. Option (b) is worth it for anything with real editorial volume — mixed formats become a debugging tax.

Private by default is a silent regression for existing public images. After upgrading, previously public assets can start returning 403 from the new authenticated, owner-scoped routes. Symptom: images render in Nova but break on the public site. Fix by explicit ->visibility('public') on the affected fields plus a storage-side visibility update for already-stored objects — the field setting alone won't retroactively make old objects public.

Do not disable Composer's security blocking to install this. On EOL Laravel/Nova lines, active framework advisories can block resolution. The release notes are explicit: the CI-only legacy override exists to detect Imagic regressions, not as a production path. If Composer refuses, upgrade the application stack — a Nova image field is not worth disabling advisory blocking on a production app.

Intervention Image 3 is a real API change. If you extended the package or wrote a custom save/transform callback against Intervention 2, expect to rewrite it: the v3 manager is drive-based (ImageManager::read() / ->save()) rather than static Image::make(). The 2.7 path is a transition crutch, not a destination. Extension points to audit: custom field callbacks, transform pipelines, any code touching thumbnails directly.

Rate-limited, authenticated routes fail in confusing ways. The media routes require a Nova-authenticated session and are rate-limited. Common false alarms: uploads failing for users behind a misconfigured reverse proxy (untrusted proxies → wrong client IP → shared rate-limit bucket), CDN caching of authenticated responses, and long editorial sessions tripping the limiter mid-batch. Check TrustProxies and cache headers before assuming the field is broken.

Debugging checklist, in order: (1) does the catalog row exist? (2) does the object exist on the configured disk at the expected path? (3) does the disk's url config match what the browser requests? (4) do the route/auth/rate-limit constraints hold for this user? (5) is the stored value a media: reference or a legacy URL? Step 5 catches a surprising share of "the image disappeared" reports.

Bulk deletion is guarded, but guardrails are not backups. Per-user, owner-scoped deletion limits blast radius, but an editor cleaning up a folder can still remove something in active use. Snapshot the catalog table (or enable bucket versioning) before enabling bulk operations for a large editorial team.

Upgrade in one PR, not incrementally. Because migrations, value format, visibility defaults, and Intervention version all change together, trying to stage this across multiple deploys is harder than doing it at once behind a short maintenance window. Run the full test suite against a production-shaped S3 disk (not just Storage::fake) before the cutover — S3-compatible quirks around object naming and permissions won't surface locally.

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.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator