laragear/alerts

Set multiple alerts from your backend, render them in the frontend.

v2.0.0 2024-03-07 02:29 UTC

This package is auto-updated.

Last update: 2024-10-13 07:47:44 UTC


README

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

Set multiple alerts from your backend, render them in the frontend with any HTML.

alert('This is awesome! 😍', 'success')
<div class="alert alert-success" role="alert">
    This is awesome! 😍
</div>

Become a sponsor

Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can spread the word!

Requirements

  • Laravel 10 or later

Installation

You can install the package via composer:

composer require laragear/alerts

If you don't have anything to start with in your frontend, you can use Laravel Jetstream, or go the classic way and use Bootstrap, Bulma.io, UI kit, TailwindCSS and INK, among many others.

Usage

This package allows you to set a list of Alerts in your application and render them in the frontend in just a few minutes.

The default renderer uses Bootstrap 5 styles to transform each alert into alerts. If you're using Tailwind CSS, you can use the included Tailwind renderer by changing the configuration. Alternatively, you may create your own renderer for your particular framework.

Quickstart

To set an Alert in your frontend, you can use the alert() helper for shorter syntax, or the Alert Facade, whatever is your preference. A good place to use them is before sending a response to the browser, like in your HTTP Controllers.

If you're sending a redirect, the alerts will be magically flashed so the next request can show them.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    /**
     * Update the Article 
     * 
     * @param \Illuminate\Http\Request $request
     * @param \App\Article $article
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Article $article)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'body' => 'required|string'
        ]);
        
        $article->fill($request)->save();
        
        alert('Your article has been updated!', 'success');
        
        return redirect()->action('ArticleController@edit', $article);
    }
}

The alert() helper accepts the text message and the types of the alert. In the above example, we created a simple "success" alert.

To render the alerts in the frontend, use the <x-alerts-container /> Blade component which will take care of the magic, anywhere you want to put it.

<div class="header">
    <h1>Welcome to my site</h1>
    <x-alerts-container />
</div>

If there is at least one Alert to be rendered, the above will be transformed into proper HTML:

<div class="header">
    <h1>Welcome to my site</h1>
    <div class="alerts">
        <div class="alert alert-success" role="alert">
            Your article has been updated!
        </div>
    </div>
</div>

Alternatively, you can use the type($message) syntax right from the Alert facade or the alert() helper.

use Laragear\Alerts\Facades\Alert;

alert()->success('This was a triumph!');

Alert::info("I'm making a note here: huge success.");
<div class="alert alert-success" role="alert">
    This was a triumph!
</div>

<div class="alert alert-info" role="alert">
    I'm making a note here: huge success.
</div>

Message

You can use the type($message) syntax, or go for the classic route and add the text inside the Alert using the message() method.

use Laragear\Alerts\Facades\Alert;

Alert::success('You are gonna love this! 😍');

Alert::message('We will email you 📨 a copy!')->types('info');
<div class="alert alert-success" role="alert">
    You are gonna love this! 😍
</div>

<div class="alert alert-info" role="alert">
    We will email you 📨 a copy!
</div>

Important

By default, the message() method escapes the text. If you want to send a raw message, you should use raw().

Raw message

Since the message() method escapes the text for safety, you can use the raw() method to output a string verbatim. This allows you to use HTML for personalized messages, like adding some style, links, or even scripts.

use Laragear\Alerts\Facades\Alert;

Alert::warning('This is <strong>FUBAR</strong>.');

Alert::raw('But this is <strong>important</strong>.')->types('warning');
<div class="alert alert-warning" role="alert">
    This is &lt;strong&gt;FUBAR&lt;/strong&gt;.
</div>

<div class="alert alert-warning" role="alert">
    But this is <strong>important</strong>.
</div>

Warning: Don't use raw() to show user-generated content. YOU HAVE BEEN WARNED.

Alert Type

You can set an alert "type" by its name by just using it as a method name. The types() method is preferred if you need to set more than one.

use Laragear\Alerts\Facades\Alert;

Alert::primary('Your message was sent!');

Alert::message('There is an unread message.')->types('info', 'cool');
<div class="alert alert-primary" role="alert">
    Your message was sent!
</div>

<div class="alert alert-info cool" role="alert">
    There is an unread message.
</div>

The types are like keywords that the underlying Renderer will use to transform the alert into proper HTML code.

Note

The default Bootstrap Renderer will set each unrecognized type as an additional CSS class.

Localization

To gracefully localize messages on the fly, use the trans() method, which is a mirror of the __() helper.

use Laragear\Alerts\Facades\Alert;

Alert::trans('email.changed', ['email' => $email], 'es')->success();
<div class="alert alert-success" role="alert">
    ¡Tu email ha sido cambiado a "margarita@madrid.cl" con éxito!
</div>

You can also use transChoice() with the same parameters of trans_choice().

use Laragear\Alerts\Facades\Alert;

Alert::transChoice('messages.apples', 1)->success();

Alert::transChoice('messages.apples', 10)->success();
<div class="alert alert-success" role="alert">
    ¡Ahora tienes 1 manzana! 
</div>

<div class="alert alert-success" role="alert">
    ¡Ahora tienes 10 manzanas! 
</div>

Dismiss

Most of the frontend frameworks have alerts or notifications that can be dismissible, but require adding more than a single class to allow for interactivity.

You can set an alert to be dismissible using dismiss(), signaling the Renderer to make the modification necessary to be dismissible.

use Laragear\Alerts\Facades\Alert;

Alert::success('You can disregard this')->dismiss();

If you want to change your mind, you can use dismiss(false):

use Laragear\Alerts\Facades\Alert;

Alert::success('You can disregard this')->dismiss(false);

How the dismissible alert is transformed into code will depend on the renderer itself. The default Bootstrap renderer adds the proper CSS classes and a dismiss button automatically.

<div class="alert alert-success alert-dismissible fade show" role="alert">
  You can disregard this
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>

Conditional Alerts

You can also push an Alert if a condition evaluates to true or false by using when() and unless(), respectively. Further method calls will be sent to the void.

use Illuminate\Support\Facades\Auth;
use Laragear\Alerts\Facades\Alert;

Alert::when(Auth::check())
    ->success('You are authenticated');

Alert::unless(Auth::user()->mailbox()->isNotEmpty())
       ->warning('You have messages in your inbox');

Persistent Alerts

Alerts only last for the actual response being sent. On redirects, these are flashed into the session so these are available on the next request (the redirection target).

To make any alert persistent you can use the persistAs() method with a key to identify the alert.

use Laragear\Alerts\Facades\Alert;

Alert::danger('Your disk size is almost full')->persistAs('disk.full');

Note

Setting a persistent alert replaces any previous set with the same key.

Once you're done, you can delete the persistent Alert using abandon() method directly from the helper using the key of the persisted Alert. For example, we can abandon the previous alert if the disk is no longer full.

use Laragear\Alerts\Facades\Alert;

if ($disk->notFull()) {
    Alert::abandon('disk.full');
}

Links

Setting up links for an alert doesn't have to be cumbersome. You can easily replace a string between curly braces in your message for a link using to(), route(), action(), and away().

use Laragear\Alerts\Facades\Alert;

Alert::success('Remember, you can follow your order in your {dashboard}.')
    ->to('dashboard', '/dashboard/orders')

Links can also work over translated messages, as long these have a word in curly braces.

use Laragear\Alerts\Facades\Alert;

// You can see your package status in the {tracking}.
Alert::trans('user.dashboard.tracking.order', ['order' => $order->tracking_number])
    ->types('success')
    ->route('tracking', 'orders.tracking', ['order' => 45])

If you have more than one link, you can chain multiple links to a message.

use Laragear\Alerts\Facades\Alert;

Alert::trans('Your {product} is contained in this {order}.')
    ->types('success')
    ->action('product', [\App\Http\Controllers\Product::class, 'show'], ['product' => 180])
    ->to('order', '/dashboard/order/45')

Important

Links strings are case-sensitive, and replaces all occurrences of the same string. You can create your own Renderer if this is not desired.

Tags

Sometimes you may have more than one place in your site to place Alerts, like one for global alerts and other for small user alerts. Tags can work to filter which Alerts you want to render.

You can set the tags of the Alert using tag().

use Laragear\Alerts\Facades\Alert;

Alert::warning('Maintenance is scheduled for tomorrow')
    ->tag('user', 'admin')

Using the Alerts directive, you can filter the Alerts to render by the tag names using the :tags slot.

<!-- Render the Alerts in the default list -->
<x-alerts-container :tags="'default'" />

<!-- Here we will render alerts for users and admins. -->
<x-alerts-container :tags="['user', 'admin']" />

Configuration

Alerts will work out-of-the-box with some common defaults, but if you need a better approach for your particular application, you can configure some parameters. First, publish the configuration file.

php artisan vendor:publish --provider="Laragear\Alerts\AlertsServiceProvider" --tag="config"

Let's examine the configuration array, which is quite simple:

<?php 

return [
    'renderer' => 'bootstrap',
    'key' => '_alerts',
    'tags' => 'default',
];

Renderer

return [
    'renderer' => 'bootstrap',
];

This picks the Renderer to use when transforming Alerts metadata into HTML.

This package ships with Bootstrap 5 and Tailwind CSS renderers, but you can create your own for other frontend frameworks like Bulma.io, UI kit, INK, or even your own custom frontend framework.

Session Key

return [
    'key' => '_alerts',
];

When alerts are flashed or persisted, these are stored in the Session by a given key, which is _alerts by default. If you're using this key name for other things, you may want to change it.

This key is also used when sending JSON alerts.

Default tag list

return [
    'tags' => ['user', 'admin'],
];

This holds the default tag list to inject to all Alerts when created. You can leave this alone if you're not using tags.

Renderers

A Renderer takes a collection of Alerts and transforms each into an HTML string. This makes swapping a frontend framework easier, and allows greater flexibility when rendering HTML.

Creating a custom renderer

You can create your own using the Renderer contract, and registering it into the RendererManager in your AppServiceProvider. You can use the BootstrapRenderer as a starting point to create your own.

<?php

use Laragear\Alerts\RendererManager;
use App\Alerts\Renderers\TailwindRenderer;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot(RendererManager $alert)
{
    $alert->extend('tailwind', function ($app) {
        return new TailwindRenderer($app->make('blade.compiler');
    });
}

Then, in your config file, set the renderer to the one you have registered.

// config/alerts.php

return [
    'renderer' => 'tailwind'
    
    // ...
];

When you issue an alert, the alert will be rendered using your own custom renderer.

use Laragear\Alerts\Facades\Alert;

Alert::default('Popping colors!');
<div class="bg-slate-100 rounded-xl p-8 dark:bg-slate-800">
    Popping colors!
</div> 

Alerts Container HTML

When the Renderer receives Alerts to render, it will call a "container" view which will render all the Alerts by using a loop.

For example, the included BootstrapRenderer calls the laralerts::bootstrap.container.

@if($alerts->isNotEmpty())
    <div class="alerts">
        @each('alerts::bootstrap.alert', $alerts, 'alert')
    </div>
@endif

You may be using another frontend framework different from Bootstrap 5, or you may want to change the HTML to better suit your application design. In any case, you can override the View files in views/vendor/alerts:

  • container.blade.php: The HTML that contains all the Alerts.
  • alert.blade.php: The HTML for a single Alert.

The variables the alert.blade.php view receives are set from by Renderer. For the case of the included Bootstrap renderer, these are:

  • $alert->message: The message to show inside the Alert.
  • $alert->classes: The CSS classes to incorporate into the Alert.
  • $alert->dismissible: A boolean that sets the alert as dismissible or not.

As you're suspecting, you can publish the views and override them to suit your needs.

php artisan vendor:publish --provider="Laragear\Alerts\AlertsServiceProvider" --tag="views"

JSON Alerts

Receiving JSON Alerts

Sometimes your application may receive a JSON Alert from an external service using this package. You can quickly add this JSON as an Alert to your application using the fromJson() method.

{
    "alert": {
        "message": "Email delivered",
        "types": [
            "success",
            "important"
        ],
        "dismissible": false
    }
}
use Laragear\Alerts\Facades\Alert;

Alert::fromJson($json);

This will work as long the JSON has the message key with the text to include inside the Alert. Additionally, you can add the types and dismiss keys to add an Alert, with the possibility of override them afterward.

Warning

The message from JSON is set raw.

Sending JSON Alerts

This library has a convenient way to add Alerts into your JSON Responses. This can be very useful to add your alerts to each response being sent to the browser, like combining this package with Laravel Jetstream.

Just simply add the alerts.json middleware into your api routes or, if you're using Laravel Jetstream or similar, as a global middleware.

When you return a JsonResponse to the browser, the middleware will append the alert as JSON using the same session key defined in the configuration, which is _alerts by default. It also accepts the key parameter to use as an alternative, compatible with dot notation.

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\UserController;

Route::prefix('api')
    ->middleware('alerts.json:_status.alerts')
    ->controller(UserController::class, function () {
    
        Route::post('user/create', 'create');
        Route::post('user/{user}/update', 'update');
        
    });

When you receive a JSON Response, you will see the alerts appended to whichever key you issued. Using the above example, we should see the alerts key under the _status key:

{
    "resource": "users",
    "url": "/v1/users",
    "_status": {
        "timestamp":  "2019-06-05T03:47:24Z",
        "action" : "created",
        "id": 648,
        "alerts": [
            {
                "message": "The user has been created!",
                "types" : ["success", "important"],
                "dismiss": true
            }
        ]
    }
}

Warning

If your key is already present in the JSON response, the key will be overwritten.

Testing

To test if alerts were generated, you can use Alert::fake(), which works like any other faked services. It returns a fake Alert Bag that holds a copy of all alerts generated, which exposes some convenient assertion methods.

use \Laragear\Alerts\Facades\Alert;

public function test_alert_sent()
{
    $alert = Alert::fake();
    
    $this->post('/comment', ['body' => 'cool'])->assertOk();
    
    $alert->assertHasOne();
}

The following assertions are available:

Asserting specific alerts

The fake Alert bag allows building conditions for the existence (or nonexistence) of alerts with specific properties, by using assertAlert().

Once you build your conditions, you can use exists() to check if any alert matches, or missing() to check if no alert should match.

use \Laragear\Alerts\Facades\Alert;

$alert = Alert::fake();

$alert->assertAlert()->withMessage('Hello world!')->exists();

$alert->assertAlert()->withTypes('danger')->dismissible()->missing();

Alternatively, you can use count() if you expect a specific number of alerts to match the given conditions, or unique() for matching only one alert.

use \Laragear\Alerts\Facades\Alert;

$bag = Alert::fake();

$bag->assertAlert()->persisted()->count(2);

$bag->assertAlert()->notDismissible()->withTag('toast')->unique();

The following conditions are available:

Laravel Octane compatibility

  • The Renderer-related classes are registered as singletons.
  • It's safe to extend the RendererManager at boot time.
  • The Bag is registered as singleton. You shouldn't resolve it at boot time.
  • The Bag contains a stale version of the app config. You shouldn't change the config.
  • There are no static properties written during a request.

There should be no problems using this package with Laravel Octane if you use this package as intended.

Security

If you discover any security related issues, please email darkghosthunter@gmail.com instead of using the issue tracker.

License

This specific package version is licensed under the terms of the MIT License, at time of publishing.

Laravel is a Trademark of Taylor Otwell. Copyright © 2011-2024 Laravel LLC.