PHPackages                             andydefer/laravel-vote - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. andydefer/laravel-vote

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

andydefer/laravel-vote
======================

Polymorphic voting system for Laravel with Positive, Negative, Abstention, and Neutral vote types.

v1.0.0(yesterday)00MITPHPPHP ^8.2

Since Jun 20Pushed yesterdayCompare

[ Source](https://github.com/andydefer/laravel-vote)[ Packagist](https://packagist.org/packages/andydefer/laravel-vote)[ RSS](/packages/andydefer-laravel-vote/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (11)Versions (2)Used By (0)

Laravel Vote
============

[](#laravel-vote)

> Système de vote polymorphique pour applications Laravel

Un package Laravel complet pour gérer des votes (Positif, Négatif, Abstention, Neutre) avec le pattern Repository, des DTOs, des Value Objects et un système de toggle intelligent.

---

📋 Table des matières
--------------------

[](#-table-des-matières)

- [Fonctionnalités](#fonctionnalit%C3%A9s)
- [Prérequis](#pr%C3%A9requis)
- [Installation](#installation)
- [Configuration](#configuration)
- [Utilisation](#utilisation)
    - [Voter](#voter)
    - [Toggle un vote](#toggle-un-vote)
    - [Modifier un vote](#modifier-un-vote)
    - [Supprimer un vote](#supprimer-un-vote)
    - [Vérifier un vote](#v%C3%A9rifier-un-vote)
    - [Compter les votes](#compter-les-votes)
    - [Récupérer les votes](#r%C3%A9cup%C3%A9rer-les-votes)
    - [Statistiques](#statistiques)
- [Types de vote](#types-de-vote)
- [Référence de l'API](#r%C3%A9f%C3%A9rence-de-lapi)
- [Value Objects](#value-objects)
- [Structure de la base de données](#structure-de-la-base-de-donn%C3%A9es)
- [Tests](#tests)
- [Contribuer](#contribuer)
- [Licence](#licence)

---

✨ Fonctionnalités
-----------------

[](#-fonctionnalités)

- ✅ **Double polymorphisme** - Voter sur n'importe quel modèle avec n'importe quel utilisateur
- ✅ **4 types de vote** - Positif, Négatif, Abstention, Neutre
- ✅ **Toggle intelligent** - Changez de vote en un seul appel
- ✅ **Anti-doublon** - Un utilisateur ne peut pas voter deux fois sur le même objet
- ✅ **Support des DTOs** - Objets de transfert de données typés
- ✅ **Value Objects** - DateTime, Métadonnées
- ✅ **Support des métadonnées** - Stockez des données supplémentaires au format JSON
- ✅ **Suppression douce** - Suppression sécurisée avec possibilité de restauration
- ✅ **Filtrage avancé** - Filtrez par type de vote, par auteur, par objet
- ✅ **Statistiques** - Répartition des votes, pourcentages, scores
- ✅ **Tests complets** - Couverture complète des tests d'intégration

---

🚀 Prérequis
-----------

[](#-prérequis)

- PHP 8.2 ou supérieur
- Laravel 12.0, 13.0, 14.0 ou 15.0

---

📦 Installation
--------------

[](#-installation)

Installez le package via Composer :

```
composer require andydefer/laravel-vote
```

### Publier les migrations

[](#publier-les-migrations)

```
php artisan vendor:publish --tag=vote-migrations
```

### Exécuter les migrations

[](#exécuter-les-migrations)

```
php artisan migrate
```

---

⚙️ Configuration
----------------

[](#️-configuration)

Le package est automatiquement découvert par Laravel. Aucune configuration supplémentaire n'est requise.

Si vous devez personnaliser le Service Provider, ajoutez-le manuellement dans `config/app.php` :

```
'providers' => [
    // ...
    AndyDefer\LaravelVote\VoteServiceProvider::class,
],
```

---

📖 Utilisation
-------------

[](#-utilisation)

### Voter

[](#voter)

```
use AndyDefer\LaravelVote\Services\VoteService;
use AndyDefer\LaravelVote\Enums\VoteType;

class CommentController extends Controller
{
    public function vote(VoteService $voteService, Comment $comment)
    {
        $user = auth()->user();

        // Vote positif (👍)
        $vote = $voteService->vote(
            voter: $user,
            votable: $comment,
            type: VoteType::POSITIVE
        );

        // Vote négatif (👎)
        $vote = $voteService->vote(
            voter: $user,
            votable: $comment,
            type: VoteType::NEGATIVE
        );

        // Vote abstention (🤷)
        $vote = $voteService->vote(
            voter: $user,
            votable: $comment,
            type: VoteType::ABSTENTION
        );

        // Vote neutre (😐)
        $vote = $voteService->vote(
            voter: $user,
            votable: $comment,
            type: VoteType::NEUTRAL
        );

        return response()->json([
            'message' => 'Vote enregistré',
            'type' => $vote->getType()->value,
            'emoji' => $vote->getType()->getEmoji()
        ]);
    }
}
```

### Toggle un vote

[](#toggle-un-vote)

La méthode `toggle()` permet de :

- Ajouter un vote s'il n'existe pas
- Supprimer le vote si le même type est utilisé
- Changer de type de vote s'il existe déjà

```
public function toggleVote(VoteService $voteService, Comment $comment)
{
    $user = auth()->user();

    // Toggle un vote positif (ajoute ou supprime)
    $voted = $voteService->toggle($user, $comment, VoteType::POSITIVE);

    // Toggle un vote négatif (ajoute ou supprime)
    $voted = $voteService->toggle($user, $comment, VoteType::NEGATIVE);

    // Changer de type de vote
    // Si l'utilisateur a voté positif, cela changera en négatif
    // Si l'utilisateur a voté négatif, cela changera en positif
    $voted = $voteService->toggle($user, $comment, VoteType::NEGATIVE);

    return response()->json([
        'voted' => $voted,
        'type' => $voted ? VoteType::POSITIVE->value : null,
        'emoji' => $voted ? VoteType::POSITIVE->getEmoji() : null,
    ]);
}
```

### Modifier un vote

[](#modifier-un-vote)

```
public function updateVote(VoteService $voteService, Comment $comment)
{
    $user = auth()->user();

    try {
        $updated = $voteService->updateVote(
            voter: $user,
            votable: $comment,
            type: VoteType::NEUTRAL
        );

        return response()->json([
            'message' => 'Vote mis à jour',
            'vote' => $updated
        ]);
    } catch (RuntimeException $e) {
        return response()->json(['error' => $e->getMessage()], 404);
    }
}
```

### Supprimer un vote

[](#supprimer-un-vote)

```
public function deleteVote(VoteService $voteService, Comment $comment)
{
    $user = auth()->user();

    try {
        $voteService->deleteVote($user, $comment);

        return response()->json([
            'message' => 'Vote supprimé avec succès'
        ]);
    } catch (RuntimeException $e) {
        return response()->json(['error' => $e->getMessage()], 404);
    }
}
```

### Vérifier un vote

[](#vérifier-un-vote)

```
// Vérifier si l'utilisateur a voté
$hasVoted = $voteService->hasVoted($user, $comment);

// Vérifier le type de vote de l'utilisateur
$hasPositive = $voteService->hasVotedType($user, $comment, VoteType::POSITIVE);

// Récupérer le vote spécifique
$vote = $voteService->getVoterVote($user, $comment);

// Obtenir le type de vote de l'utilisateur
$type = $voteService->getVoterVoteType($user, $comment);
```

### Compter les votes

[](#compter-les-votes)

```
// Compter tous les votes
$total = $voteService->countVotes($comment);

// Compter les votes par type
$positive = $voteService->countVotesByType($comment, VoteType::POSITIVE);
$negative = $voteService->countVotesByType($comment, VoteType::NEGATIVE);
$abstentions = $voteService->countVotesByType($comment, VoteType::ABSTENTION);
$neutrals = $voteService->countVotesByType($comment, VoteType::NEUTRAL);
```

### Récupérer les votes

[](#récupérer-les-votes)

```
// Récupérer tous les voteurs d'un objet
$voters = $voteService->getVoters($comment);

// Récupérer les voteurs par type
$positiveVoters = $voteService->getVotersByType($comment, VoteType::POSITIVE);

// Récupérer tous les votes d'un utilisateur
$userVotes = $voteService->getVoterVotes($user);

// Récupérer les votes d'un utilisateur par type
$userPositiveVotes = $voteService->getVoterVotesByType($user, VoteType::POSITIVE);
```

### Statistiques

[](#statistiques)

```
// Score total (positif - négatif)
$score = $voteService->getScore($comment); // 42

// Taux de participation
$participation = $voteService->getParticipationRate($comment); // 75.5%

// Répartition des votes
$distribution = $voteService->getDistribution($comment);
// [
//     'positive' => 42,
//     'negative' => 20,
//     'abstention' => 10,
//     'neutral' => 8,
//     'total' => 80
// ]

// Pourcentage par type
$positivePercentage = $voteService->getPercentage($comment, VoteType::POSITIVE); // 52.5%

// Statistiques complètes
$stats = $voteService->getStats($comment);
// [
//     'positive' => 42,
//     'negative' => 20,
//     'abstention' => 10,
//     'neutral' => 8,
//     'total' => 80,
//     'score' => 22,
//     'participation_rate' => 75.5,
//     'distribution' => [
//         'positive' => 52.5,
//         'negative' => 25.0,
//         'abstention' => 12.5,
//         'neutral' => 10.0
//     ]
// ]
```

---

🏷️ Types de vote
----------------

[](#️-types-de-vote)

TypeValeurEmojiLabelDescription`VoteType::POSITIVE``'positive'`👍PositifVote favorable`VoteType::NEGATIVE``'negative'`👎NégatifVote défavorable`VoteType::ABSTENTION``'abstention'`🤷AbstentionVote neutre sans avis`VoteType::NEUTRAL``'neutral'`😐NeutreVote sans opinion### Utilisation des émojis et labels

[](#utilisation-des-émojis-et-labels)

```
use AndyDefer\LaravelVote\Enums\VoteType;

$type = VoteType::POSITIVE;
echo $type->getEmoji();  // 👍
echo $type->getLabel();  // Positif
echo $type->getColor();  // green

$type = VoteType::NEGATIVE;
echo $type->getEmoji();  // 👎
echo $type->getLabel();  // Négatif
echo $type->getColor();  // red

$type = VoteType::ABSTENTION;
echo $type->getEmoji();  // 🤷
echo $type->getLabel();  // Abstention
echo $type->getColor();  // gray

$type = VoteType::NEUTRAL;
echo $type->getEmoji();  // 😐
echo $type->getLabel();  // Neutre
echo $type->getColor();  // blue
```

---

📚 Référence de l'API
--------------------

[](#-référence-de-lapi)

### VoteService

[](#voteservice)

MéthodeDescriptionRetourne`vote(Model $voter, Model $votable, VoteType $type)`Crée un vote`Model``toggle(Model $voter, Model $votable, VoteType $type)`Toggle un vote (ajoute/change/supprime)`bool``updateVote(Model $voter, Model $votable, VoteType $type)`Modifie un vote existant`Model``deleteVote(Model $voter, Model $votable)`Supprime un vote`void``hasVoted(Model $voter, Model $votable)`Vérifie si l'utilisateur a voté`bool``hasVotedType(Model $voter, Model $votable, VoteType $type)`Vérifie si l'utilisateur a voté pour un type`bool``getVoterVote(Model $voter, Model $votable)`Récupère le vote d'un utilisateur`?Model``getVoterVoteType(Model $voter, Model $votable)`Récupère le type de vote d'un utilisateur`?VoteType``getVotes(Model $votable)`Récupère tous les votes d'un objet`Collection``getVoterVotes(Model $voter)`Récupère les votes d'un utilisateur`Collection``getVoterVotesByType(Model $voter, VoteType $type)`Récupère les votes d'un utilisateur par type`Collection``getVoters(Model $votable)`Récupère tous les voteurs`Collection``getVotersByType(Model $votable, VoteType $type)`Récupère les voteurs par type`Collection``countVotes(Model $votable)`Compte tous les votes`int``countVotesByType(Model $votable, VoteType $type)`Compte les votes par type`int``getScore(Model $votable)`Score total (positif - négatif)`int``getParticipationRate(Model $votable)`Taux de participation`float``getDistribution(Model $votable)`Répartition des votes`array``getPercentage(Model $votable, VoteType $type)`Pourcentage de votes d'un type`float``getStats(Model $votable)`Statistiques complètes`array`---

🎯 Value Objects
---------------

[](#-value-objects)

Le package supporte les Value Objects suivants :

Value ObjectDescriptionExemple`DateTimeVO`Date/heure`DateTimeVO::from('2024-01-01 12:00:00')``StrictDataObject`Métadonnées typées`StrictDataObject::from(['key' => 'value'])`### Accesseurs dans le modèle Vote

[](#accesseurs-dans-le-modèle-vote)

```
$vote = Vote::find(1);

// Accès via les getters
$createdAt = $vote->getCreatedAt();    // DateTimeVO|null
$updatedAt = $vote->getUpdatedAt();    // DateTimeVO|null
$deletedAt = $vote->getDeletedAt();    // DateTimeVO|null
$metadata = $vote->getMetadata();      // StrictDataObject|null
$type = $vote->getType();              // VoteType

// Relations
$voter = $vote->voter;          // Auteur (User, Admin, etc.)
$votable = $vote->votable;      // Objet voté (Comment, Article, etc.)
```

---

📝 Structure de la base de données
---------------------------------

[](#-structure-de-la-base-de-données)

```
CREATE TABLE votes (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    voter_type VARCHAR(255) NOT NULL,      -- Type de l'électeur
    voter_id BIGINT UNSIGNED NOT NULL,     -- ID de l'électeur
    votable_type VARCHAR(255) NOT NULL,    -- Type de l'objet voté
    votable_id BIGINT UNSIGNED NOT NULL,   -- ID de l'objet voté
    type VARCHAR(20) NOT NULL,             -- positive, negative, abstention, neutral
    metadata JSON NULL,                    -- Métadonnées
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    deleted_at TIMESTAMP NULL,

    UNIQUE INDEX idx_unique_vote (voter_type, voter_id, votable_type, votable_id),
    INDEX idx_voter (voter_type, voter_id),
    INDEX idx_votable (votable_type, votable_id),
    INDEX idx_type (type)
);
```

---

🔍 Exemple complet
-----------------

[](#-exemple-complet)

```
use AndyDefer\LaravelVote\Services\VoteService;
use AndyDefer\LaravelVote\Enums\VoteType;
use Illuminate\Http\Request;

class ProposalVoteController extends Controller
{
    public function __construct(
        private readonly VoteService $voteService
    ) {}

    public function store(Request $request, Proposal $proposal)
    {
        $user = $request->user();

        $type = VoteType::tryFrom($request->input('type'));
        if (!$type) {
            return response()->json([
                'error' => 'Type de vote invalide'
            ], 400);
        }

        try {
            $vote = $this->voteService->vote(
                voter: $user,
                votable: $proposal,
                type: $type
            );

            return response()->json([
                'message' => 'Vote enregistré avec succès',
                'vote' => [
                    'type' => $vote->getType()->value,
                    'emoji' => $vote->getType()->getEmoji(),
                    'label' => $vote->getType()->getLabel(),
                ],
                'stats' => $this->voteService->getStats($proposal)
            ], 201);

        } catch (RuntimeException $e) {
            return response()->json([
                'error' => $e->getMessage()
            ], 422);
        }
    }

    public function toggle(Request $request, Proposal $proposal)
    {
        $user = $request->user();
        $type = VoteType::tryFrom($request->input('type', 'positive'));

        if (!$type) {
            return response()->json([
                'error' => 'Type de vote invalide'
            ], 400);
        }

        $voted = $this->voteService->toggle($user, $proposal, $type);

        return response()->json([
            'voted' => $voted,
            'type' => $voted ? $type->value : null,
            'emoji' => $voted ? $type->getEmoji() : null,
            'stats' => $this->voteService->getStats($proposal)
        ]);
    }

    public function show(Proposal $proposal)
    {
        $stats = $this->voteService->getStats($proposal);
        $userVote = null;

        if (auth()->check()) {
            $userVote = $this->voteService->getVoterVoteType(auth()->user(), $proposal);
        }

        return response()->json([
            'proposal' => $proposal->id,
            'title' => $proposal->title,
            'stats' => $stats,
            'user_vote' => $userVote?->value,
            'user_emoji' => $userVote?->getEmoji(),
        ]);
    }

    public function destroy(Proposal $proposal)
    {
        $user = request()->user();

        try {
            $this->voteService->deleteVote($user, $proposal);

            return response()->json([
                'message' => 'Vote supprimé avec succès',
                'stats' => $this->voteService->getStats($proposal)
            ]);

        } catch (RuntimeException $e) {
            return response()->json([
                'error' => $e->getMessage()
            ], 404);
        }
    }

    public function myVotes(Request $request)
    {
        $user = $request->user();
        $votes = $this->voteService->getVoterVotes($user);

        return response()->json([
            'total' => $votes->count(),
            'votes' => $votes->map(function ($vote) {
                return [
                    'id' => $vote->id,
                    'type' => $vote->getType()->value,
                    'emoji' => $vote->getType()->getEmoji(),
                    'votable_type' => $vote->votable_type,
                    'votable_id' => $vote->votable_id,
                    'created_at' => $vote->getCreatedAt()?->format('Y-m-d H:i:s')
                ];
            })
        ]);
    }

    public function stats(Proposal $proposal)
    {
        $stats = $this->voteService->getStats($proposal);
        $score = $this->voteService->getScore($proposal);

        return response()->json([
            'proposal' => $proposal->id,
            'score' => $score,
            'participation_rate' => $stats['participation_rate'],
            'positive' => $stats['positive'],
            'negative' => $stats['negative'],
            'abstention' => $stats['abstention'],
            'neutral' => $stats['neutral'],
            'total' => $stats['total'],
            'distribution' => $stats['distribution']
        ]);
    }

    public function leaderboard()
    {
        $proposals = Proposal::withCount([
            'votes as positive_votes' => function ($query) {
                $query->where('type', 'positive');
            },
            'votes as negative_votes' => function ($query) {
                $query->where('type', 'negative');
            }
        ])->having('positive_votes', '>', 0)
          ->orderByRaw('positive_votes - negative_votes DESC')
          ->limit(10)
          ->get();

        return response()->json($proposals->map(function ($proposal) {
            return [
                'id' => $proposal->id,
                'title' => $proposal->title,
                'score' => $proposal->positive_votes - $proposal->negative_votes,
                'positive_votes' => $proposal->positive_votes,
                'negative_votes' => $proposal->negative_votes
            ];
        }));
    }
}
```

---

🚀 Cas d'utilisation
-------------------

[](#-cas-dutilisation)

### 1. Système de votes pour propositions

[](#1-système-de-votes-pour-propositions)

```
// Afficher les propositions avec leurs votes
public function getProposals()
{
    $proposals = Proposal::with('votes')->get();

    return response()->json($proposals->map(function ($proposal) {
        $stats = $this->voteService->getStats($proposal);

        return [
            'id' => $proposal->id,
            'title' => $proposal->title,
            'score' => $stats['score'],
            'positive' => $stats['positive'],
            'negative' => $stats['negative'],
            'participation' => $stats['participation_rate']
        ];
    }));
}
```

### 2. Système de feedback

[](#2-système-de-feedback)

```
// Feedback sur une fonctionnalité
public function feedback(Feature $feature)
{
    $user = auth()->user();

    $this->voteService->vote($user, $feature, VoteType::POSITIVE);

    // Si assez de votes positifs, prioriser la fonctionnalité
    $stats = $this->voteService->getStats($feature);
    if ($stats['score'] > 50) {
        $feature->update(['priority' => 'high']);
    }

    return response()->json(['message' => 'Feedback enregistré']);
}
```

### 3. Système d'approbation

[](#3-système-dapprobation)

```
// Approbation d'un document
public function approve(Document $document)
{
    $user = auth()->user();

    $this->voteService->vote($user, $document, VoteType::POSITIVE);

    // Si suffisamment d'approbations, valider le document
    $stats = $this->voteService->getStats($document);
    if ($stats['positive'] >= 5 && $stats['positive'] > $stats['negative'] * 2) {
        $document->update(['status' => 'approved']);
    }

    return response()->json(['message' => 'Document approuvé']);
}
```

---

🧪 Tests
-------

[](#-tests)

### Exécuter les tests

[](#exécuter-les-tests)

```
composer test
```

### Exécuter uniquement les tests unitaires

[](#exécuter-uniquement-les-tests-unitaires)

```
composer test-unit
```

### Exécuter uniquement les tests d'intégration

[](#exécuter-uniquement-les-tests-dintégration)

```
composer test-integration
```

---

🔧 Développement
---------------

[](#-développement)

### Style de code

[](#style-de-code)

```
./vendor/bin/pint
```

### Analyse statique

[](#analyse-statique)

```
./vendor/bin/phpstan analyse
./vendor/bin/psalm
```

---

🤝 Contribuer
------------

[](#-contribuer)

Veuillez consulter [CONTRIBUTING](CONTRIBUTING.md) pour plus de détails.

### Flux de développement

[](#flux-de-développement)

1. Forkez le dépôt
2. Créez une branche de fonctionnalité (`git checkout -b feature/amazing-feature`)
3. Apportez vos modifications
4. Exécutez les tests (`composer test`)
5. Committez vos modifications (`git commit -m 'Ajouter une fonctionnalité géniale'`)
6. Poussez vers la branche (`git push origin feature/amazing-feature`)
7. Ouvrez une Pull Request

---

📦 Dépendances
-------------

[](#-dépendances)

- [`andydefer/php-vo`](https://github.com/andydefer/php-vo) - Value Objects
- [`andydefer/laravel-repository`](https://github.com/andydefer/laravel-repository) - Implémentation du pattern Repository
- [`andydefer/domain-structures`](https://github.com/andydefer/domain-structures) - Structures de domaine (AbstractRecord, AbstractData)

---

👨‍💻 Auteur
----------

[](#‍-auteur)

**Andy Kani**

- GitHub: [@andydefer](https://github.com/andydefer)
- Email:

---

📄 Licence
---------

[](#-licence)

Ce package est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus d'informations.

---

⭐ Support
---------

[](#-support)

Si vous trouvez ce package utile, n'hésitez pas à lui donner une ⭐ sur GitHub !

---

🙏 Remerciements
---------------

[](#-remerciements)

- Framework Laravel
- Tous les contributeurs et utilisateurs de ce package

---

**Construit avec ❤️ pour la communauté Laravel**

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

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

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2170ec3fbad9eb4b002661ab4f58b1cc374eae4293b92904c6a74bc2818bd570?d=identicon)[andydefer](/maintainers/andydefer)

---

Top Contributors

[![andydefer](https://avatars.githubusercontent.com/u/124321745?v=4)](https://github.com/andydefer "andydefer (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/andydefer-laravel-vote/health.svg)

```
[![Health](https://phpackages.com/badges/andydefer-laravel-vote/health.svg)](https://phpackages.com/packages/andydefer-laravel-vote)
```

###  Alternatives

[markwalet/nova-modal-response

A Laravel Nova asset for Modal responses on an action.

17818.7k](/packages/markwalet-nova-modal-response)[crumbls/layup

A visual page builder plugin for Filament 5 — Divi-style grid layouts with extensible widgets.

591.7k1](/packages/crumbls-layup)[tomshaw/electricgrid

A feature-rich Livewire package designed for projects that require dynamic, interactive data tables.

119.2k](/packages/tomshaw-electricgrid)

PHPackages © 2026

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