This package provides handy utilities for Laravel applications.
| Branch | Status | Code Coverage |
|---|---|---|
| Main | ||
| Staging | ||
| Dev |
- Installation
- Run Tests Locally
- Service Providers
- Middleware
- Helper Methods
- Helper Macros
- Blade Components
- Casts
- Traits
- Rules
- Services
- Notifications
- Exception Handling
- Logging
- Localization
- Publishing Assets
You can install the package via Composer:
composer require internetguru/laravel-commonThe CommonServiceProvider is auto-discovered via composer.json extra.laravel.providers. No manual registration is needed for the core provider.
To run the tests manually, you can use the following command:
./test.shAuto-registered via package discovery. It provides:
- Custom exception handler registration.
- Loading of package routes, views (
ig-commonnamespace), translations, and Blade components (ignamespace). - Registration of the Livewire Messages component.
- Listener for mail logging (
NotificationSentevent). - Registration of the
ulid32validation rule. - Registration of all helper macros (String, Number, Carbon).
- Automatic registration of all middleware into the
webmiddleware group. - Queue connection safety check — throws an exception at boot if the queue connection is set to
sync(except during unit tests).
Intercepts all write database queries and throws
DbReadOnlyExceptionwhenconfig('app.readonly')istrue.
Read-only operations (SELECT, SHOW, DESCRIBE, EXPLAIN, PRAGMA) are always allowed. Queries targeting sessions, token_auths, mail_logs, users, and socialites tables are whitelisted.
To use, register the provider in your config/app.php:
'providers' => ServiceProvider::defaultProviders()->merge([
InternetGuru\LaravelCommon\ReadOnlyServiceProvider::class,
])->toArray(),Then set readonly to true in config/app.php (or via environment variable) to activate read-only mode.
Logs missing translations and translation variables in the current language. Throws an exception when not in production environment. In debug mode, checks all available languages.
- Logs warning when a translation key is missing or a variable required in a translation string is not provided.
- Checks all languages in debug mode from all available locales.
- Throws exception
InternetGuru\LaravelCommon\Exceptions\TranslatorExceptioninstead of logging when the app is not in production mode.
To use the provider, replace the default TranslationServiceProvider in config/app.php:
use Illuminate\Support\ServiceProvider;
'providers' => ServiceProvider::defaultProviders()->replace([
Illuminate\Translation\TranslationServiceProvider::class => InternetGuru\LaravelCommon\TranslationServiceProvider::class,
])->toArray(),All middleware listed below is automatically registered in the web middleware group by the CommonServiceProvider. No manual registration is needed.
To bypass a specific middleware on a route, use the withoutMiddleware method:
Route::get('/example', ExampleController::class)
->withoutMiddleware(\InternetGuru\LaravelCommon\Http\Middleware\PreventDuplicateSubmissions::class);Checks for invalid POST parameter names containing dots
".". Helps prevent issues with Laravel's input handling. Throws an exception in non-production environments and logs a warning in production.
Example:
-
When a POST request contains parameter names with dots:
POST /submit-form Content-Type: application/x-www-form-urlencoded username=johndoe&user.email=johndoe@example.com
-
In Non-Production Environments: The middleware will throw an HTTP 400 exception:
Invalid POST parameter names containing dots: user.email -
In Production Environment: The middleware will log a warning:
[WARNING] Invalid POST parameter names containing dots: user.email
Automatically injects the Umami analytics tracking script into HTML responses when
UMAMI_WEBSITE_IDis set.
The script is injected before the closing </head> tag. Set the following environment variables to enable:
| Variable | Description | Default |
|---|---|---|
UMAMI_WEBSITE_ID |
Your Umami website ID (required to enable tracking). | '' (disabled) |
UMAMI_SRC |
URL to the Umami tracking script. | https://umami.internetguru.io/script.js |
Example .env:
UMAMI_WEBSITE_ID=0d38f931-afdc-4a99-a913-5c601fc95629The injected script:
<script defer src="https://umami.internetguru.io/script.js" data-website-id="0d38f931-afdc-4a99-a913-5c601fc95629"></script>Prevents duplicate POST form submissions by caching a hashed request fingerprint (IP + path + input minus reCAPTCHA) for 1 minute. Livewire update requests are excluded.
When a duplicate submission is detected, the user is redirected back with input and an error message.
Tracks the current and previous page URLs in the session for GET requests. Used internally by the exception handler to redirect users back to meaningful pages on errors.
Ignores AJAX requests and image (img/*) requests. Prevents tracking the same URL consecutively.
Detects the user's timezone via IP geolocation and stores it in the session as
display_timezone.
Uses the GeolocationService to resolve the IP address. Falls back to config('geoip.default_location.timezone') on failure. Resolves only once per session.
Extends Laravel's CSRF verification with HMAC-based request signature verification. Requests containing a valid
X-SignatureandX-Timestampheader pair bypass CSRF checks. Livewire routes are also excluded by default.
The signature is validated using the app key with a 60-second freshness window.
The
Helpersclass provides useful static methods for Laravel applications.
Configuration and example usage:
-
Add the following lines to
config/app.php:use Illuminate\Support\Facades\Facade; 'aliases' => Facade::defaultAliases()->merge([ 'Helpers' => InternetGuru\LaravelCommon\Support\Helpers::class, ])->toArray(),
-
Use
Helpersclass methods in your application:<meta name="generator" content="{{ Helpers::getAppInfo() }}"/>
Available methods:
| Method | Description |
|---|---|
getAppInfoArray() |
Returns app name, environment, version, git branch, and commit as an array. |
getAppInfo() |
Returns app info as a single string. |
parseUrlPath($homeRoute, $skipFirst) |
Parses the current URL path into breadcrumb segments with translations. |
createTitle($separator, $homeRoute) |
Generates a page title from breadcrumb segments (reversed, separated). |
getEmailClientLink() |
Returns a link to the Mailpit inbox when using Mailpit mailer. |
verifyRequestSignature(Request $request) |
Verifies HMAC-SHA256 request signature (X-Signature + X-Timestamp headers). |
For full implementation details, see the Helpers class.
The package registers a set of useful macros for
Str,Carbon, andNumber. See macros.php for the complete list.
| Macro | Description |
|---|---|
Str::ref($length) |
Generates a random alphanumeric reference code (excludes ambiguous characters i, l, o, 0, 1, u). Starts with a letter and contains at least one digit. |
| Macro | Description |
|---|---|
Number::currencyForHumans($number, $in, $precision) |
Formats a number as a locale-aware currency string. Returns the currency symbol if no number is provided. |
Number::formatCurrencyToInput($number, $in, $precision, $inputTemplate) |
Formats a number for use inside an input field with a currency symbol. |
| Macro | Description |
|---|---|
$date->dateForHumans() |
Locale-aware date (L format). |
$date->dateTimeForHumans() |
Locale-aware date and time (L LT format). |
$date->myDiffForHumans() |
Human-readable time difference with "just now" for <60 seconds and "1 year" normalization. |
$date->timeForHumans() |
Clean time format (removes leading zeros and :00). |
$date->randomWorkTime($from, $to) |
Sets a random time during work hours (default 9–17). |
Example usage:
use Carbon\Carbon;
use Illuminate\Support\Facades\Number;
use Illuminate\Support\Str;
echo Str::ref(6);
// Output: "k3mhpq"
Number::useCurrency('USD');
echo Number::currencyForHumans(1234);
// Output (en_US locale): $1,234
echo Number::currencyForHumans();
// Output (en_US locale): $
echo Number::currencyForHumans(1234.567, in: 'EUR', precision: 2);
// Output (en_US locale): €1,234.57
$date = Carbon::parse('2023-12-31');
echo $date->dateForHumans();
// Output (en_US locale): 12/31/2023
$dateTime = Carbon::parse('2023-12-31 18:30:00');
echo $dateTime->dateTimeForHumans();
// Output (en_US locale): 12/31/2023 6:30 PMAll Blade components are registered under the ig namespace and can be used with <x-ig::component-name />.
Renders breadcrumb navigation based on routes matching the current URL segments. Supports translations with short and long labels, custom divider, and segment skipping.
Key Features:
-
Customizable Divider – Allows a custom divider symbol between breadcrumb items.
-
Short and Long Labels – Using
trans_choiceif available shows n-th right translation based on the item position. -
Segment Skipping – Skips a specified number of URL segments. Useful for nested routes or routes with prefixes (e.g. language).
-
Skip Prefixes – Routes whose URI starts with a configured prefix are treated as error pages (no breadcrumb generated), preventing missing translation warnings. Configured via
breadcrumb_skip_prefixesinconfig/ig-common.php:'breadcrumb_skip_prefixes' => [ '_debugbar', '_ignition', 'livewire', 'storage', 'telescope', 'horizon', ],
Usage:
<!-- By default, this will generate breadcrumb items based on the current URL path. -->
<x-ig::breadcrumb/>
<!-- You can change the divider symbol by setting the divider attribute -->
<x-ig::breadcrumb divider="|" />
<!-- If you need to skip certain segments of the URL (e.g., a language prefix), use the skipFirst attribute -->
<x-ig::breadcrumb :skipFirst="1" />Example:
- Assuming you have the following routes defined:
<?php Route::get('/', function () { // ... })->name('home'); Route::get('/products', function () { // ... })->name('products.index'); Route::get('/products/{product}', function ($product) { // ... })->name('products.show');
- And your translation files (
resources/lang/en/navig.php) include:<?php return [ 'home' => 'Long Application Name|LAN', 'products.index' => 'All Products|Products', 'products.show' => 'Product Details', ];
- When you visit the
/products/123URL, the short translation will be used for thehomeandproducts.indexroutes.LAN > Products > Product Details - When you visit the
/productsURL, the short label will be used for thehomeroute.LAN > All Products - When you visit the
/URL, the long label will be used for thehomeroute.Long Application Name
Renders system temporary success messages and persistent error messages in different colors, with a close button. Powered by Livewire.
The component automatically picks up session success and errors data. You can also send messages dynamically via Livewire events.
Include the component in your Blade template:
<livewire:ig-messages />Dispatching messages from other Livewire components:
$this->dispatch('ig-message', type: 'success', message: 'Item saved!');
$this->dispatch('ig-message', type: 'danger', message: 'Something went wrong.');The package provides a set of Blade components for forms and various input types.
Notes:
- The Google reCAPTCHA V3 service is enabled by default. To disable it, set the
recaptchaattribute tofalse.
Complete example:
<x-ig::form action="route('test')" :recaptcha="false">
<x-ig::input type="text" name="name" required>Name</x-ig::input>
<x-ig::input type="option" name="simple-options" :value="['a', 'b', 'c']">Simple Options</x-ig::input>
<x-ig::input type="option" name="advanced-options" :value="[
['id' => '1', 'value' => 'User 1' ],
['id' => '2', 'value' => 'User 2' ],
['id' => '3', 'value' => 'User 3' ],
]">Advanced Options</x-ig::input>
<x-ig::input type="checkbox" name="checkbox" value="1">Checkbox</x-ig::input>
<x-ig::input type="radio" name="radio" value="1">Radio</x-ig::input>
<x-ig::input type="textarea" name="description">Description</x-ig::input>
<x-ig::submit>Submit Form</x-ig::submit>
</x-ig::form>Renders a language switcher as a list of links with the current language highlighted.
<x-ig::lang-switch />Renders a print button that triggers the browser's print dialog.
<x-ig::print-button />Renders a copyright footer with provider information and year range.
<x-ig::footer-copy />Renders a demo mode warning banner informing users that displayed information is illustrative and may reset.
<x-ig::demo-info />Renders an informational banner indicating the application is in read-only mode and editing is disabled.
<x-ig::read-only-mode-info />Renders a technical support email link with pre-filled subject and diagnostic data.
<x-ig::email-feedback />Provides an Alpine.js
editabledata component for inline editing functionality.
<x-ig::editable />Casts a string to a
CarbonIntervaland back. UsesCarbonInterval::fromString()to parse andforHumans()(in English locale) to serialize.
use Illuminate\Database\Eloquent\Model;
use InternetGuru\LaravelCommon\Casts\CarbonIntervalCast;
class Task extends Model
{
protected $casts = [
'duration' => CarbonIntervalCast::class,
];
}Provides ULID (Crockford Base32) utility methods for Eloquent models. Generates 26-character Base32-encoded UUIDs and adds human-readable formatting, URL generation, and link rendering.
use InternetGuru\LaravelCommon\Traits\Ulid32;
class Order extends Model
{
use Ulid32;
}Available methods:
| Method | Description |
|---|---|
$model->ulidForHumans() |
Formats the ULID with dashes (e.g. 01JM-ABCDEF-GHIJKL-MNOPQR-STUV). |
$model->shortUlidForHumans() |
Returns the last 7 characters of the formatted ULID. |
$model->ulidUrl($usp) |
Generates a URL to the model's show route. |
$model->ulidLink($content) |
Renders an HTML link to the model. |
Model::generateBase32Uuid() |
Generates a new Crockford Base32-encoded UUID (26 characters). |
Validates that a value is a valid 26-character Crockford Base32 ULID (no
I,L,O,Ucharacters).
Can be used as a class-based rule or via the globally registered ulid32 rule:
// Class-based
use InternetGuru\LaravelCommon\Rules\Ulid32;
$request->validate([
'code' => ['required', new Ulid32],
]);
// String-based (registered globally by CommonServiceProvider)
$request->validate([
'code' => 'required|ulid32',
]);Resolves an IP address to a geographic location using the
torann/geoippackage. Results are cached and rate-limited (5 lookups per 60 seconds).
use InternetGuru\LaravelCommon\Services\GeolocationService;
$geoService = app(GeolocationService::class);
$location = $geoService->getLocation('8.8.8.8');
echo $location->timezone; // "America/Chicago"
echo $location->country; // "US"Throws GeolocationServiceException on failure or rate limit.
Abstract queued notification class that captures request context (IP, timezone, user ID, URL) at creation time and sends via the
Features:
- Queued with 10 retries and 2-minute backoff.
- Retry middleware via
LogNotificationFailure— logs warnings on transient failures. - Captures the sender's IP, timezone (via geolocation), authenticated user ID, and current page URL.
- Logs permanently failed notifications.
Extend this class to create your own notifications:
use InternetGuru\LaravelCommon\Notifications\BaseNotification;
class OrderConfirmation extends BaseNotification
{
public function toMail(object $notifiable): MailMessage
{
return (new \InternetGuru\LaravelCommon\Mail\MailMessage)
->setExtraMailData($this->getExtraMailData())
->subject('Order Confirmed')
->view(['html' => 'emails.order-confirmed', 'text' => 'emails.order-confirmed-text']);
}
}Extends Laravel's
MailMessagewith automatic reference number generation, no-reply detection, and extra mail data injection.
Features:
- Appends a random reference code (
Ref XXXXX) to every subject line for tracking. - Automatically detects no-reply addresses and adds a "replies not delivered" note.
- Supports arbitrary extra data (
setExtraMailData) passed to email views (IP, timezone, user ID, etc.).
All sent mail notifications are automatically logged to the
mail_logsdatabase table via theLogSentNotificationlistener.
Publish the migration to create the mail_logs table:
php artisan vendor:publish --tag=ig-common:migrations
php artisan migrateLogged fields: to, replyto, subject, body, created_at, updated_at.
The package registers a custom exception handler that provides user-friendly error pages and JSON responses for common HTTP errors (401, 402, 403, 404, 419, 429, 500, 503).
Features:
- Read-only mode:
DbReadOnlyExceptionreturns a 503 response (JSON or redirect with error). - Connection errors:
ConnectExceptionreturns a friendly error message. - Rate limiting (429) and session expiration (419): Handled with translated messages.
- Debug mode: Uses
dd()for detailed exception inspection. - JSON support: Returns JSON responses when the request expects JSON.
- Redirects to the previously tracked page (via
SetPrevPagemiddleware) on error.
Custom error views are included for standard HTTP status codes. The error pages use the ig-common::layouts.base layout.
A custom Monolog logger that writes JSON-formatted daily rotating log files enriched with request context.
Each log entry includes:
- User info: ID, name, email (if authenticated).
- Request input: All inputs except
password,_token, andg-recaptcha-response. Livewire snapshot data is stripped. - Session ID.
- App info: Git branch and commit.
- Request info: URL, IP, HTTP method, server, referrer, user agent.
Configuration in config/logging.php:
'channels' => [
'json_daily' => [
'driver' => 'custom',
'via' => InternetGuru\LaravelCommon\Logging\JsonDailyLogger::class,
'path' => storage_path('logs/app.log'),
'days' => 14,
'level' => 'debug',
],
],The package includes translations in English (en), Czech (cs), and Danish (da) for:
- Error pages (
errors.php) – HTTP status messages and descriptions. - Layout strings (
layouts.php) – Footer, email templates, support links, and UI labels. - System messages (
messages.php) – Validation messages, email labels, and demo mode warnings. - Navigation (
navig.php) – Breadcrumb labels for routes.
You can publish package assets to customize them:
# Publish database migrations
php artisan vendor:publish --tag=ig-common:migrations
# Publish configuration
php artisan vendor:publish --tag=ig-common:config
# Publish views for customization
php artisan vendor:publish --tag=ig-common:views
# Publish language files
php artisan vendor:publish --tag=ig-common:langCopyright © 2026 Internet Guru
This software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license.
Disclaimer: This software is provided "as is", without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages or other liability.
The standard CC BY-NC-SA license prohibits commercial use. If you wish to use this software in a commercial environment or product, we offer flexible commercial licenses tailored to:
- Your company size.
- The nature of your project.
- Your specific integration needs.
Note: In many instances (especially for startups or small-scale tools), this may result in no fees being charged at all. Please contact us to obtain written permission or a commercial agreement.
Contact for Licensing: info@internetguru.io
Are you looking to get the most out of this project? We are available for:
- Custom Development: Tailoring the software to your specific requirements.
- Integration & Support: Helping your team implement and maintain the solution.
- Training & Workshops: Seminars and hands-on workshops for your developers.
Reach out to us at info@internetguru.io — we are more than happy to assist you!