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

Utils.value.value Interface Laravel Package

mouf/utils.value.value-interface

A tiny PHP package from Mouf that defines the ValueInterface contract for objects exposing a value through getValue(). It provides a simple, reusable abstraction for value wrappers and dependency-injection-friendly components.

View on GitHub
Deep Wiki
Context7

Getting Started

What you actually get. One file: an interface declaring a single method, val(), that returns the computed value of the object. There is no service provider, no config, no facade, no runtime code. It is a naming convention published as a Composer package so that unrelated libraries can agree on how to ask "give me your value."

composer require mouf/utils.value.value-interface

Where to look first. Read the interface itself — it is shorter than the README:

cat vendor/mouf/utils.value.value-interface/src/ValueInterface.php

Confirm two things before writing any code: the exact namespace (it is a Mouf/TheCodingMachine package, so expect something under Mouf\Utils\Value or Interop\Value) and the exact method signature. Do not assume from blog posts — the package is old enough that the namespace has moved between major versions.

First use case. The interface exists to make lazy computation type-safe. Instead of injecting float $discountRate (resolved at construction) or Closure $discountRate (untyped, undiscoverable), you inject ValueInterface and call val() at the moment you need the number:

use Mouf\Utils\Value\ValueInterface; // verify namespace

final class DiscountRule
{
    public function __construct(private ValueInterface $rate) {}

    public function apply(Money $price): Money
    {
        return $price->multiply($this->rate->val());
    }
}

The single most useful adapter you will write is the closure wrapper, because it lets every existing Laravel closure-based binding satisfy the interface:

final class CallbackValue implements ValueInterface
{
    public function __construct(private \Closure $callback) {}

    public function val(): mixed
    {
        return ($this->callback)();
    }
}

That is the whole onboarding. Everything below is pattern-matching that adapter onto real Laravel code.

Implementation Patterns

Bind configuration as values, not scalars. The natural Laravel fit is config that is cheap to read but should not be read at container-build time:

// AppServiceProvider::register()
$this->app->bind(DiscountRate::class, fn () => new CallbackValue(
    fn () => (float) config('shop.discount_rate')
));

Now DiscountRule gets fresh config per resolution, and tests can bind a CallbackValue(fn () => 0.0) without touching config().

Normalize at the boundary with a union adapter. Accept ValueInterface|float|Closure in public APIs, convert immediately, and keep the interface on the inside only:

function asValue(ValueInterface|float|Closure $input): ValueInterface
{
    return match (true) {
        $input instanceof ValueInterface => $input,
        $input instanceof Closure        => new CallbackValue($input),
        default                          => new CallbackValue(fn () => $input),
    };
}

Compose rather than cache. Because the contract is one method, composites are trivial and read well for pricing, quotas, and feature-flag thresholds:

final class Sum implements ValueInterface
{
    /** @param ValueInterface[] $values */
    public function __construct(private array $values) {}

    public function val(): float
    {
        return array_sum(array_map(fn (ValueInterface $v) => $v->val(), $this->values));
    }
}

Layer a memoizing decorator when a value is expensive (DB lookup, HTTP call) and read more than once:

final class CachedValue implements ValueInterface
{
    private bool $resolved = false;
    private mixed $value = null;

    public function __construct(private ValueInterface $inner) {}

    public function val(): mixed
    {
        return $this->resolved
            ? $this->value
            : [$this->value, $this->resolved] = [$this->inner->val(), true];
    }
}

Use it as a strategy seam for rules, not for models. Validation rules, tax calculators, and eligibility checks are good hosts. Eloquent models are not — see Gotchas.

Persist through a cast, not through the model. If you store a computed value, convert on the way out and flatten on the way in:

final class ValueCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes): ?ValueInterface
    {
        return $value === null ? null : new CallbackValue(fn () => $value);
    }

    public function set($model, $key, $value, $attributes): array
    {
        return [$key => $value instanceof ValueInterface ? $value->val() : $value];
    }
}

Declare a typed sub-interface for static analysis. The upstream interface almost certainly has no return type and no generics. Add your own narrowing contract in App\Values and have domain classes implement that:

/**
 * @template T
 */
interface TypedValueInterface extends ValueInterface
{
    /** @return T */
    public function val(): mixed;
}

PHP's covariance rules let you narrow to : float in the concrete class even when the parent declares nothing.

Gotchas and Tips

  • val() is not cached and not idempotent by contract. The interface says nothing about side effects or stability. If a value is read in a foreach or inside a Blade loop, wrap it in CachedValue or you will re-hit the database per iteration.

  • Never implement it on an Eloquent model. val() on a model collides conceptually with value(), with accessor magic, and with toArray(). If you need a model to expose a value, return a CallbackValue from a plain method.

  • JSON and Blade will not serialize it. json_encode(new CallbackValue(...)) gives {}. Add JsonSerializable to your concrete classes returning $this->val(), and in Blade call ->val() explicitly rather than relying on __toString. If you do add __toString(), keep it pure — an exception thrown from __toString in PHP 8 during string coercion produces confusing traces.

  • Name collision with Laravel helpers. value() (container/closure resolution) and val() in illuminate/support (Illuminate\Support\HigherOrderCollectionProxy) both exist. Inside a class implementing the interface, an unqualified val($x) call may resolve unexpectedly. Fully qualify or import explicitly.

  • The package is effectively a one-file contract with 1 star and 0 dependents. Treat it as a vendored convention. If you are not interoperating with other Mouf/TheCodingMachine packages, define the identical interface in app/Values/ValueInterface.php and save yourself a dependency. The only real argument for the package is interop with mouf/utils.value.value and friends, which is exactly what the shared namespace buys you.

  • Add declare(strict_types=1) in every implementation. Because val() is untyped upstream, a stray "12.50" string will silently pass into money math. Static analysis (PHPStan/Psalm at level 6+) is what actually enforces the contract here, not PHP.

  • Testing pattern: assert on val(), never on the object identity. $this->assertSame(0.2, $rule->val()). For consumers, inject a stub rather than mocking the interface — mocking a single-method interface with no return type gives you less signal than a two-line anonymous class.

  • Debugging a wrong value is almost always "the closure captured the wrong scope." CallbackValue closes over whatever was in scope at construction; container singletons built once will keep a stale captured config across a long-running Octane worker. Bind with fn () => (re-reads the config each resolution) or use bind, never singleton, for config-derived values.

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.
users-au/socialite-provider
twidpay/twid-logger
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