Skip to content

Commit

Permalink
feat: Added support for QR codes config option (#871)
Browse files Browse the repository at this point in the history
  • Loading branch information
realodix committed Dec 14, 2022
1 parent ae618fc commit cc892f0
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 14 deletions.
9 changes: 7 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ UH_PUBLIC_SITE=true
UH_REGISTRATION=true
UH_HASH_LENGTH=6
UH_HASH_CHAR=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
UH_REDIRECT_CACHE_LIFETIME=5
UH_REDIRECT_STATUS_CODE=301
UH_WEB_TITLE=true
UH_REDIRECT_CACHE_LIFETIME=5
UH_QRCODE=true
QRCODE=true
QRCODE_SIZE=170
QRCODE_MARGIN=0
QRCODE_FORMAT=png
QRCODE_ERROR_CORRECTION=m
QRCODE_ROUND_BLOCK_SIZE=true

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
Expand Down
9 changes: 7 additions & 2 deletions .env.testing
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ UH_PUBLIC_SITE=true
UH_REGISTRATION=true
UH_HASH_LENGTH=6
UH_HASH_CHAR=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
UH_REDIRECT_CACHE_LIFETIME=5
UH_REDIRECT_STATUS_CODE=301
UH_WEB_TITLE=true
UH_REDIRECT_CACHE_LIFETIME=5
UH_QRCODE=true
QRCODE=true
QRCODE_SIZE=170
QRCODE_MARGIN=0
QRCODE_FORMAT=png
QRCODE_ERROR_CORRECTION=m
QRCODE_ROUND_BLOCK_SIZE=true

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
Expand Down
103 changes: 103 additions & 0 deletions app/Actions/QrCode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace App\Actions;

use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Writer\Result\ResultInterface;

class QrCode
{
const MIN_SIZE = 50;

const MAX_SIZE = 1000;

const FORMAT = 'png';

const SUPPORTED_FORMAT = ['png', 'svg'];

public function process(string $data): ResultInterface
{
return Builder::create()
->data($data)
->labelText(__('Scan QR Code'))
->size($this->resolveSize())
->margin($this->resolveMargin())
->writer($this->resolveWriter())
->errorCorrectionLevel($this->resolveErrorCorrection())
->roundBlockSizeMode($this->resolveRoundBlockSize())
->build();
}

protected function resolveSize(): int
{
$size = config('urlhub.qrcode_size');

if ($size < self::MIN_SIZE) {
return self::MIN_SIZE;
}

return $size > self::MAX_SIZE ? self::MAX_SIZE : $size;
}

protected function resolveMargin(): int
{
$margin = config('urlhub.qrcode_margin');
$intMargin = (int) $margin;

if ($margin !== (string) $intMargin) {
return 0;
}

return $intMargin < 0 ? 0 : $intMargin;
}

/**
* @return \Endroid\QrCode\Writer\WriterInterface
*/
protected function resolveWriter()
{
$qFormat = self::normalizeValue(config('urlhub.qrcode_format'));
$containSupportedFormat = collect(self::SUPPORTED_FORMAT)
->containsStrict($qFormat);
$format = $containSupportedFormat ? $qFormat : self::FORMAT;

return match ($format) {
'svg' => new \Endroid\QrCode\Writer\SvgWriter,
default => new \Endroid\QrCode\Writer\PngWriter,
};
}

/**
* @return \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelInterface
*/
protected function resolveErrorCorrection()
{
$level = self::normalizeValue(config('urlhub.qrcode_error_correction'));

return match ($level) {
'h' => new \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh,
'q' => new \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile,
'm' => new \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium,
default => new \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow, // 'l'
};
}

/**
* @return \Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeInterface
*/
protected function resolveRoundBlockSize()
{
$isRounded = config('urlhub.qrcode_round_block_size');

if (! $isRounded) {
return new \Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeNone;
}

return new \Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
}

protected function normalizeValue(string $param): string
{
return strtolower(trim($param));
}
}
10 changes: 2 additions & 8 deletions app/Http/Controllers/UrlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Controllers;

use App\Actions\QrCode;
use App\Http\Requests\StoreUrl;
use App\Models\Url;
use Illuminate\Support\Facades\Auth;
Expand Down Expand Up @@ -42,14 +43,7 @@ public function showShortenedUrlDetails($key)
$url = Url::with('visit')->whereKeyword($key)->firstOrFail();

if (config('urlhub.qrcode')) {
$qrCode = \Endroid\QrCode\Builder\Builder::create()
->data($url->short_url)
->size(170)
->labelText('Scan QR Code')
->errorCorrectionLevel(
new \Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh
)
->build();
$qrCode = (new QrCode)->process($url->short_url);

return view('frontend.short', compact(['qrCode']), ['url' => $url]);
}
Expand Down
55 changes: 53 additions & 2 deletions config/urlhub.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@

'web_title' => env('UH_WEB_TITLE', true),

'qrcode' => env('UH_QRCODE', true),

/*
|--------------------------------------------------------------------------
| Visiting
Expand Down Expand Up @@ -105,4 +103,57 @@
* status is 301. Default values is 90.
*/
'redirect_cache_lifetime' => env('UH_REDIRECT_CACHE_LIFETIME', 90),

/*
|--------------------------------------------------------------------------
| QR codes
|--------------------------------------------------------------------------
*/

/**
* Type: bool
* Accepted values: true or false
*/
'qrcode' => env('QRCODE', true),

/**
* Determines the width/height in pixels.
*
* Type: int
* Accepted values: 50 to 1000
*/
'qrcode_size' => env('QRCODE_SIZE', 170),

/**
* The space in pixels between the QR code itself and the border of the image.
*
* Type: int (positive)
*/
'qrcode_margin' => env('QRCODE_MARGIN', 0),

/**
* Type: string
* Accepted values: png or svg
*/
'qrcode_format' => env('QRCODE_FORMAT', 'png'),

/**
* Determine error correction levels to restore data if the code is dirty or
* damaged.
*
* Type: string
* Accepted values: l, m, q, h
*
* See https://www.qrcode.com/en/about/error_correction.html for more information.
*/
'qrcode_error_correction' => env('QRCODE_ERROR_CORRECTION', 'm'),

/**
* Tells if the block size should be rounded, making the QR code more readable,
* but potentially adding some extra margin as a side effect.
*
* Type: bool
* Accepted values: true or false
*/
'qrcode_round_block_size' => env('QRCODE_ROUND_BLOCK_SIZE', true),
];
52 changes: 52 additions & 0 deletions tests/Unit/Actions/QrCodeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Tests\Unit\Actions;

use App\Actions\QrCode;
use Endroid\QrCode\Writer\Result\ResultInterface;
use Tests\TestCase;

class QrCodeTest extends TestCase
{
/**
* @test
* @group u-actions
*/
public function qrCode()
{
$qrCode = (new QrCode)->process('foo');

$this->assertInstanceOf(ResultInterface::class, $qrCode);
$this->assertIsString($qrCode->getDataUri());
}

/**
* @test
* @group u-actions
*/
public function sizeMin()
{
$size = QrCode::MIN_SIZE - 1;
config(['urlhub.qrcode_size' => $size]);

$image = imagecreatefromstring((new QrCode)->process('foo')->getString());

$this->assertNotSame($size, (int) imagesx($image));
$this->assertSame(QrCode::MIN_SIZE, imagesx($image));
}

/**
* @test
* @group u-actions
*/
public function sizeMax()
{
$size = QrCode::MAX_SIZE + 1;
config(['urlhub.qrcode_size' => $size]);

$image = imagecreatefromstring((new QrCode)->process('foo')->getString());

$this->assertNotSame($size, imagesx($image));
$this->assertSame(QrCode::MAX_SIZE, imagesx($image));
}
}

0 comments on commit cc892f0

Please sign in to comment.