Um conjunto de "goodies" (utilitários) para projetos Laravel da Artisan Digital: macros para os componentes nativos do framework, traits para Enums, traits/observers para Models e alguns helpers globais de PHP e Blade.
- Instalação
- Configuração
- Macros
- Traits para Enums
- Traits e Observers para Models
- Helpers globais
- Helpers de componentes Blade
- Licença
Instale o pacote via Composer:
composer require artisanbr/goodiesO pacote utiliza package discovery do Laravel, então o GoodiesServiceProvider é registrado automaticamente. Nenhuma configuração adicional é necessária para usar as macros e os helpers globais.
Se quiser customizar as opções do pacote, publique o arquivo de configuração:
php artisan vendor:publish --provider="ArtisanBR\Goodies\GoodiesServiceProvider"Isso irá gerar config/goodies.php:
return [
'user_relation_key' => 'user_id',
];user_relation_key: nome da coluna usada como foreign key do usuário logado pela traitBelongsToUsere peloBelongsToUserObserver.
Todas as macros abaixo são registradas automaticamente no boot() do service provider — basta ter o pacote instalado para usá-las.
Encurta um caminho de arquivo/URL mantendo os primeiros e últimos segmentos, útil para exibir paths longos de forma legível.
str('app/Http/Controllers/Api/V1/UserController.php')
->limitPath(keepFirst: 1, keepLast: 2);
// "app/.../V1/UserController.php"Assinatura: limitPath(string $path, int $keepFirst = 1, int $keepLast = 2, string $ellipsis = '...'): string
Busca (LIKE) em múltiplos atributos — inclusive em relacionamentos, usando a notação relacionamento.atributo — combinados com OR.
User::query()
->whereAnyLike(['name', 'email', 'profile.bio'], 'joão')
->get();
// case-sensitive
User::query()->whereAnyLike('name', 'João', sensitive: true)->get();Assinatura: whereAnyLike(array|string $attributes, string $searchTerm, bool $sensitive = false)
Equivalente ao whereAnyLike(), mas para o Query Builder puro (DB::table()), aceitando um ou vários termos de busca.
DB::table('users')
->whereLikeAny(['name', 'email'], ['joão', 'maria'])
->get();Assinatura: whereLikeAny(array|string $attributes, array|string $searchTerm)
Filtra uma Collection (de arrays, objetos ou Models) verificando se algum dos atributos contém qualquer um dos termos informados. Suporta notação em "ponto" (categoria.nome) via data_get() e um callback opcional de escape.
collect($items)->whereLike(['name', 'category.name'], 'eletrônico');
collect($items)->whereLike('name', ['a', 'b'], escape: fn ($item) => $item['featured']);Filtram os itens pelo índice (chave) par ou ímpar.
collect([1, 2, 3, 4])->even(); // [0 => 1, 2 => 3]
collect([1, 2, 3, 4])->odd(); // [1 => 2, 3 => 4]Aplica um callback recursivamente em todos os valores da coleção (via array_walk_recursive).
collect(['a' => 1, 'b' => ['c' => 2]])->mapRecursive(function (&$value) {
$value *= 10;
});Atalho para keyBy(fn ($item) => $item) — indexa a coleção pelos próprios valores.
collect(['a', 'b', 'c'])->keyByValues();
// ['a' => 'a', 'b' => 'b', 'c' => 'c']Atalho para data_get() sobre o array da coleção, já retornando uma nova Collection.
collect(['user' => ['name' => 'João']])->dataGet('user.name');Prefixa cada item da coleção com um ponto, transformando uma lista de extensões em formato .ext.
collect(['pdf', 'jpg', 'png'])->toExtensions();
// ['.pdf', '.jpg', '.png']Convertem coleções entre o formato "flat" e o formato {type, data} usado pelo Filament Builder field, útil ao persistir/ler dados de blocos dinâmicos do Filament fora do próprio field.
// De [{'type' => 'text', 'data' => ['value' => 'foo']}, ...] para [{'type' => 'text', 'value' => 'foo'}, ...]
collect($filamentBuilderState)->fromFilamentBuilder();
// E de volta ao formato do Filament Builder
collect($flatItems)->toFilamentBuilder();Traits pensadas para Enums nativos do PHP (enum ... : string), acrescentando conveniências comuns em Enums de Laravel.
use ArtisanBR\Goodies\Enums\Traits\EnumToArray;
enum Status: string
{
use EnumToArray;
case Active = 'active';
case Inactive = 'inactive';
}
Status::names(); // ['Active', 'Inactive']
Status::values(); // ['active', 'inactive']
Status::array(); // ['active' => 'Active', 'inactive' => 'Inactive']
Status::toArray(); // idem a array()Já inclui EnumToArray e adiciona comparações rápidas com o value do Enum.
use ArtisanBR\Goodies\Enums\Traits\EnumBase;
enum Status: string
{
use EnumBase;
case Active = 'active';
case Inactive = 'inactive';
case Blocked = 'blocked';
}
$status = Status::Active;
$status->is('active'); // true
$status->isAny('inactive', 'blocked'); // falseGera títulos legíveis (e traduzíveis via __()) a partir do nome de cada case, separando palavras em PascalCase/camelCase.
use ArtisanBR\Goodies\Enums\Traits\EnumWithTitles;
enum Status: string
{
use EnumWithTitles;
case AwaitingPayment = 'awaiting_payment';
case Shipped = 'shipped';
}
Status::AwaitingPayment->title(); // "Awaiting Payment"
Status::titles();
// ['awaiting_payment' => 'Awaiting Payment', 'shipped' => 'Shipped']As três traits podem ser combinadas normalmente com
use EnumBase, EnumWithTitles;em um mesmo Enum.
A trait BelongsToUser adiciona um relacionamento user() (e o alias owner()) ao Model, usando o model de usuário configurado em auth.providers.users.model. O BelongsToUserObserver preenche automaticamente a foreign key do usuário logado ao criar um novo registro.
A coluna usada é resolvida nesta ordem: propriedade estática $userRelationKey no Model → config('goodies.user_relation_key') → 'user_id'.
use ArtisanBR\Goodies\Traits\BelongsToUser;
use ArtisanBR\Goodies\Observers\BelongsTo\BelongsToUserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
#[ObservedBy(BelongsToUserObserver::class)]
class Post extends Model
{
use BelongsToUser;
// opcional: customiza o nome da coluna para este model
public static string $userRelationKey = 'author_id';
}$post = Post::create(['title' => 'Olá mundo']);
$post->user_id; // preenchido automaticamente com o id do usuário autenticado
$post->user; // relacionamento BelongsTo
$post->owner; // alias de user()Caso prefira registrar o observer manualmente (sem o atributo #[ObservedBy]), use Post::observe(BelongsToUserObserver::class) no boot() de um EventServiceProvider/AppServiceProvider.
Adiciona uma relação hierárquica simples (pai/filhos) a um Model que referencia a si mesmo através de uma coluna parent_id, além de scopes para filtrar por hierarquia.
use ArtisanBR\Goodies\Traits\SelfRelationship;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use SelfRelationship;
}$category->parent; // BelongsTo self, via parent_id
$category->children; // HasMany self, via parent_id
Category::whereParent($categoryId)->get(); // filhos diretos de um registro
Category::whereIsParent()->get(); // apenas registros raiz (parent_id null)Funções globais carregadas automaticamente pelo Composer (files autoload).
Igual ao array_filter() nativo, mas aplicado recursivamente em arrays aninhados.
array_filter_recursive([
'a' => 1,
'b' => ['c' => 0, 'd' => 2],
]);
// ['a' => 1, 'b' => ['d' => 2]]Assinatura: array_filter_recursive(array $array, ?callable $callback = null, int $mode = 0): array
Utilitários para agrupar atributos de componentes Blade por prefixo — útil para repassar, por exemplo, todos os atributos wrapper-* para o elemento wrapper de um componente e input-* para o input interno.
{{-- <x-field wrapper-class="mb-4" input-placeholder="Digite aqui" /> --}}
@php
$wrapperAttrs = attributesBagGroup('wrapper', $attributes);
$inputAttrs = attributesBagGroup('input', $attributes);
@endphp
<div {{ $wrapperAttrs }}>
<input {{ $inputAttrs }} />
</div>Também aceita um array de prefixos, retornando os atributos que casarem com qualquer um deles:
attributesBagGroup(['wrapper', 'container'], $attributes);Assinatura: attributesBagGroup(string|array $groupName, ComponentAttributeBag $attributes): ComponentAttributeBag
Classe invocável utilitária para prefixar nomes de componentes Blade dinamicamente (por exemplo, ao registrar um namespace de componentes de um pacote).
use ArtisanBR\Goodies\Support\Blade\BladeComponentPrefix;
$resolver = new BladeComponentPrefix('goodies');
$resolver('button'); // "goodies-button"
$resolver = new BladeComponentPrefix(null);
$resolver('button'); // "button" (sem prefixo)Este pacote é open-source e distribuído sob a licença MIT.