madong/helper
A lightweight Laravel/PHP helper package that bundles convenient utility functions for common development tasks. Designed to reduce boilerplate and simplify everyday coding, it offers an easy-to-integrate set of helpers for faster, cleaner application development.
madong/helper is not a Laravel package — it is the shared utility layer for the madong admin ecosystem running on Webman (Workerman). Treat it as the equivalent of laravel/framework's Illuminate\Support helpers plus your own app/helpers.php, extracted so that single-tenant and multi-tenant madong backends can share one implementation. That framing tells you most of what you need before you open the source.
Minimal steps
require.php constraint first. v1.1.2 is the release that added PHP 8.4+ syntax support (#1 对PHP8.4+语法支持) — meaning anything older than 1.1.2 is a liability on PHP 8.4. On a modern Webman image you are probably on 8.3/8.4 already, so treat 1.1.2 as the minimum rather than an optional bump.composer require madong/helper in the Webman project (it will almost certainly be pulled in transitively by madong/admin — check composer.lock first, you may already have it, and check which version you got).vendor/madong/helper/composer.json and read the autoload block. Helper libraries of this shape load global functions via "autoload": { "files": [...] }; that list is your actual API index. Read those files top to bottom — with a 0-star package there is no documentation, the source is the docs.src/ for the class-based pieces: expect a tenant/context object, a response/result wrapper, tree/array utilities, and config accessors.config/plugin/madong/helper/*.php or an install.php/Webman\Bootstrap implementation). If it does, that config file is where tenant resolution mode is switched between single- and multi-tenant.Where to look first, in order
composer.json → require.php, autoload.files and autoload.psr-4 (the floor version, the function list, and the namespace root).tenant). This is the single most consequential piece of the library, because it determines whether your code is reusable across both deployment modes.helpers.php-style file for global functions, and cross-check each name against Laravel/Symfony/Webman globals for collisions (see Gotchas).First use case
The first thing you should actually do with this package is not call a string/array helper — it's to wire the tenant context correctly in a multi-tenant boot. Write a two-line probe:
// in a controller or a `php webman` tinker-style script
var_dump(function_exists('tenant_id') ? tenant_id() : null);
(or whatever the context accessor turns out to be called after step 3). Get a real tenant ID out of it for a request in both a single-tenant and a multi-tenant deployment before building anything on top. Everything else in the library is convenience; tenancy is correctness.
Call helpers, don't re-wrap them. The whole value proposition is that madong/admin, madong/helper, and any plugin you write all resolve tenancy and formatting identically. If you write function my_tenant_id() you've just forked the ecosystem's source of truth and your plugin will drift on the next release. Use the package's accessor everywhere, including in your own plugin's models/repositories.
Tenant-scoped queries at the repository edge, not in controllers. The typical madong pattern is: controller → service → repository, with the repository applying the tenant predicate. Keep the helper's tenant accessor inside the repository so that a single-tenant deployment (where the accessor returns null/0 and the predicate is skipped) needs zero code changes. Any where('tenant_id', ...) you hand-write in a controller is a bug waiting for the multi-tenant switch.
Single-tenant/multi-tenant parity as a design constraint. Design so the mode is configuration, not code branching. Prefer:
$query = $this->applyTenantScope($model->newQuery()); // helper-driven, no-op in single-tenant
over
if (config('tenant.enabled')) { ... } else { ... }
The second form doubles your test matrix and is where most madong-ecosystem bugs live. If the helper exposes a "current tenant" object, code against the object's interface rather than checking a boolean flag.
Response shaping. madong backends conventionally expose a normalized JSON envelope (code/msg/data) so the admin frontend can interpret every endpoint uniformly. If the package ships a result/response helper, use it as the only return path from controllers — mixed envelopes is the fastest way to break the SPA's error handling. Wrap it once in your own base controller method if the raw API is verbose.
Array/tree utilities for menu and permission data. Admin ecosystems are full of parent-child arrays (menus, departments, categories). Use the package's tree builder rather than array_filter recursion in each plugin; it keeps ordering and children key conventions consistent with the admin UI's expectations.
Mapping to Laravel muscle memory. Useful translations when you're new to Webman:
app()/container → support\Container or the package's own accessor; there are no facades.config('x.y') → config('x.y') still exists in Webman, but the package may have its own config accessor with defaults — prefer whichever the package itself uses internally so plugin config resolution matches.config/middleware.php. If it runs after auth, your auth queries are unscoped.illuminate/database — madong is generally Laravel-DB-based, so models/query builder feel familiar but global scopes and booted() hooks may behave differently than you expect.Version pinning and the upgrade cadence. Pin explicitly — "madong/helper": "^1.1.2", not *, and not ~1.0 (which can resolve below the 8.4-safe release on a fresh composer update). The library is 0-star with a very recent release date; you are effectively the QA team. For a syntax-only bump like 1.1.2 the risk is minimal (no new API surface), but that is also exactly why you should diff vendor/madong/helper before and after rather than trusting a one-line changelog.
Global function collisions are the #1 failure mode. Helper libraries that autoload files define functions in the global namespace. Watch for: str_*/arr_* names that overlap illuminate/support, env() and config() if you run madong code alongside Laravel components, and short names like tree(), format(), result(). Composer loads autoload.files before your app's own helper file in most setups, so a colliding name in your app/functions.php is a fatal Cannot redeclare. Grep before you add:
grep -rho "function [a-zA-Z0-9_]*(" vendor/madong/helper --include=*.php | sort -u > /tmp/helper_fns.txt
Diff that against your app's functions once, at install time.
PHP 8.4 is a hard floor on 1.1.2, and a question mark below it. The v1.1.2 changelog is a single line — "PHP 8.4+ syntax support" — which is a strong signal that pre-1.1.2 code either emitted E_DEPRECATED notices under 8.4 (the classic culprit being implicitly nullable parameters, function f(Foo $x = null)) or straight-up used syntax that only parses on newer interpreters. Two things to do:
composer.json's require.php between your resolved version and 1.1.2. If the constraint did not change, the author fixed syntax without advertising a floor — you may still be fine on 8.1+, but don't assume 1.1.2 dropped older PHP.E_DEPRECATED-to-ErrorException conversions in Webman's error handler can turn a harmless deprecation into a 500 on a request path you never exercise locally.Static tenant state leaks across requests — this is the big one. Webman is a resident process, not PHP-FPM. A static $tenantId in the helper (a very common implementation) survives between requests handled by the same worker. In single-tenant mode nobody notices; in multi-tenant mode, request B can inherit request A's tenant under load. Verify that the tenancy context is reset per request — typically via a middleware before/after, or a request-scoped object. Write a test that hits the same worker twice with different hosts/headers and asserts the tenant ID changes:
// pseudo: two sequential requests via the same Webman worker
$first = requestAs('tenant-a.test');
$second = requestAs('tenant-b.test');
assert($second->tenantId !== $first->tenantId); // if this fails, you have a leak
This same class of bug applies to any cached config, locale, or auth identity held statically. It is the single most valuable hour you will spend on this package.
No facades, no auto-discovery. Don't assume Laravel's package auto-discovery applies; Webman plugins register via config/plugin/... and Webman\Bootstrap classes. If something "doesn't work," you usually forgot to enable the plugin config or the bootstrap class — check config/plugin/madong/helper/app.php exists and is enabled.
Debugging the tenant resolution. Add a temporary log line in the tenancy bootstrap printing the resolved tenant, the source (subdomain/header/domain), and the worker PID. When a tenant appears wrong in staging, that log tells you instantly whether it's resolution or leakage. Include PHP_VERSION in that same line while you are validating a PHP bump — it makes "works on my machine" conversations with the 8.4 upgrade short.
Extension points to prefer over forking. If the package allows injecting a tenant resolver (interface + config binding) or overriding the config defaults, use that. Forking a 0-star helper library means you own its maintenance forever, and madong's own packages will still call the original namespace. If you must patch, use a composer patches entry (cweagans/composer-patches) so the change is visible and re-appliable — and note that a syntax-only release like 1.1.2 is the cheapest kind of upstream fix you can get, so prefer bumping over patching here.
Config quirks. Ecosystem helpers often read config with hardcoded defaults that hide the real value (e.g. config('plugin.madong.helper.tenant.enabled', false)), which makes a missing config file look like "feature off" instead of "misconfigured." Dump the effective config at boot once, and prefer the fully-qualified plugin. prefix — it's a common source of silently-ignored settings in Webman.
Testing and upgrade risk. Treat the library as third-party but load-bearing: add a smoke test suite that exercises tenancy resolution, response envelope shape, and tree building against a pinned version. When you bump it, those three tests catch essentially all the breakage, given how small the surface area is. Widen your CI matrix to the PHP minors you actually deploy when you move to ^1.1.2; the release is explicitly a runtime-compatibility change, so a single-version test run tells you nothing new. And because the license is Apache-2.0, you have the option to vendor/fork with explicit attribution if the project stalls — a reasonable contingency plan for a 0-star dependency in production.
How can I help you explore Laravel packages today?