Search by

witify / support

tibou-tech

Base classes of the Witify Laravel applications: actions, controllers, resources, query builders, confirmation flow, rich text sanitization and module providers.

Package info

github.com/witify/support

pkg:composer/witify/support

Statistics

Installs: 43

Dependents: 1

Suggesters: 0

Stars: 0

v1.4.1 2026-09-24 20:23 UTC

This package is auto-updated.

Last update: 2026-09-24 22:53:01 UTC


README

Base classes shared by the Witify Laravel applications: the Action contract, the base controller with its confirmation flow, the API resource and query builder bases, the module service provider with its shared-data and user-notification registries, the rich text sanitizer, the notification mail message and database channel, and the IsResource contract that feeds resource_data to the frontend.

Every other witify/* package that needs these classes depends on this one. Applications generated by the Sprintify template consume it instead of their former src/Support copies.

Requirements

PHP 8.2 to 8.4
Laravel 11 and 12
Database any Eloquent connection; DatabaseChannel writes to the application's notifications table

symfony/html-sanitizer is installed with the package.

Installation

composer require witify/support

The service provider is discovered automatically. It binds the two registries (config::sharedData, config::userNotifications), merges the default config and loads the support:: translations.

Publish the config only when a default needs to change, the translations only to reword them:

php artisan vendor:publish --tag="support-config"
php artisan vendor:publish --tag="support-translations"

Tell the package how to sign the mails, in a service provider of the application:

use Witify\Support\Host;

Host::resolveCompanyNameUsing(fn (): ?string => settings()->company_name);

Without a resolver the mails are signed with support.company_name, then app.name.

What the package provides

Class Role
Action\Action handle() contract of the single-purpose actions
Controller\Controller Base controller: user(), rateLimit(), confirmRequest()
Resource\BaseResource JSON resource that appends can: {view, update, delete} from the policies
QueryBuilder\BaseQueryBuilder Eloquent builder base: protect(), search(), dateBetween(), exclude(), deterministic pagination
Search\SearchQuery, Search\BelongsToSearchConstraint, Search\SearchConstraintInterface Keyword search over columns, expressions, callbacks and relations
Exception\BusinessRuleException, ReportableBusinessRuleException, RateLimitException, ShouldntReportOnRequest JSON exceptions (400, 429) and the marker that keeps them out of the error reporter
Http\Confirmation\Actions\ConfirmRequestAction, Http\Confirmation\ConfirmationHttpException, Http\Middlewares\ClearConfirmationFlow Confirmation flow: the first call answers 428, the replay with the confirmation headers runs the action
Model\IsResource, Model\IsResourceTrait Title, subtitle, icon, color and admin path of a model, appended as resource_data
Model\SanitizesHtmlTrait, Html\RichTextSanitizer Sanitizes the rich text fields of a model on every save against the html-sanitizer allow-list
Mail\MailMessage Notification mail with the greeting and the company signature
NotificationChannels\DatabaseChannel, Events\NewNotification Database channel writing the wide notifications row and broadcasting notifications.created on the user's private channel
ModuleServiceProvider Base provider of the modules: routes, policies, translations, shared data and user notifications
SharedData\SharedData, UserNotifications\UserNotifications The two registries the modules fill and the application reads
Host Resolvers for the values the package takes from the application

Contracts the frontend relies on

These shapes are read by the Vue applications and by the code the Architect generator produces. They do not change inside a major version.

  • resource_data on every IsResource model: title, subtitle, icon, color, admin_to, admin_url, model_type. admin_url is support.admin_url (default /admin) followed by getResourceAdminTo(). color is gray unless the model overrides getResourceColor().
  • can on every BaseResource: view, update, delete, from the policy of the model. Override canDo() to add abilities.
  • search() treats the keyword as a literal (% and _ match themselves), relies on the collation for case-insensitivity and hands a callback the lowercased keyword.
  • The confirmation flow: a 428 response with code: "confirmation_required" and a confirmation object (flow, token, title, message, errorMessage, confirmText, cancelText, requiresPassword). The client replays the same request with X-Confirmation-Flow, X-Confirmation-Token and, when a password is required, X-Confirmation-Password. Register ClearConfirmationFlow on the web middleware group so a completed flow leaves the session. Three wrong passwords lock the action for the user who typed them, and a right one wipes the count.
  • NewNotification broadcasts notifications.created on private-user.{id}. The channel name comes from support.notifications.channel; set support.notifications.broadcast to false in an application without a WebSocket server.

Shared data

Modules put what the frontend needs into the registry from their provider:

class OrderServiceProvider extends ModuleServiceProvider
{
    public function name(): string
    {
        return 'orders';
    }

    public function sharedData(): array
    {
        return ['statuses' => OrderStatus::all()];
    }
}

The application collects the registry into its window.Laravel payload, together with its own values:

$data = array_merge(app(SupportServiceProvider::SHARED_DATA)->all(), [
    'app' => [...],
    'models' => [...],
]);

The registry no longer calls the application's ShareDataToClient: the application merges the two, so the package never imports application code.

Configuration

Key Default Purpose
support.admin_url /admin prefix of resource_data.admin_url
support.company_name null, then app.name signature of the mails when no resolver is set
support.notifications.channel user.{id} private channel of NewNotification
support.notifications.broadcast true broadcast after the database channel stored a notification
html-sanitizer.* the TipTap allow-list of sprintify-ui elements, attributes and schemes kept by RichTextSanitizer

Translations live under the support:: namespace: messages (unauthorized action, too many requests), confirmation (titles, buttons, password throttle) and mail (greeting, regards).

Migrating an application from src/Support

  1. composer require witify/support.
  2. Delete the classes listed above from src/Support and modules/Utils/ModuleServiceProvider.php; keep the application-specific ones (ShareDataToClient, UserPermissions, the middlewares, the rules, ...).
  3. Replace the imports: Support\Action\Action becomes Witify\Support\Action\Action, and so on. Support\Config\ShareData\SharedData becomes Witify\Support\SharedData\SharedData, Support\Config\UserNotifications\* becomes Witify\Support\UserNotifications\*, Modules\Utils\ModuleServiceProvider becomes Witify\Support\ModuleServiceProvider, Modules\Notification\Events\NewNotification becomes Witify\Support\Events\NewNotification.
  4. Remove the config::sharedData and config::userNotifications singletons from the application provider: the package binds them.
  5. Make ShareDataToClient merge app(SupportServiceProvider::SHARED_DATA)->all() and point the layout at it.
  6. Register Host::resolveCompanyNameUsing().
  7. Update the Architect stubs so the generated code imports the package classes.

Controller::user() now returns Illuminate\Contracts\Auth\Authenticatable; annotate or check the type where the application relies on its own User model.

Changelog and upgrades

See the CHANGELOG of the monorepo. Every package shares the same version number.

Contributing

This repository is a read-only split of witify/packages. Open pull requests there, in packages/support.