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

Shaghool Laravel Package

zareismail/shaghool

Shaghool is a small Laravel package by zareismail that adds job-related functionality to Laravel applications, following framework conventions and Eloquent patterns for quick setup. It has minimal community adoption and limited documentation, so evaluate it carefully before production use.

View on GitHub
Deep Wiki
Context7

Getting Started

This package adds a polymorphic "reporting" layer to Eloquent models: a Report model plus a trait you attach to anything that can be reported (posts, comments, users). Release 1.4.1 is a correctness release — it wires up the model boot, adds SoftDeletes to the report model, and makes report rows follow the reported model through delete and restore. If you are starting fresh, you get that behaviour for free; if you are upgrading, see the Gotchas section before you deploy.

Minimal path:

composer require <vendor>/<package>
php artisan vendor:publish --provider="<Vendor>\<Package>\<Package>ServiceProvider"
php artisan migrate

Then confirm two things before writing any code:

  1. The migration — the reports table should now include a nullable deleted_at. If your published migration predates 1.4.1, re-publish or add the column manually.
  2. The trait name and contract — open the package's src/ and read the trait and the Report model. Method names (report(), reports(), isReportedBy(), scopes) are the API surface you will live in day to day; the README is usually accurate but the trait is the source of truth.

First use case — the canonical one:

use Vendor\Package\Concerns\Reportable;

class Post extends Model
{
    use Reportable;
}

// Somewhere in a controller
$post->report(auth()->user(), 'Spam link in body');
// Moderation queue
Report::with('reportable', 'reporter')->latest()->paginate(50);

Get that loop working end-to-end (report → list → resolve) before layering on notifications or auto-moderation. It exercises the trait, the morph relation, and the config in one pass.

Implementation Patterns

Attach the trait narrowly, not globally. Put Reportable on the models that genuinely receive abuse reports. Every model that uses it gets a morph entry, extra relations, and now delete/restore event hooks — applying it to a base model class multiplies that across the app for no benefit.

Separate "who reported" from "what was reported". The trait gives you the reported side; the reporter is a BelongsTo to your user model. Keep the reporter relation explicit rather than relying on auth() inside the package, so you can seed, backfill, and test reports without a session.

Moderation state belongs in your app, not the package. The package records that a report exists; it generally does not implement resolution workflows. Add your own status / resolved_at / resolved_by columns via a follow-up migration and scope on them:

Report::whereNull('resolved_at')->with('reportable')->get();

Use the cascade semantics as your audit trail. As of 1.4.1, deleting a reported post soft-deletes its reports and restoring the post restores them. That means your moderation history stays consistent with content lifecycle without custom code — but only if you soft-delete the parent. Hard deletes will typically cascade-delete reports outright (or orphan them, depending on your FK setup); decide which you want and configure foreign keys accordingly.

Aggregate before paginating. Report counts per model are the usual dashboard need and the usual N+1 source:

Post::withCount('reports')->orderByDesc('reports_count')->take(20)->get();

If you need "distinct reporters" rather than raw report rows, add a withCount(['reports as reporter_count' => fn ($q) => $q->select(DB::raw('count(distinct reporter_id)'))]) style query and cache the result — moderation dashboards get hit constantly.

Hook into the events now that boot is fixed. With model boot in place, deleting/deleted/restoring/restored fire as expected. Use an observer on your reportable model to trigger side effects — notifying moderators when a reported post is deleted, or clearing a cached report count:

class PostObserver
{
    public function restored(Post $post): void
    {
        Cache::forget("post.{$post->id}.report_count");
    }
}

Wrap write paths in transactions when reports drive other state. Reporting + incrementing a strike counter + dispatching a job should be atomic; the package will not do that for you.

Gotchas and Tips

  • 1.4.1 adds SoftDeletes to the report model — this likely needs a migration. Verify deleted_at exists on reports before deploying. A missing column turns every report query into a SQLSTATE[42S22] at runtime, and because the trait is attached broadly the failure shows up in unrelated features first.
  • Existing hard-deleted reports are gone; existing reports get deleted_at = NULL. Nullable is the default, so no backfill is needed — but if you wrote raw SQL with DELETE FROM reports, those rows are unrecoverable and the new restore path will not resurrect them.
  • The boot fix changes observable behaviour. If you previously worked around missing events with manual ->touch() calls, observers, or a custom booted() hook, remove those workarounds or you will double-fire side effects (duplicate notifications, double-incremented counters) after upgrading.
  • Report queries now exclude trashed rows by default. Dashboard totals will drop after upgrade for any report whose parent was deleted. That is correct, but it looks like data loss in a diff. Use withTrashed() on the report relation when you deliberately want historical counts, and be explicit about which number the UI is showing.
  • restore() only helps if deleted_at is set. Restoring a parent that was never soft-deleted is a no-op; make sure your deleted_at column and casts are actually present on the reportable model, not just on Report.
  • Morph maps. If you use Relation::enforceMorphMap(), register every reportable class. Storing FQCNs means renaming or moving a model silently breaks reportable resolution for existing rows — a morphMap makes that a one-line config change instead of a data migration.
  • Config caching. Publish, edit, then php artisan config:clear in local. A stale cached config is the most common cause of "the package ignores my settings" bug reports.
  • Eager-load in lists, always. Report::with('reportable', 'reporter') — the morph relation is not lazy-loadable in bulk without a query per row, and moderation tables are exactly where this bites.
  • Testing. Use RefreshDatabase plus the package factory for reports. Assert the cascade explicitly so a future regression is caught:
$post->report($user, 'spam');
$post->delete();
$this->assertSoftDeleted('reports', ['id' => $report->id]);
$post->restore();
$this->assertNotSoftDeleted('reports', ['id' => $report->id]);
  • Extension point. If the built-in cascade does not match your retention policy (e.g. you want reports archived rather than soft-deleted), override the trait methods or model events in your own Report subclass and swap it in via the package's config model binding rather than editing vendor code.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels