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

Push Laravel Laravel Package

asundust/push-laravel

A Laravel package that wraps the asundust/push library to send notifications through multiple push providers. Config-driven setup and a simple facade make it easy to fire alerts, system messages, and on-demand notifications from your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Despite the package description's generic "push notifications" framing, asundust/push-laravel is a thin Laravel wrapper around asundust/wechat-work-push — it sends messages to WeChat Work (企业微信) group robot webhooks. Scope your expectations accordingly: it is not a multi-provider push abstraction (no FCM/APNs/web-push), and it does not replace laravel/notifications. It is an HTTP client with Laravel ergonomics.

Minimal setup:

  1. composer require asundust/push-laravel (pin to 1.0.3 explicitly given the vague Laravel 11 release note).
  2. Publish the config. The tag is not documented, so run php artisan vendor:publish and select the provider entry, or target it directly: php artisan vendor:publish --provider="Asundust\PushLaravel\PushServiceProvider".
  3. Put the robot webhook key in .env (e.g. WECHAT_WORK_PUSH_KEY=xxxxxxxx-xxxx-...) and reference it from the published config via env(). Never commit the key; anyone with it can post into your group.
  4. Smoke test in php artisan tinker using the facade/container binding exposed by the service provider.

Where to look first, in order:

  • vendor/asundust/push-laravel/src/ — the service provider (binding name, facade alias, whether it's a singleton) and the client class (which message types are actually implemented: text / markdown / image / news / file / template_card).
  • The published config file — key name, default robot, timeout, and any queue-related flags.
  • The upstream asundust/wechat-work-push README — the Laravel wrapper inherits its payload semantics, and the upstream docs are the only real reference for errcodes and message shapes.

First use case: deploy and error alerts into an ops/engineering group. Send a plain text message from a queue job, confirm delivery, then move to markdown for anything with links or code. Do not start with images or template cards; those have the most payload-shape friction and the least documentation.

Implementation Patterns

Wrap the package behind your own service. Do not call the facade from controllers. Create App\Services\WechatWorkNotifier with a small typed API (alert(), deploy(), digest()). This gives you one place to swap the package, one place to enforce truncation/redaction, and one place to add a no-op driver for local/testing.

Use a custom Notification channel. Implement App\Notifications\Channels\WechatWorkChannel with send($notifiable, $notification), and have notifications expose toWechatWork(): string. This buys you via(), ShouldQueue, onQueue(), and per-notifiable routing for free, and keeps the vendor API out of your domain code.

Always send from a job, never inline in a request. Wrap dispatch in a SendWechatWorkMessage job with public $tries = 3, public $backoff = [10, 60, 300], and ShouldQueue. Dispatch with ->afterCommit() (or after_commit = true) so you never post a "payment succeeded" alert for a transaction that then rolls back.

Throttle at the job level, not the call site. WeChat Work robots allow roughly 20 messages/minute each. Register a named limiter and apply Illuminate\Queue\Middleware\RateLimited::class to the job. Use Redis-backed limiters so multiple queue workers coordinate.

Aggregate before sending. For anything event-driven and high-volume, collect events into a buffer (cache/Redis list) and flush a single markdown digest on a schedule. One 4 KB markdown message beats twelve text messages and keeps you under the rate limit by construction.

Multi-robot / multi-tenant. If the config only exposes a single key, resolve a fresh client per robot rather than mutating config on a singleton: config(['...key' => $key]) then app()->forgetInstance(...) is fragile. Prefer instantiating the underlying client directly with a key argument, and keep your own config/wechat-robots.php map (ops, billing, ci) resolved by name.

Templating. Render message bodies from Blade views (resources/views/notifications/wechat/*.blade.php) to markdown, then pass the rendered string. Keeps formatting out of PHP and makes the truncation point explicit.

Testing. If the package uses Laravel's HTTP client or Guzzle with the default handler, Http::fake() works — assert on the outbound JSON body and fake both the success response and a {"errcode":93000,"errmsg":"invalid webhook url"} failure so your error branch is covered. Bind a NullNotifier in local/testing so a stray test can never hit a real group.

Operational alerting hook. Wire Illuminate\Queue\Events\JobFailed (or bootstrap/app.php's withExceptions) to your notifier so queue failures themselves surface in the same channel — but see the retry caveat below.

Gotchas and Tips

HTTP 200 does not mean success. Every WeChat Work API returns 200 OK with {"errcode": N, "errmsg": "..."} in the body. If the package doesn't check errcode, your sends will silently "succeed" while nothing arrives. Verify this in the source on day one; if it doesn't, wrap the call and throw on errcode !== 0 yourself.

Classify errcodes before retrying. 45009 (rate limited) and network timeouts are transient; 93000 (bad webhook key), 40058 (invalid param), and payload-too-large are permanent. Retrying permanent errors burns through your rate limit and pollutes failure metrics. Throw a non-retryable exception for these.

Duplicates are possible and there is no idempotency key. A timeout after the message was actually delivered, followed by a retry, posts it twice. For alerts that's tolerable; for anything with side effects (a "run this job" instruction), make the recipient-side action idempotent or skip retries entirely.

Content limits are byte-based. Text content is capped around 2048 bytes and markdown around 4096 bytes — Chinese characters are 3 bytes in UTF-8, so character counts mislead you. Truncate with a byte-aware helper and append a …(truncated) marker.

Markdown is a WeChat Work dialect, not CommonMark. No tables, no nested lists, no images. Supported color spans are <font color="info|comment|warning">. Mentions in markdown use <@userid>; in text payloads use mentioned_list / mentioned_mobile_list, with @all for everyone.

config:cache will bite you. env() only works inside config files, and once cached, .env edits do nothing until you re-run php artisan config:cache. This is the single most common "the package is broken" false alarm on Laravel 11 deploys.

The webhook key lives in the URL. It will appear in Guzzle debug logs, Laravel HTTP client logs, exception traces, and any APM that records request URLs. Scrub key= from logs before this package touches production, and rotate immediately if a key leaks.

Package discovery and facade aliases. Laravel 11's config/app.php no longer ships an aliases array. Package aliases registered via composer.json extra.laravel.aliases still work through the package manifest; if the alias is absent, either use the fully-qualified class name or register it in bootstrap/app.php. After any Composer change, php artisan package:discover and composer dump-autoload are the first things to run when you get "class not found."

Singleton bindings freeze config. If the service provider registers a singleton, changing config at runtime after first resolution has no effect. That is the trap in multi-robot setups — prefer constructing your own instance over mutating global config.

Set an explicit timeout. The underlying HTTP client's default may be long enough to pin queue workers during a WeChat Work slowdown. Set connect/request timeouts in the published config (typically 3–10 s) so failed sends fail fast.

Don't send inside a DB transaction. The HTTP round-trip holds locks for hundreds of milliseconds. Use afterCommit on the job or dispatch from a model event fired post-commit.

Version pinning. Per the release note, 1.0.3 claims Laravel 11 support with no stated PHP floor, no support matrix, and no migration notes. Inspect composer.json for the illuminate/* and php constraints before upgrading, diff the lockfile, and run a real send-and-receive smoke test — not just composer install.

The extension point you actually have is the seam you build. With 4 stars and thin docs, treat the package as unstable: keep your own notifier interface, keep message rendering in your views, and keep the vendor facade referenced from exactly one class. That way replacing it with a 60-line Guzzle client against the webhook endpoint — which is genuinely all this wraps — stays a half-day task.

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.
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
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle