Styling
Every code starts from the defaults in qr.defaults (square modules and eyes, black on white, a margin of four modules) and changes what you ask for. Each call returns a changed copy, so a half-built code can be reused.
use ExeQue\Qr\Facades\Qr;
use ExeQue\Qr\Shapes\Enums\Eye;
use ExeQue\Qr\Shapes\Enums\Module;
Qr::url($url)
->modules(Module::DotsMedium)
->eyes(Eye::Rounded)
->paint('#1e1b4b')
->background('#fef3c7')
->margin(2)
->svg();Modules
The shape of every dark module: a registered name, an enum case, a class name or an instance, with options by name. See Modules for every shape.
->modules('dots-medium')
->modules(Module::FlowingStrong)
->modules('flowing-medium', radius: 0.42, concave: false) // a preset with its options changed
->modules(new Flowing(radius: 0.3))A closure gets the modules' builder, for the shape and the rest together:
->modules(fn (ModulesBuilder $modules) => $modules
->shape('rounded', side: 0.9)
->paint('#be123c')
->opacity(0.9)
->size(0.95)
->rotate(45))Eyes
The three finder patterns, each an outer eye (the 7×7 ring) and an inner eye (the 3×3 middle). See Eyes.
->eyes(Eye::Heart) // a preset: outer and inner together
->eyes(outer: 'soft', inner: 'circle') // or apart
->outer('octagon') // just one part, on every corner
->inner('star')
->mirrored() // turn the top-right and bottom-left eyes so their corners point outOne corner, or each corner in turn:
use ExeQue\Qr\Rendering\Enums\Corner;
->eye(Corner::TopLeft, Eye::Dot, paint: '#be123c')
->eye(Corner::TopRight, outer: 'soft', inner: 'heart', rotate: 90)
->eyes(fn (EyesBuilder $eyes) => $eyes
->outer('rounded')
->inner('soft')
->paint('#be123c')
->topLeft(fn (EyeBuilder $eye) => $eye->innerPaint('#1e1b4b'))
->each(fn (EyeBuilder $eye) => $eye->rotate(0)))EyeBuilder has whole(), outer(), inner(), rotate(), paint(), outerPaint() and innerPaint().
Paint and background
paint() colors the modules, and every eye without a paint of its own. It takes any color form or a gradient:
->paint('#1e1b4b')
->paint('oklch(45% 0.2 293)')
->paint(LinearGradient::make('#be123c', '#1e1b4b')->angle(135))
->background('#fef3c7')
->background(RadialGradient::make('#ffffff', '#e0e7ff'))
->background(null) // transparentMargin
The quiet zone round the code, in modules. Scanners need some; the scan guards keep at least two.
->margin(2)Filters and logos
Filters change modules and eyes one by one: a mixed rotation, a palette, a wave. A logo sits in the middle.
->filter('mixed-rotation', min: -15, max: 15)
->logo(storage_path('app/logo.svg'), size: 0.22)Reusing a style
Qr::style() builds a style without content; for() turns it into a code:
$brand = Qr::style()->modules('dots-medium')->eyes('rounded')->paint('#1e1b4b');
$brand->for($event->url)->svg();
Qr::url($url)->style($brand)->png(600);Named styles
Register a style by name in a service provider:
Qr::styles()->register('brand', fn () => Qr::style()->modules('dots-medium')->eyes('rounded')->paint('#1e1b4b'));or describe it in config/qr.php, where only plain values may go, so it survives php artisan config:cache:
'styles' => [
'brand' => ['modules' => ['shape' => 'dots-medium', 'paint' => '#1e1b4b'], 'margin' => 2],
'wedding' => ['extends' => 'brand', 'modules' => ['paint' => '#be123c']],
],To use the style builder's methods in config, start with Style::make(). It keeps the calls as plain values, so they survive php artisan config:cache, and builds the style when it is first used:
use ExeQue\Qr\Rendering\Style;
'styles' => [
'brand' => Style::make()->modules('dots-medium')->eyes('rounded')->paint('#1e1b4b')->margin(2),
'wedding' => ['extends' => 'brand', 'modules' => ['paint' => '#be123c']],
],What it takes, as config must survive the cache:
- the style methods:
modules(),paint(),background(),eyes(),eye(),outer(),inner(),mirrored(),filter(),filters(),withoutFilter(),logo(),margin()andstyle(); nottoStyle(),for()or the code's own settings such aserrorCorrection(), which belong to each code; - as arguments, text, numbers, booleans, arrays, enum cases and color classes (
Hex::make(...),Oklch::make(...)); - shapes, filters and gradients by their names or arrays:
modules('dots-medium', diameter: 0.7),filter('mixed-rotation', min: -10, max: 10),paint(['kind' => 'linear', 'angle' => 135, 'stops' => [...]]); - a logo by its path or data URI.
Gradient and shape objects, closures, and builder closures such as eyes(fn (EyesBuilder $eyes) => ...) cannot be cached, and Style::make() refuses them at once with what to give instead. A style that needs them belongs in a class, or in a service provider.
Or name a class that implements ProvidesQrStyle, for a style that needs code to build:
use ExeQue\Qr\Facades\Qr;
use ExeQue\Qr\Rendering\Contracts\ProvidesQrStyle;
use ExeQue\Qr\Rendering\Style;
final class BrandStyle implements ProvidesQrStyle
{
public function qrStyle(): Style
{
return Qr::style()->modules('dots-medium')->eyes('rounded')->paint('#1e1b4b')->toStyle();
}
}
'styles' => [
'brand' => BrandStyle::class,
'wedding' => ['extends' => 'brand', 'modules' => ['paint' => '#be123c']],
],and use it by name, in code or in Blade:
Qr::url($url)->style('brand')->svg();
Qr::url($url)->style('brand', 'seasonal')->svg(); // several, each over the lastA registered style can also be a Style, a class implementing ExeQue\Qr\Rendering\Contracts\ProvidesQrStyle, or an enum of such styles, every case under its value. qr.defaults.style names a style every code starts from.
Styles are merged in order, each one's changes from the defaults laid over what came before. A value set back to its default in a later style is not a change, so set it after the styles:
Qr::url($url)->style('brand')->margin(4);Storing a style
A style can be written down as plain data, for a database column or a settings form, and read back:
$data = Qr::url($url)->modules('flowing-soft')->paint('#be123c')->toArray(); // only what differs from the defaults
$style = Qr::styles()->hydrate($data);
Qr::url($url)->style($style)->svg();Shapes are stored by class and options, filters and gradients by name and options, so a stored style keeps working as long as those classes and names exist. A style with a closure filter that has no name, or a custom gradient that cannot be stored, cannot be written down.
Scan safety
Styling can make a code harder to read. The scan guards hold the riskiest changes within limits, and throw or warn when contrast is too low or a logo too large. See Scan safety.