tomatophp/tomato-contracts
Core interfaces and contracts for the TomatoPHP ecosystem. Defines shared abstractions that keep TomatoPHP modules consistent, decoupled, and interoperable, helping developers build Laravel applications and packages against standardized contracts.
tomato-contracts is an early-stage (v0.0.1) package of PHP interfaces used across the TomatoPHP ecosystem. It ships no runtime behavior — it is a vocabulary layer that other packages (CRUD generators, plugins, admin panels) agree on.
Minimal steps:
composer require tomatophp/tomato-contracts
Where to look first:
src/Contracts/ (or src/) — the interface definitions themselves. Read every file before writing code; the method signatures are the spec.composer.json — check php and illuminate/* constraints. Pin these to match your app's Laravel version before installing.First use case: instead of type-hinting a concrete TomatoPHP class in your own plugin, code against the contract.
use TomatoPHP\Contracts\...\SomeContract;
public function __construct(private readonly SomeContract $service) {}
Then bind an implementation in a service provider. Because the package only defines contracts, you must supply the concrete class — or depend on the package that does (e.g. tomato-admin, tomato-crud, etc.).
Bind interfaces in your provider. Contracts with no implementation will throw BindingResolutionException when resolved. Register them explicitly:
public function register(): void
{
$this->app->bind(SomeContract::class, MyImplementation::class);
$this->app->singleton(OtherContract::class, OtherImplementation::class);
}
Write your own classes against the contract, not the framework. This is the whole point — you can swap TomatoPHP internals without touching your code, and the upstream packages can evolve their concretes.
class MyService implements SomeContract
{
public function handle(array $payload): mixed
{
// ...
}
}
Use contracts for plugin/extension discovery. A common TomatoPHP pattern is a plugin manager resolving an array of classes that implement a contract. Filter your manifests with is_subclass_of($class, SomeContract::class) before registering.
Testing. Since these are interfaces, mock them freely:
$this->mock(SomeContract::class)
->shouldReceive('handle')
->once()
->andReturn(true);
Integration tip — extend, don't modify. If a signature doesn't fit, wrap it in an adapter class that implements the contract, and bind the adapter. Never fork the package into your app; you'll lose upgrades.
Pair with the concrete package. Contracts alone do nothing useful. Install the matching implementation package (tomatophp/tomato-*) in the same composer require step so autoloading and provider registration line up.
"tomatophp/tomato-contracts": "0.0.1") or use a ~0.0.1 range with care. Any 0.x bump may rename or remove a method.php artisan about or php artisan vendor:publish --list before assuming bindings exist.composer.json (autoload.psr-4) — do not guess TomatoPHP\Contracts\. A wrong namespace is the most common "class not found" in early packages.require block. If it needs PHP 8.2 and you're on 8.1 (or Laravel 9 vs 11), composer will silently resolve to an older tag or fail — use composer why-not tomatophp/tomato-contracts to debug.tomato-contracts versions. Run composer update --with-dependencies and check composer show -t for conflicts if you get Declaration must be compatible errors.php artisan tinker and app()->make(SomeContract::class) to isolate a missing binding; the stack trace will name the contract, not the caller.Given the version number and lack of release notes, treat this as a watch-and-pin dependency rather than a foundation until the API stabilizes past 0.1.
How can I help you explore Laravel packages today?