PHPackages                             melogail/multipart-field - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. melogail/multipart-field

ActiveLibrary[Admin Panels](/categories/admin)

melogail/multipart-field
========================

A Laravel Nova field.

v1.0.0(3mo ago)0162↓90%MITVuePHP ^8.1

Since Apr 1Pushed 3mo agoCompare

[ Source](https://github.com/melogail/nova-multipart-field)[ Packagist](https://packagist.org/packages/melogail/multipart-field)[ RSS](/packages/melogail-multipart-field/feed)WikiDiscussions main Synced 4w ago

READMEChangelog (1)Dependencies (3)Versions (2)Used By (0)

MultipartField for Laravel Nova
===============================

[](#multipartfield-for-laravel-nova)

A Nova field for **multiple file uploads** on create/update forms. It provides a **drag-and-drop** zone, **multi-select** file input, **fixed-size thumbnails** for previews (with CSS that overrides Nova’s global `img` rules), **per-file remove** before submit, and **full edit support**: existing stored paths are shown on the update form, users can remove individual files, and the model’s path array is updated while **orphan files are deleted from disk** (default storage).

**Package namespace:** `Melogail\MultipartField`
**Vue component key:** `multipart-field`

---

Table of contents
-----------------

[](#table-of-contents)

1. [Requirements](#requirements)
2. [Installation](#installation)
3. [Feature overview](#feature-overview)
4. [Screenshots](#screenshots)
5. [Registering the field in Nova](#registering-the-field-in-nova)
6. [Minimal resource example](#minimal-resource-example)
7. [Database and Eloquent model](#database-and-eloquent-model)
8. [Fluent API reference](#fluent-api-reference)
9. [Editing existing files (update)](#editing-existing-files-update)
10. [Default storage behavior](#default-storage-behavior)
11. [Custom `store()` callback](#custom-store-callback)
12. [Validation](#validation)
13. [How the HTTP request looks](#how-the-http-request-looks)
14. [End-user experience (form UI)](#end-user-experience-form-ui)
15. [Custom field CSS (thumbnails)](#custom-field-css-thumbnails)
16. [Preview URLs (`preview()`)](#preview-urls-preview)
17. [Detail and index views](#detail-and-index-views)
18. [Download and delete](#download-and-delete)
19. [Building frontend assets](#building-frontend-assets)
20. [Troubleshooting](#troubleshooting)
21. [Summary](#summary)

---

Requirements
------------

[](#requirements)

- Laravel Nova **v5** (aligned with the APIs used by this field)
- PHP **8.1+** (package `composer.json` constraint; use the version your Laravel app requires)
- The field is registered as a **local Composer package** from `nova-components/MultipartField` (see your app’s `composer.json` `repositories` and `require` entry)

---

Installation
------------

[](#installation)

From your Laravel application root (paths may match your project):

1. Require the path repository package (if not already in `composer.json`):

    ```
    {
      "repositories": [
        {
          "type": "path",
          "url": "./nova-components/MultipartField"
        }
      ],
      "require": {
        "melogail/multipart-field": "*"
      }
    }
    ```
2. Install dependencies:

    ```
    composer update melogail/multipart-field
    ```
3. The package’s service provider should be **auto-discovered** (`FieldServiceProvider` registers the Nova scripts). If not, register `Melogail\MultipartField\FieldServiceProvider` manually in `bootstrap/providers.php`.
4. **Build the field’s JavaScript and CSS** (required for Nova to load the form UI and thumbnail styles):

    ```
    cd nova-components/MultipartField && npm ci && npm run prod
    ```

    Or use your app’s script if defined, e.g. `npm run build-multipart-field-prod`.

---

Feature overview
----------------

[](#feature-overview)

AreaWhat you get**Create**Drag-and-drop + multi file picker, `accept` filtering, local previews before upload, remove from pending selection.**Update**Lists **saved** files (with server `previewUrls` when `preview()` is set), remove updates the kept list; new files append. Submit merges **kept paths + new uploads** into one array.**Storage (default)**Each upload stored on `disk` under `path`; model attribute = `string[]` of paths. Paths removed on edit are **deleted from the disk** as well as dropped from the array.**Security**Kept paths from the client are **intersected** with the model’s previous paths so arbitrary paths cannot be injected.**Form layout**Saved and new files render as **compact cards** in a **wrapping row** (side by side until wrap). **64×64px** thumbnails (`.multipart-field-thumb` in `field.css`).**Filenames**Long names show **start…end** with extension preserved; full name on hover (`title`).**Spacing**Extra **gap and margin** between file cards.**Index****Shown by default** (`showOnIndex = true`): document icon + **badge count**; tooltip lists up to five basenames.**Detail****Side-by-side** previews (when URLs exist), per-file **View** / **Download**, fallback toolbar download.**Validation**Parent attribute as `array`; per-upload rules on `{attribute}.*`; optional `{attribute}_existing` as JSON string.---

Screenshots
-----------

[](#screenshots)

Examples from a Nova **Documents** resource using `MultipartField` for the **Documents** column.

### Index

[](#index)

On the resource index, the field shows a **document icon** and a **badge** with the file count; hover the cell for a tooltip listing file names (see [Detail and index views](#detail-and-index-views)).

[![MultipartField on a Nova resource index table](docs/images/nova-index.png)](docs/images/nova-index.png)

### Detail

[](#detail)

With **`preview()`** configured, stored files render as **thumbnails** in a row, each with **View** and **Download** links.

[![MultipartField on a Nova resource detail page](docs/images/nova-detail.png)](docs/images/nova-detail.png)

### Create / edit form

[](#create--edit-form)

The **drag-and-drop** zone shows the primary prompt, **accepted extensions** (from `accept()`), and optional **help** text (e.g. per-file size rules via `help()`).

[![MultipartField on a Nova create or edit form](docs/images/nova-create.png)](docs/images/nova-create.png)

---

Registering the field in Nova
-----------------------------

[](#registering-the-field-in-nova)

In your resource’s `fields()` method, import and add the field:

```
use Melogail\MultipartField\MultipartField;

public function fields(NovaRequest $request): array
{
    return [
        // ...

        MultipartField::make('Attachments', 'attachments')
            ->accept('.pdf,.jpg,.jpeg,.png')
            ->disk(config('nova.storage_disk', 'public'))
            ->path('uploads/attachments'),

        // ...
    ];
}
```

- **First argument:** Label shown in Nova.
- **Second argument:** **Request attribute name** (and usually the **model attribute** that receives the stored result). The browser submits new files as `attachments[]`.
- **Index table:** the field is **shown on the index view by default** (`showOnIndex = true`), using `IndexField` (file icon + count). Hide it with `->hideFromIndex()` if you do not want a column.

---

Minimal resource example
------------------------

[](#minimal-resource-example)

```
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Laravel\Nova\Http\Requests\NovaRequest;
use Melogail\MultipartField\MultipartField;

MultipartField::make('Documents', 'document_paths')
    ->accept('.pdf,.jpg,.jpeg,.png,.webp')
    ->disk(config('nova.storage_disk', 's3'))
    ->path('users/documents')
    ->rules('nullable', 'array')
    ->eachFileRules('file', 'max:2048', 'mimes:pdf,jpg,jpeg,png,webp');
```

With only `disk()` and `path()` and **no** custom `->store()`, the field uses the [default storage behavior](#default-storage-behavior) and sets `document_paths` on the model to an **array of stored path strings**.

For **create vs update** rules (e.g. require files only on create), use a `NovaRequest` closure:

```
->rules(function (NovaRequest $request) {
    return $request->isCreateOrAttachRequest()
        ? ['required', 'array', 'min:1']
        : ['nullable', 'array'];
})
```

---

Database and Eloquent model
---------------------------

[](#database-and-eloquent-model)

The default implementation assigns the field attribute to a **PHP array of strings** (storage paths). Persist that in one of these ways:

### Option A: JSON column (recommended)

[](#option-a-json-column-recommended)

Migration:

```
$table->json('document_paths')->nullable();
```

Model:

```
protected $fillable = ['document_paths', /* ... */];

protected function casts(): array
{
    return [
        'document_paths' => 'array',
        // ...
    ];
}
```

### Option B: Custom `store()` only

[](#option-b-custom-store-only)

You do not have to store paths on the same attribute: your `store()` callback can write related models, a single string, etc., and return the attributes Nova should assign (see [Custom `store()` callback](#custom-store-callback)). If you customize storage, you are responsible for reading **`MultipartField::existingPathsRequestKey($attribute)`** (e.g. `document_paths_existing`) and merging kept paths when you need the same edit behavior.

---

Fluent API reference
--------------------

[](#fluent-api-reference)

MethodDescription`make(string $name, ?string $attribute = null, ?string $disk = null, ?callable $storageCallback = null)`Create the field. Optional 3rd/4th constructor args set initial disk and storage callback (same as chaining `disk()` / `store()`). Prefer `MultipartField::make('Label', 'attr')` for clarity.`disk(?string $disk)`**Filesystem disk** name (from `config/filesystems.php`). If omitted, falls back to `config('nova.storage_disk', 'public')`. *(From `Storable` trait.)*`path(string $path)`**Directory** on that disk (passed to `UploadedFile::store()`). Default in trait is `'/'`.`store(callable $callback)`**Custom** persistence. Receives six arguments (see below).`storeAs(callable $callback)`**Custom filename** per file (same arity as Nova’s `File::storeAs`). Used when storing each upload in the default multi-file flow.`storeOriginalName(string $column)`When using **default** storage, also set `$column` to the **first** uploaded file’s client original name.`storeSize(string $column)`When using **default** storage, set `$column` to the **sum** of all uploaded files’ sizes (bytes).`accept(string $accept)`Sets the HTML `accept` attribute (e.g. `'.pdf,.png'` or MIME list). Exposed to Vue as `field.accept`.`preview(callable $callback)`Nova `HasPreview` API: return a **URL string** (or `null`) for the detail/index preview. See [Preview URLs](#preview-urls-preview).`rules(...)`Standard Nova validation (from `Field`). Use `array` on the attribute and `eachFileRules()` for per-file rules.`eachFileRules(...)`Registers validation on `{attribute}.*` (each uploaded file). Pair with `->rules('required', 'array', 'min:1')` on create, or `nullable` on update.**Static helper:**

MethodDescription`MultipartField::existingPathsRequestKey(string $requestAttribute)`Returns `{attribute}_existing` — the form POST key for the JSON array of paths the user chose to keep on update.Other Nova `Field` methods (`hideFromIndex`, `readonly`, `help`, `dependsOn`, etc.) work as usual.

---

Editing existing files (update)
-------------------------------

[](#editing-existing-files-update)

On **update**, the form:

1. Hydrates **saved paths** from `field.value` and shows **preview URLs** from `field.previewUrls` (same order as paths) when you configure `->preview(...)`.
2. Sends **`{attribute}_existing`** as a **JSON-encoded array of strings** — the paths the user still wants after clicking Remove on any card.
3. Sends new picks as **`{attribute}[]`** as on create.

The **default** `store` callback:

- Merges **kept paths** (validated against the model’s previous paths) **then** appends **newly uploaded** paths.
- Computes paths that disappeared from that merged list and **`Storage::disk(...)->delete($path)`** for each.

If the request **does not** include `{attribute}_existing`, only **new** uploads are used as the path list (same as a fresh create) — the Vue field **always** appends `_existing` on submit, so normal Nova forms get correct merge behavior.

---

Default storage behavior
------------------------

[](#default-storage-behavior)

If you **do not** call `->store()` with your own callback, the field:

1. Reads **kept** paths from `{attribute}_existing` when present (intersected with the model’s stored paths).
2. Reads all valid **new** uploads for the request key (e.g. `document_paths` / `document_paths[]`).
3. Stores **each** new file with `UploadedFile::store($this->getStorageDir(), $this->getStorageDisk())`, or `storeAs(...)` if `storeAs()` was configured.
4. Sets **`$this->attribute` =&gt; merged `string[]`**, deletes removed paths from disk, and optionally sets **original name** / **total size** columns if you used `storeOriginalName` / `storeSize` (only from **new** uploads when those are non-empty).

Nova then assigns those keys on the model (same idea as `File` returning an array of columns).

**Fill runs** when there is **at least one new upload** **or** the `{attribute}_existing` key is present — so “only remove files, no new uploads” still persists.

---

Custom `store()` callback
-------------------------

[](#custom-store-callback)

Your callback receives **six** arguments (same order as Laravel Nova’s `File` field):

```
function (
    \Illuminate\Http\Request $request,
    \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent $model,
    string $attribute,           // Nova attribute name (usually same as request key)
    string $requestAttribute,    // Request key for files (e.g. 'document_paths')
    ?string $disk,               // Resolved disk from ->disk() / Nova default
    string $dir                  // Storage directory from ->path() (Storable)
) {
    // ...
}
```

### Reading uploaded files

[](#reading-uploaded-files)

Always normalize to a list of `UploadedFile` instances:

```
use Illuminate\Support\Arr;

$files = array_values(array_filter(
    Arr::wrap($request->file($requestAttribute) ?? [])
));
```

### Reading kept paths on update

[](#reading-kept-paths-on-update)

```
use Melogail\MultipartField\MultipartField;

$key = MultipartField::existingPathsRequestKey($requestAttribute);
$raw = $request->input($key);
$decoded = is_string($raw) ? json_decode($raw, true) : (is_array($raw) ? $raw : []);
// Intersect with $model's previous paths for security, then merge with new stored paths.
```

### Return value (Nova merge rules)

[](#return-value-nova-merge-rules)

Same contract as `File::store()`:

ReturnEffect`true`No model changes from this return path.`Closure`Deferred work (Nova handles like `File`).`string`Sets `$model->{$attribute}` to that string.`array`For each `key => value`, sets `$model->{$key} = $value`.---

Validation
----------

[](#validation)

Uploads are sent as **`{attribute}[]`**, so Laravel sees **`file_paths` as an array** of `UploadedFile` instances. You must **not** put the `file` rule on the main attribute only (that validates the whole array as one file and fails with “must be a file”).

Validate the **array** on the field, and each **element** with `eachFileRules()` (registers `{attribute}.*` for you):

```
MultipartField::make('Files', 'attachments')
    ->rules('required', 'array', 'min:1')
    ->eachFileRules('file', 'max:5120', 'mimes:pdf,jpg,jpeg,png');
```

The field also registers **`{attribute}_existing`** as **`nullable|string`** (JSON body from the form). You may add `json` or custom rules if needed.

Or pass a single array / callable to `eachFileRules`:

```
->eachFileRules(['file', 'max:5120', 'mimes:pdf,jpg,jpeg,png'])

->eachFileRules(fn (NovaRequest $request) => ['file', 'max:10240'])
```

Use `nullable` instead of `required` on the array when uploads are optional (and drop `min:1` or use `min:0` as appropriate). On **update**, use **`nullable|array`** on the upload key if the user may keep existing files without uploading new ones (see minimal example above).

---

How the HTTP request looks
--------------------------

[](#how-the-http-request-looks)

The Vue form:

1. Appends **`{attribute}_existing`** with **`JSON.stringify(string[])`** — paths the user kept (may be `[]`).
2. Appends each **new** selected file under **`{attribute}[]`**.

Examples for attribute `document_paths`:

- `document_paths_existing` — JSON string, e.g. `["uploads/a.pdf","uploads/b.jpg"]`
- `document_paths[]` — repeated multipart file parts

Laravel exposes new files as:

```
$request->file('document_paths'); // array of UploadedFile (or single UploadedFile if one file)
```

---

End-user experience (form UI)
-----------------------------

[](#end-user-experience-form-ui)

- **Saved files (update):** Shown **above** the drop zone as compact cards: **64×64px** thumbnail (or extension chip), **short filename** (middle replaced with `...`, extension kept), “Saved” line, **Remove** (updates kept list only until save).
- **New files:** Same card layout **below** the drop zone; shows **file size** instead of “Saved”.
- **Layout:** Cards use **flex + wrap** with **horizontal gap** and **margin** so multiple files sit **side by side** until the row wraps.
- **Click** the dashed area to open the system file picker (`multiple` enabled).
- **Drag and drop** files onto the zone (highlight on drag-over).
- **Remove** on a card drops that file from the pending selection (new) or from the kept list (existing).
- **Readonly** fields: no add/remove/drop.
- **`accept`:** restricts picker and is shown as helper text when set.
- **Full filename:** Hover the label to see the complete name via the native **`title`** tooltip.

---

Custom field CSS (thumbnails)
-----------------------------

[](#custom-field-css-thumbnails)

Nova’s panel styles often include `img { max-width: 100%; height: auto }`, which breaks small fixed previews. This package ships **`resources/css/field.css`** (built to `dist/css/field.css`) with:

- **`.multipart-field-thumb`** — fixed **4rem × 4rem** box.
- **`.multipart-field-thumb img`** — absolutely positioned, **`object-fit: cover`**, **`max-width` / `max-height: none !important`** so previews stay thumbnail-sized.
- **`.multipart-field-thumb__placeholder`** — centers the extension label for non-images.

Rebuild CSS after edits:

```
cd nova-components/MultipartField && npm run prod
```

---

Preview URLs (`preview()`)
--------------------------

[](#preview-urls-preview)

The field uses Nova’s **`HasPreview`** trait, so you customize the preview the same way as **`File::make()->preview(...)`**.

### Callback signature

[](#callback-signature)

```
preview(function (mixed $value, ?string $disk, mixed $resource): ?string {
    // Return an absolute or relative URL Nova can load in an  / iframe, or null for no preview.
})
```

Arguments match Nova’s preview resolver:

ArgumentMeaning`$value`The field’s resolved value (after `resolve()`). With **default** multipart storage this is usually a **`string[]` of disk paths**; with a custom `store()` it may be a string, array, or something else. For **`previewUrls`**, the callback is invoked **once per path** with that path as the first argument (`string`).`$disk`The field’s storage disk name (`getStorageDisk()`), same as for `File`.`$resource`The underlying model (or resource wrapper Nova passes through). Often unused.Nova serializes:

- **`previewUrl`** — from **`resolvePreviewUrl()`**: your callback receives the **full** field value (e.g. the whole `string[]` of paths). Handy for a single URL derived from the first path (same idea as download).
- **`previewUrls`** — built by calling your **`preview()`** callback **once per stored path**, with that path as the **first argument** (a `string`). The **form** uses this list on update so each saved file can show an image thumb; the **detail** view uses it for multiple previews.

Write your callback so it works when `$value` is either the **full array** (for `previewUrl`) or a **single path string** (for each `previewUrls` entry), e.g. `$path = is_array($value) ? ($value[0] ?? null) : $value;`

### Example: private disk / S3 temporary URL

[](#example-private-disk--s3-temporary-url)

```
use Illuminate\Support\Facades\Storage;

MultipartField::make('Multiple Files', 'file_paths')
    ->disk(config('nova.storage_disk', 's3'))
    ->path('users/documents')
    ->preview(function ($value, $disk) {
        $path = is_array($value) ? ($value[0] ?? null) : $value;

        if (! is_string($path) || $path === '') {
            return null;
        }

        return Storage::disk($disk)->temporaryUrl($path, now()->addMinutes(5));
    });
```

### Example: public `public` disk

[](#example-public-public-disk)

```
->disk('public')
->preview(function ($value, $disk) {
    $path = is_array($value) ? ($value[0] ?? null) : $value;

    return $path ? Storage::disk($disk)->url($path) : null;
});
```

### Default behavior

[](#default-behavior)

The package constructor sets a **no-op** preview (`null` URL) until you chain **`->preview(...)`**. If you do not call `preview()`, Nova will not show image URLs for this field until you add URLs via customization.

### Form vs detail preview

[](#form-vs-detail-preview)

- **Create/update form:** **Local** object URLs for **new** picks; **server** `previewUrls` for **existing** paths when `preview()` is configured.
- **`preview()`:** Used when Nova **displays stored** values (detail, index meta, and **update form** for saved files).

---

Detail and index views
----------------------

[](#detail-and-index-views)

### Detail (`DetailField.vue`)

[](#detail-detailfieldvue)

- When **`previewUrls`** is non-empty: **horizontal flex wrap** of cards (thumbnail up to ~120px, **View** / **Download** per file).
- Images use Nova **`ImageLoader`** when the URL looks like an image extension; otherwise a “File *n*” placeholder with download link.
- Fallback: single **`previewUrl`** / **`thumbnailUrl`**, then raw value, then em dash.
- Optional **toolbar** download when previews are empty but the field is downloadable.

### Index (`IndexField.vue`)

[](#index-indexfieldvue)

- **Document icon** + **badge** with **file count**.
- **Tooltip:** “*N* files” plus up to **five** basenames (paths truncated with “+N more” if needed).
- Respects field **text alignment** (`left` / `center` / `right`).

---

Download and delete
-------------------

[](#download-and-delete)

The field registers Nova **download** and **delete** behaviors similar to `File`:

- **Download:** uses the **first** path in the stored array (or the only string) to stream a download. If there are no paths, responds **404**.
- **Delete:** removes **every** path in the stored array (or a single string path) from the configured disk, then clears the field-related columns (including optional original name / size columns).

If you need different download behavior (e.g. ZIP of all files), replace the download logic via Nova’s field customization patterns or disable download and handle downloads elsewhere.

---

Building frontend assets
------------------------

[](#building-frontend-assets)

After changing `resources/js` or `resources/css` under this package:

```
cd nova-components/MultipartField
npm run dev    # development
npm run prod   # production
```

Commit updated `dist/` files if your deployment does not build Nova tools in CI.

---

Troubleshooting
---------------

[](#troubleshooting)

IssueWhat to checkField shows as empty / broken in NovaRun `npm run prod` in the package; clear browser cache; confirm `FieldServiceProvider` is loaded.Previews huge / not thumbnail-sizedEnsure **`dist/css/field.css`** is loaded (rebuild after `resources/css/field.css` changes). The **`.multipart-field-thumb`** rules override Nova `img` styles.`Attempt to read property on null` on saveEnsure `disk` exists in `config/filesystems.php` and `nova.storage_disk` is valid if you rely on defaults.Validation always failsUse per-file rules via `eachFileRules()`; ensure max size aligns with PHP `upload_max_filesize` / `post_max_size`. On update, avoid `requiredModel not saving pathsAttribute must be `$fillable` (or explicitly allowed). For JSON storage, use a `json` column and `array` cast.Custom `store()` not runningFill runs when there is **at least one upload** **or** `{attribute}_existing` is present. Implement merge + disk cleanup yourself if you replace the default callback.Edit form shows no thumbnails for saved filesConfigure **`->preview(...)`** so Nova serializes **`previewUrls`** for each stored path.---

Summary
-------

[](#summary)

- Add **`MultipartField::make('Label', 'attribute')`** to your Nova resource.
- Point **`disk()`** and **`path()`** at where files should live, or implement **`store()`** for full control (and handle **`existingPathsRequestKey()`** if you need the same edit semantics).
- Default behavior sets **`attribute` =&gt; `string[]`** of stored paths; **merges kept + new on update**; **deletes removed paths from disk**; use a **JSON + array cast** on the model for persistence.
- Validate with **`array`** on the attribute, **`eachFileRules()`** for per-file rules, and consider **different rules for create vs update**.
- Request includes **`{attribute}_existing`** (JSON) and **`{attribute}[]`** (files).
- Use **`preview()`** so **detail**, **index**, and **update form** can show URLs per file (**`previewUrls`**).
- Rebuild **`dist/`** (JS + CSS) whenever Vue or field CSS changes.

For implementation details, see `src/MultipartField.php`, `resources/js/components/FormField.vue`, `DetailField.vue`, `IndexField.vue`, and `resources/css/field.css`.

nova-multipart-field
====================

[](#nova-multipart-field)

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance82

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

91d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7257046?v=4)[Mohamed Elogail](/maintainers/melogail)[@melogail](https://github.com/melogail)

---

Top Contributors

[![melogail](https://avatars.githubusercontent.com/u/7257046?v=4)](https://github.com/melogail "melogail (3 commits)")

---

Tags

laravelnova

### Embed Badge

![Health badge](/badges/melogail-multipart-field/health.svg)

```
[![Health](https://phpackages.com/badges/melogail-multipart-field/health.svg)](https://phpackages.com/packages/melogail-multipart-field)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M342](/packages/psalm-plugin-laravel)[sebastienheyd/boilerplate

Laravel Boilerplate based on AdminLTE 3 with blade components, user management, roles, permissions, logs viewer, ...

29419.5k3](/packages/sebastienheyd-boilerplate)[slowlyo/owl-admin

基于 laravel、amis 开发的后台框架~

61114.7k26](/packages/slowlyo-owl-admin)[takielias/tablar-kit

The Elegance of Tablar Dashboard

423.6k](/packages/takielias-tablar-kit)[a2insights/filament-saas

Filament Saas for A2Insights

171.5k](/packages/a2insights-filament-saas)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
