ralphjsmit / laravel-helpers
A package containing handy helpers for your Laravel-application.
Installs: 243 487
Dependents: 1
Suggesters: 0
Security: 0
Stars: 11
Watchers: 2
Forks: 2
Open Issues: 2
Requires
- php: ^8.0
- guzzlehttp/guzzle: ^7.4
- illuminate/contracts: ^8.73|^9.0|^10.0|^11.0
- spatie/laravel-package-tools: ^1.9.2
Requires (Dev)
- livewire/livewire: ^2.9|^3.4
- nesbot/carbon: ^2.66|^3.0
- nunomaduro/collision: ^5.10|^6.1|^7.0|^8.0
- orchestra/testbench: ^6.22|^7.0|^8.0|^9.0
- pestphp/pest: ^1.21|^2.34
- pestphp/pest-plugin-laravel: ^1.1|^2.3
- phpunit/phpunit: ^9.5|^10.5
- spatie/invade: ^1.0|^2.0
- spatie/laravel-ray: ^1.26
README
This package contains a set of handy helpers for your Laravel-application.
Installation
You can install the package via composer:
composer require ralphjsmit/laravel-helpers
Laravel
Factories
Instead of letting your factories extend Laravel's default factories, you can let them extend the RalphJSmit\Helpers\Laravel\Factories\Factory
class.
Complex factory relations with resolveRelationship()
When you have a complex relationship, you can use the resolveRelationship()
method to resolve the relationship. This is useful if you want to modify the factory using a closure or something similar:
use RalphJSmit\Helpers\Laravel\Factories\Factory; class PostFactory extends Factory { public function definition(): array { return [ 'user_id' => null, ]; } public function withUser(User|UserFactory|Closure|int|null $user = null): static { return $this->set('user_id', $this->resolveRelationship(User::class, $user)); } }
A simple example would look like this:
$post = Post::factory() ->withUser(fn(UserFactory $factory): UserFactory => $factory->set('name', 'John Doe')) ->create();
Pipes, pipelines & pipeable
Pipes v. pipelines
Laravel has an undocumented feature called Pipelines. Pipelines are handy for sending data through a series of functions/classes.
The pipelines in Laravel, however, have one big downside: inside every function or class, you need to call a closure to move to the next pipe in the pipeline. If you don't call that closure, the pipeline stops. Laravel Middleware is a great example of this.
However, sometimes you don't want that closure and just pass the result of the first pipe into the second pipe, meaning that the full pipeline always gets executed. This package offers a custom implementation for that, called a "pipe". You don't have a way to stop execution (use a regular pipeline instead), but you don't have to call the closure inside each pipe either.
From now on, I'll call the custom implementation of this package a "pipe" and the more advanced, regular Laravel-implementation a "pipeline".
Pipeline
You can use the pipeline()
function to create a new pipeline:
$result = pipeline() ->send($class) ->through($pipes) ->thenReturn();
To learn more about pipelines, check out this article.
Pipe
You can create a new pipe with the pipe()
function:
$result = pipe($thing) ->through([ PerformAction::class, PerformAnotherAction::class, ]) ->then(function($result) { // Do something with $result and return it. return $result; });
Each of the items in the ->through()
array can be one of the following:
- A closure or callable
- An invokable class (which is a callable as well)
- A class instance
- A class string
By default, the pipe will try to execute each item with via a method 'handle'. You can change that method by using the ->via()
method.
At the end of each pipe, you can pass a closure to the ->then()
method to do something with the result. To immedialtely return the result, you can also use the ->thenReturn()
method.
$result = pipe($thing) ->through(/** */) ->via('execute') ->thenReturn();
Pipeable
This package also offers a handy trait that you can use to make your classes pipeable. If you include this trait, your classes will have the following new methods:
->pipe($callback)
. This method passes the object to the given closure or callable and returns the result of the executed callable.->pipeInto($class)
. ThepipeInto()
method creates a new instance of the given class and passes the object into the constructor.->pipeThrough($callbacks)
. ThepipeThrough()
method passes the object to the given array of closures/callables and returns the result of the executed callbacks.
All three methods are the same as the three identically named methods in the \Illuminate\Support\Collection
class. For examples, see the docs for pipe()
, pipeInto()
and pipeThrough()
` on the Laravel site.
Carbon
The package offers several handy helpers to make working with Carbon-objects more pleasant.
carbon($input = null, $timezone = null)
You can use the carbon($input, $timezone)
helper to create a Carbon-object. Under the hood, this uses Carbon::parse()
. Both parameters are optional.
$firstDayOfNextMonth = carbon('first day of next month', 'Europe/Amsterdam');
carbonDate($input = null, $timezone = null)
You can use the carbonDate($input, $timezone)
helper to create a Carbon-object and floor the day to 00:00:00. Under the hood, this uses Carbon::parse()->floorDay()
. Both parameters are optional.
$firstDayOfNextMonth = carbonDate('first day of next month', 'Europe/Amsterdam'); $firstDayOfNextMonth->toTimeString(); // '00:00:00'
yesterday()
and tomorrow()
You can use the tomorrow()
function to create a Carbon-instance for tomorrow. Under the hood this uses now()->addDay()
.
You can use the yesterday()
function to create a Carbon-instance for yesterday. Under the hood this uses now()->subDay()
.
$tomorrow = tomorrow(); $yesterday = yesterday();
daysOfMonth(Carbon|string $month)
You can use the daysOfMonth()
function to create a Collection
instance with the days of the month as numeric keys and a value of 0
. Useful for creating things like graphs and the like.
$days = daysOfMonth(carbon('march 2021')); $days->all(); // [ // 1 => 0, // 2 => 0, // 3 => 0, // 4 => 0, // 5 => 0, // ... // 30 => 0, // 31 => 0, // ]
Eloquent
TimeCast
Eloquent provides many nice casts, like array
, date
and datetime
. However, Laravel doesn't provide with a time
cast, for storing and casting timestamps like "08:30:00" to a Carbon instance.
The reason is probably that passing only a timestamp like "08:30:00" to Carbon assumes a Carbon instance with the current date. This might or might not be a problem for you.
In order to cast a timestring to a Carbon instance, you can set the TimeCast
class as a cast. However, please be aware that this will create a carbon instance that assumes the current date.
use RalphJSmit\Helpers\Laravel\Models\Casts\TimeCast; class Something extends Model { protected $casts = [ 'time' => TimeCast::class, ]; };
BooleanAsTimestampCast
The BooleanAsTimestampCast
cast is a handy cast that you can use to store a boolean value as a timestamp. In practice this still works the same as a normal boolean in the database. However, storing a boolean like this gives you the additional benefit of knowing when the boolean was enabled.
If the value is set to true
, it will store the current timestamp in the database. If the value is set to false, it will store null
in the database.
use RalphJSmit\Helpers\Laravel\Models\Casts\TimeCast; class Something extends Model { protected $casts = [ 'time' => BooleanAsTimestampCast::class, ]; };
#### Smart factory name guessing with `HasFactory` You can use the new `HasFactory` trait to automatically guess the name of your factories. This optimized factory trait can also guess the name of factories in different namespaces than `App\Models`, like `Support\. ```php use Illuminate\Database\Eloquent\Model; use RalphJSmit\Helpers\Laravel\Concerns\HasFactory; class MyModel extends Model { use HasFactory; }
If you prefer or need to define the factory class yourself, you may also use the protected $factory
property to define your factory:
class MyModel extends Model { use HasFactory; protected $factory = MyModelFactory::class; }
Livewire
CanBeRefreshed trait
You may use the CanBeRefreshed
trait to automatically register the following event:
use RalphJSmit\Helpers\Livewire\CanBeRefreshed; class MyComponent extends Component { use CanBeRefreshed; // Will register the following event and listener: // [ '$refresh' => '$refresh', ] // Registering additional listeners is no problem: protected $listeners = [ /* Your other listeners */ ]; }
RegisterListeners trait
You may use the RegisterListeners
trait to dynamically register event listeners, for example from the mount()
or initialized()
methods:
use RalphJSmit\Helpers\Livewire\RegisterListeners; // Somewhere... $this->registerListeners([ 'my-event' => 'myListener' ]);
RegisterMessages trait
You may use the RegisterMessages
trait to dynamically register event listeners, for example from the mount()
or initialized()
methods:
use RalphJSmit\Helpers\Livewire\RegisterMessages; // Somewhere... $this->registerMessages([ 'user.email.required' => 'Adding an e-mail address is required.' ]);
General
🐞 If you spot a bug, please submit a detailed issue and I'll try to fix it as soon as possible.
🔐 If you discover a vulnerability, please review our security policy.
🙌 If you want to contribute, please submit a pull request. All PRs will be fully credited. If you're unsure whether I'd accept your idea, feel free to contact me!
🙋♂️ Ralph J. Smit