Service Providers
Adding your own forms and dropdowns is great, but what if you need a little more power? Flick has you covered with Service Providers.
Looking for Auth, Mail, SQL, Uploads, or Bot Protection?
Flick Pro includes production-ready services for common needs. Tested and maintained, $99/year.
See Pro Services | Continue below to build your own.
As you can see by the directory structure below, a Flick Service is pretty straightforward consisting of, at minimum, only three files; the ServiceProvider class, the Class which contains your logic, and the package's composer file.
foo ├─ src │ └─ Foo.php │ └─ FooServiceProvider.php │ └─ register.php ├─ composer.json
The Service Provider Class
The Service Provider class is where you import your Service's configuration and Flick's Support methods. It's also where you give your service its name, which is used when working with Flick.
foo/src/FooServiceProvider.php
<?php namespace Foo; use Flick\Service\ServiceProvider; use Flick\Support\Support; class FooServiceProvider implements ServiceProvider { protected array $config; protected Support $support; public function register($container): void { // this is where we name our service; // we access it like this: $form->foo->methodToUse() $container->set('foo', function () use ($container) { return new Foo($this->config, $this->support); }); } // this imports the config values for your service and your form public function setConfig($config): void { $this->config = $config; } // this imports Flick's Support methods public function setSupport($support): void { $this->support = $support; } }
Tip
If your Service's name is the same as one of Flick's, such as 'forms', your Service will take priority and override Flick's. So feel free to name your Service whatever you want.
The Logic Class
This is where we work our magic and do our stuff. This example is pretty basic, however; it shows you how simple it actually is to create Services with Flick.
foo/src/Foo.php
<?php namespace Foo; use Flick\Support\Support; class Foo { protected array $config; protected Support $support; public function __construct($config, $support) { $this->config = $config; $this->support = $support; } public function hello($message = ''): string { if (! empty($message)) { return $this->support->return($message); } else { return $this->support->return("Hello from Flick Foo!"); } } public function error(): void { $this->support->addError('key', '', 'invalidRule', 'foo'); } }
The Error Handling Pattern
Flick services use a consistent pattern for error handling: instead of throwing exceptions, use addError() and return false. This allows developers to use simple if-checks.
In your service:
public function doSomething(): bool { try { // ... operation that might fail return true; } catch (SomeException $e) { $this->support->addError('myservice', $e->getMessage()); return false; } }
How developers use it:
if ($form->myservice->doSomething()) { $form->successMessage('Success!'); } else { $form->errors(); }
Tip
Use addError() + return false for runtime failures (transport errors, file not found, API failures). Throw InvalidArgumentException for configuration errors that should fail fast.
Composer
Flick finds your service through Composer's autoloader. Your composer.json lists a small file under autoload.files; Composer includes it on every request, and the file tells Flick about your provider.
foo/composer.json
{ "name": "my/foo", "type": "library", "description": "A foo package for Flick", "license": "MIT", "autoload": { "files": ["src/register.php"], "psr-4": { "Foo\\": "src/" } }, "minimum-stability": "dev" }
foo/src/register.php
<?php declare(strict_types=1); use Flick\Service\Registry; use Foo\FooServiceProvider; Registry::add('foo', FooServiceProvider::class);
The first argument is the service's name — the same name you give it in register() and use as $form->foo. Keep this file to that one call: Composer runs it on every request of the application, whether or not a form is built on that page.
Configuring Our Service
Registration already happened, back in register.php. This array is how the Service gets its settings: add its name to the services configuration array with the values it needs. A Service with nothing to configure needs no entry at all.
$config = [ 'services' => [ 'foo' => ['text' => 'Hello from the Foo config array!'] ] ]; $form = new Flick($config);
How Flick Finds It
There is nothing to configure. Composer includes register.php when the application's autoloader loads, so by the time you call new Flick() the service is already on the list. Installing, updating, or removing the package is picked up the next time Composer regenerates the autoloader, which composer require and composer remove do for you.
If $form->hasService('foo') is false, check that the package is installed and that register.php is listed under autoload.files. A register.php that names a class that does not exist, or one that does not implement ServiceProvider, throws when the form is built, with the name and class in the message.
Registering Without a Package
A service does not have to be a Composer package. Call Registry::add() yourself anywhere before you build the form and Flick treats the provider exactly like an installed one:
use Flick\Flick; use Flick\Service\Registry; Registry::add('foo', App\Flick\FooServiceProvider::class); $form = new Flick($config); $form->foo->doSomething();
This is the quickest way to try a service out before packaging it. Registering a name that is already taken replaces the earlier provider.
Using The Service
We're now ready to use our Service. Chain the Service's name and the method you want to use to your form object, and you're ready to go.
// produces "Hello from Flick Foo!" $form->foo->hello(); // produces "This is custom text" $form->foo->hello('This is custom text');
Tip
To try Foo locally, put the three files above in their own directory and install it as a Composer path repository:
"repositories": [ { "type": "path", "url": "../foo", "options": { "symlink": true } } ]
Then composer require my/foo:dev-main and Flick will find it automatically.
Support Methods available to your Service
The Support class is a shared utility that connects your service to Flick's core functionality. It provides error handling, session management, form utilities, and configuration access. When your service needs to report an error back to the developer, or check if a form was submitted, you'll use $this->support.
You have access to all public methods in Flick's Helpers trait.
Error Methods:
addError()errorsIsEmpty()errorsIsNotEmpty()getError()getErrors()hasError()
Messaging Methods:
errors()errorMessage()infoMessage()successMessage()warningMessage()
Utility Methods:
clear()dd()destroySession()dump()getIp()inputIsEmpty()inputIsNotEmpty()ok()redirect()return()slug()submitted()verifyPassword()
Request and Session:
These four are how a service reaches the outside world. request() and
session() hand back the adapters Flick resolved, so your service works the
same standalone as it does inside Laravel.
request()- theRequestInterface: POST, GET, files, cookies, headers,ip()session()- theSessionInterfacegetSessionValue(),addSessionValue(),hasSessionValue(),deleteSessionValue(),sessionIsActive()config()- read a value from the merged configuration
// The proxy-aware client IP, however the host application supplies requests $ip = $this->support->request()->ip(); // Session values, through whichever session adapter is in play $this->support->addSessionValue('my_service_state', $value);
Translatable Messages:
message() looks a key up in the active language file, so an error goes into the
bag already translated. This is how every Pro service does it.
$this->support->addError( 'myservice', $this->support->message('UploadFileTooLarge', ['size' => '3 MB', 'max' => '2 MB']) );
Placeholders use the :name style, without the leading colon in the array. A key
a translation leaves out falls back to the shipped English rather than rendering
blank.
Warning
The key has to exist in Flick's own lang/en/messages.php. A key that doesn't
throws a LogicException rather than returning the key, because a missing key is
a bug and a silent blank error message is worse than a loud failure.
That makes message() the right tool for Flick's own services and Pro's. For a
third-party service, pass your own string to addError() — or ship a language
file with your package and read it yourself.
Add a message to Flick's error bag from within your Service
// addError(string $name, string $message, string $rule = '', array|string $matches = '') $this->support->addError('myservice', 'Something went wrong'); // With rule and matches (useful for validation-style errors) $this->support->addError('email', 'Invalid email format', 'email', 'user@invalid');
Get the form ID from the configuration array
$this->config['form']['id'];