headlesslaravel / cards
Headless Cards for Laravel
Fund package maintenance!
headlesslaravel
Requires
- php: ^8.0
Requires (Dev)
- nunomaduro/collision: ^5.3
- nunomaduro/larastan: ^0.7.12
- orchestra/testbench: ^6.15
- phpmd/phpmd: ^2.10
- phpunit/phpunit: ^9.3
- spatie/laravel-ray: ^1.23
README
Installation
You can install the package via composer:
composer require headlesslaravel/cards
Make a card class to manage multiple cards of one type:
php artisan make:cards Dashboard
Then add the class to an endpoint:
Route::cards('api/dashboard', Dashboard::class);
<?php namespace App\Http\Cards; use HeadlessLaravel\Cards\Cards; class Dashboard extends Cards { public function cards(): array { return [ Card::make('Total Users') ->link('/users') ->component('number-card') ->value(function() { return User::count(); }), ]; } }
/api/dashboard
{ "data": [ { "key": "total_users", "title": "Total Users", "value": 5, "component": "number-card", "link": "/users", "endpoint": "api/dashboard/total-users" } ] }
You can also reference the single method using the key
in slug format.
This is useful when you want your ui to update / filter a single card.
/api/dashboard/total-users
{ "key": "total_users", "title": "Total Users", "value": 5, "component": "number-card", "link": "/users", "endpoint": "api/dashboard/total-users" }
This is only a basic example. The real power comes in the filtering multiple cards using one query string and validating that the query string is accurate.
<?php namespace App\Http\Cards; use HeadlessLaravel\Cards\Cards; class Dashboard extends Cards { public function rules() { return [ 'from' => ['nullable', 'date', 'before_or_equal:to'], 'to' => ['nullable', 'date', 'after_or_equal:from'], ]; } public function cards(): array { return [ Card::make('Total Users') ->link('/users') ->component('number-card') ->value(function() { return User::whereBetween('created_at', [ Request::input('from', now()), Request::input('to', now()) ])->count(); }), Card::make('Total Orders', 'total_orders') ->link('/orders') ->component('number-card') ->value(function() { return Order::whereBetween('created_at', [ Request::input('from', now()), Request::input('to', now()) ])->count(); }), ]; } }
Which results in both models being filtered by the same query string.
/dashboard?from=...&to=...
{ "data": [ { "key": "total_users", "title": "Total Users", "value": 5, "component": "number-card", "link": "/users", "endpoint": "api/dashboard/total-users" }, { "key": "total_orders", "title": "Total Orders", "value": 50, "component": "number-card", "link": "/orders", "endpoint": "api/dashboard/total-orders" } ] }
The filters also work on a single card request:
/dashboard/total-users?from=...&to=...
/dashboard/total-orders?from=...&to=...
You can pass a number of things as values:
Views
Card::make('Welcome')->view('cards.welcome');
{ "key": "welcome", "title": "Welcome", "value": "<h1>Welcome!</h1>", "component": null, "link": null, "endpoint": "api/dashboard/welcome" }
Http
Card::make('Weather')->http('api.com/weather', 'data.results.0');
{ "key": "weather", "title": "Weather", "value": { "today": "90 degrees", "tomorrow": "50 degrees" }, "component": null, "link": null, "endpoint": "api/dashboard/weather" }
Which is just shorthand for:
Card::make('Weather') ->value(function() { return Http::get('api.com/weather')->json('data.results.0'); }),
Cache
Any values in a callable can be cached: (seconds)
Card::make('Weather') ->cache(60) ->value(function() { return Http::get('api.com/weather')->json('data.today'); }),
License
The MIT License (MIT). Please see License File for more information.