Flick
Guide Validation Services API Pro Examples GitHub
Docs / Services

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() - the RequestInterface: POST, GET, files, cookies, headers, ip()
  • session() - the SessionInterface
  • getSessionValue(), 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'];