artisanbr / goodies
Laravel/PHP goodies pack by Artisan Digital
Package info
Type:package
pkg:composer/artisanbr/goodies
Requires
- php: >=8.1
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- illuminate/view: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- laravel/framework: ^10.0|^11.0|^12.0|^13.0
- laravel/pint: ^1.30
- laravel/tinker: ^3.0
README
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.
Índice
- Instalação
- Configuração
- Macros
- Traits para Enums
- Traits e Observers para Models
- Helpers globais
- Helpers de componentes Blade
- Licença
Instalação
Instale o pacote via Composer:
composer require artisanbr/goodies
O 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.
Configuração
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.
Macros
Todas as macros abaixo são registradas automaticamente no boot() do service provider — basta ter o pacote instalado para usá-las.
Stringable
Stringable::limitPath()
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
Eloquent Builder
whereAnyLike()
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)
Query Builder (DB)
whereLikeAny()
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)
Collection
whereLike()
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']);
even() / odd()
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]
mapRecursive()
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; });
keyByValues()
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']
dataGet()
Atalho para data_get() sobre o array da coleção, já retornando uma nova Collection.
collect(['user' => ['name' => 'João']])->dataGet('user.name');
toExtensions()
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']
fromFilamentBuilder() / toFilamentBuilder()
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 para Enums
Traits pensadas para Enums nativos do PHP (enum ... : string), acrescentando conveniências comuns em Enums de Laravel.
EnumToArray
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()
EnumBase
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'); // false
EnumWithTitles
Gera 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.
Traits e Observers para Models
BelongsToUser + BelongsToUserObserver
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.
SelfRelationship
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)
Helpers globais
Funções globais carregadas automaticamente pelo Composer (files autoload).
array_filter_recursive()
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
Helpers de componentes Blade
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.
attributesBagGroup()
{{-- <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
BladeComponentPrefix
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)
Licença
Este pacote é open-source e distribuído sob a licença MIT.