hallindavid/manny
Manny is a Laravel package for managing many-to-many relationships. It provides a clean, fluent API for attaching, detaching, syncing, and querying related Eloquent models through pivot tables, helping keep relationship code simple and readable.
manny is a small utility library of reusable "manipulators" and "formatters" for PHP. The intended first move is to understand what helpers actually ship before writing any glue code.
Install
composer require hallindavid/manny
There is no published release since December 2022, so pin the constraint ("^x.y") and audit composer.lock in CI. If auto-discovery doesn't pick up a service provider (several pre-2023 packages don't declare it), register it manually in config/app.php.
Where to look first (in this order)
README.md — this is the de-facto API index for packages of this size. Read the usage block end-to-end; the method names are the whole product.src/ — grep for public static function and public function. That one-liner gives you the entire surface area faster than the README, and confirms namespace (Hallindavid\Manny\... is the convention but verify).composer.json — check the autoload.psr-4 map and the require block. A light-weight package should pull in almost nothing; if you see large transitive deps, that's a smell.tests/ — the test files are executable documentation and will show edge-case expectations better than prose.First use case
The high-value use for this kind of package in a Laravel app is cleaning up scattered closure-style formatting in Blade views and form requests. Pick one formatter you'd otherwise hand-roll (slug, ordinal, currency, truncation, etc.), replace two or three ad-hoc implementations, and confirm the output matches. Do this before adopting it broadly, so you can judge whether each helper actually earns its keep over Str:: / Number:: (which have shipped natively since Laravel 9/10).
Static helper usage in presenters / view models
Keep controller code clean by delegating to a small presenter that wraps the package calls. That gives you one seam to swap or remove the package later:
final class ProductPresenter
{
public function __construct(private Product $product) {}
public function priceLabel(): string
{
// whatever the package's currency/format helper is named
return Manny::currency($this->product->price_cents, 'USD');
}
public function slug(): string
{
return Manny::slug($this->product->name);
}
}
The presenter is the pattern: the package lives behind one class, not sprinkled across 40 files. When it gets abandoned (and given the release cadence, plan for it), you swap the body of the presenter and move on.
Form request / validation normalization
Formatters that trim, slugify, or normalize phone numbers belong in prepareForValidation():
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Manny::slug($this->input('title')),
'phone' => Manny::phone($this->input('phone'), 'US'),
]);
}
This keeps validators readable and makes the transformation testable in isolation.
Blade directives / accessors
For display-only concerns (ordinals, percent, human-readable byte sizes), expose them as model accessors or a Blade directive rather than calling the library inline in templates. Templates should not import namespaces.
Composition across manipulators
If the package exposes chainable manipulators (fluent wrappers), compose them in one place to avoid re-deriving the same pipeline:
$headline = Manny::of($raw)
->trim()
->truncate(80)
->title();
Wrap these pipelines in named methods on a domain class so the intent (headline, ticker) is what callers see, not the chained calls.
Integration tip — one adapter class per concern
Split into Formatters, Slugs, Numbers adapters if the package grows large. It keeps test coverage tight and makes it obvious which parts of the package you actually depend on. When you eventually migrate off, you delete adapters, not call sites.
Stale maintenance window
Last release was December 2022. Before adopting, check the GitHub issues tab and recent commit activity on the default branch. If it's dormant, budget for a fork or an in-house port. Pin your version and document the decision in an ADR.
Overlap with native Laravel helpers
Laravel 9+ ships Illuminate\Support\Str with slug, headline, title, limit, of(), and Laravel 10+ ships Illuminate\Support\Number with currency/percentage formatting. Before adding manny, diff each helper you intend to use against the native equivalent — the win may be smaller than expected, and native helpers are covered by framework security updates.
Namespace and class-name assumption
Do not assume the class name. Hallindavid\Manny\Manny is a plausible guess, but the package may expose a facade, a Helpers namespace, or function-style autoloads (via files in composer.json). Grep for function in src/ and for autoload.files in composer.json to confirm.
PHP version / typing strictness
A 2022 package may target PHP 7.4 or 8.0. Run it through your CI matrix before relying on it. If it declares no php constraint in composer.json, add one in your own application composer file or risk runtime type errors on newer PHP.
Locale and ICU
Formatting helpers (currency, dates, ordinals) frequently depend on intl / ICU. If the package calls NumberFormatter or IntlDateFormatter under the hood, verify ext-intl is enabled in your production and queue workers — a common source of "works locally, throws in prod" bugs.
Testing and mocking
Static calls are hard to mock. Do not call the package directly from tests where you need to fake output — route through the presenter/adapter so tests can swap the adapter. For the adapter itself, write a small contract test asserting the exact input/output pairs you depend on. That test suite doubles as your migration spec if you replace the package.
Extension point — subclassing vs wrapping
Prefer composition over subclassing even if the package's classes are non-final. If you must extend, put the extension in app/Support/Manny/ and mark it final so the surface area stops growing.
Debugging format output
When a formatter returns something unexpected, check in this order: (1) input type — some formatters accept int cents while others accept a float dollars amount; (2) locale — pass it explicitly, don't rely on setlocale() side effects; (3) PHP intl version. A quick var_dump(get_class_methods(Manny::class)) in tinker will confirm the actual method names and save you a round-trip to the README.
Lockfile hygiene
Given the release age and 37 stars, add the package to your dependency review allow-list and re-audit on every composer update. A tiny utility library is low-risk, but a dormant one that formats user-visible strings is worth a periodic look.
How can I help you explore Laravel packages today?