Add Useful Info to the Laravel About Command

The Laravel about command released in Laravel 9.21 provides an excellent overview of important configurations for your application. Out of the box, it lists environment details, cache status, and configured drivers:

Another neat feature of the new about command is the ability for packages to add helpful information too. For example, we’ve covered Filament components here on Laravel News; after the release of Laravel 9.21, Ryan Chandler opened a pull request to add useful plugin details to Filament.

I think we’ll see a lot of package authors add helpful details to the about command. Hopefully, the end-user doesn’t get overwhelmed with too much info, or perhaps package developers make the inclusion of data in the about command configurable.

With that intro out of the way, how would you add custom data to the about command?

You can do so in a service provider, using the AboutCommand::add() method within the service provider’s boot() method.

In the following example, let’s say I wanted my package or application to output specific XDebug configuration values:

1use IlluminateFoundationConsoleAboutCommand;

2 

3// ...

4 

5public function boot()

6{

7 AboutCommand::add('XDebug Settings', [

8 'Client Port' => fn() => ini_get('xdebug.client_port'),

9 'Client Host' => fn() => ini_get('xdebug.client_host'),

10 'Start With Request' => fn() => ini_get('xdebug.start_with_request'),

11 'Max Nesting Level' => fn() => ini_get('xdebug.max_nesting_level'),

12 'Mode' => fn() => ini_get('xdebug.mode'),

13 'Output Dir' => fn() => ini_get('xdebug.output_dir'),

14 'Log' => fn() => !empty(ini_get('xdebug.log')) ? ini_get('xdebug.log') : 'No Value',

15 ]);

16}

The above might look like the following locally, depending on your XDebug configuration:

Lazy Loading

One thing to note when creating custom about commands is that you should lazy load the output by wrapping the settings in an fn() => arrow function. For example:

1'Client Port' => ini_get('xdebug.client_port'),

2'Client Port' => fn() => ini_get('xdebug.client_port'),

I am excited to see what helpful information package authors start adding to this command!

Credit: Source link

Laravel 9.21 Introduces a Fresh Look for Artisan

The Laravel team released 9.21 with a fresh new look for Artisan, two brand-new Artisan commands, and more. Let’s take a look at all the goodness in the latest Laravel 9 release:

A fresh new look for Artisan

Nuno Maduro contributed a huge refresh of the artisan CLI. “Nearly all the built-in Artisan commands have been reimagined to deliver a better experience.”

Here are a few examples from a fresh install of Laravel 9.21:

artisan route:list

artisan migrate:fresh

If you’d like a deeper dive, check out Laravel: Refreshing Artisan on the Laravel Blog. Also, Pull Request #43065 has the implementation details and dozens of side-by-side comparisons of the old vs new CLI.

Artisan about command

Speaking of Artisan improvements, James Brooks created a brand-new command: about. The about command displays output about the Laravel environment, such as debug mode, PHP version, cache statuses, and more:

artisan about

Artisan model show command

Jess Archer contributed a new model:show Artisan command that displays helpful information to give you an overview of your model:

artisan model:show

It provides data from the database and Eloquent to give you a complete, useful picture of your model in one place. Typically, you’d have to explore the database and the model class to compile this information.

Added a whenCounted method to JsonResource

Steve Bauman contributed a whenCounted method to JSON resources to conditionally include a relation count when the relation is set on the model:

1// new PostResource($post->loadCount('comments'));

2 

3 

4class PostResource extends PostResource

5{

6 public function toArray($request)

7 {

8 return [

9 'id' => $this->id,

10 'comments_count' => $this->whenCounted('comments'),

11 ];

12 }

13}

Retrieve input from the request as an enum

@emargareten contributed to retrieving an input as an enum from the request object:

1// Before

2public function post(Request $request)

3{

4 $status = StatusEnum::tryFrom($request->input('status'));

5 

6 // do stuff with status enum...

7}

8 

9// After

10public function post(Request $request)

11{

12 $status = $request->enum('status', StatusEnum::class);

13 

14 // do stuff with status enum...

15}

Release Notes

You can see the complete list of new features and updates below and the diff between 9.20.0 and 9.21.0 on GitHub. The following release notes are directly from the changelog:

v9.21.0

Added

Revert

Fixed

  • Fix transaction attempts counter for sqlsrv (#43176)

Changed

  • Make assertDatabaseHas failureDescription more multibyte character friendly (#43181)
  • ValidationException summarize only when use strings (#43177)
  • Improve mode function in collection (#43240)
  • clear Facade resolvedInstances in queue worker resetScope callback (#43215)
  • Improves queue:work command (#43252)
  • Remove null default attributes names when UPDATED_AT or CREATED_AT is null at Model::replicate (#43279)
  • Protect against ambiguous columns (#43278)
  • Use readpast query hint instead of holdlock for sqlsrv database queue (#43259)
  • Vendor publish flag that restricts to only existing files (#43212)

Credit: Source link

A Simple Draft and Revision System for Laravel

Laravel Drafts is a simple drop-in draft and revision system for Eloquent models. This package also supports drafts for related models; you can control saving models in a published or draft mode.

To get started with this package, your project will use the provided HasDrafts trait:

1use IlluminateDatabaseEloquentModel;

2use OddvalueLaravelDraftsConcernsHasDrafts;

3 

4class Post extends Model

5{

6 use HasDrafts;

7 // ...

8}

With the trait configured, you can use the following API to control creating a published model or a draft:

1// By default, the record is published

2Post::create([

3 'title' => 'Foo',

4 // ...

5]);

6 

7// You can set the record as a draft in a few different ways:

8 

9// Via the `is_published` flag

10Post::create([

11 'title' => 'Foo',

12 'is_published' => false,

13]);

14 

15// Calling `createDraft`

16Post::createDraft(['title' => 'Foo']);

17 

18// Using the `saveAsDraft` method on a model

19Post::make(['title' => 'Foo'])->saveAsDraft();

Similarly, you can update a model, keeping it as a draft:

1$post->updateAsDraft(['title' => 'Bar']);

2 

3// Or

4 

5$post->title = 'Bar';

6$post->saveAsDraft();

You can also access draft revisions and configure how many total revisions to keep in the database. The readme is an excellent place to get started with this package.

Check out this package and contribute on GitHub at oddvalue/laravel-drafts.

Credit: Source link

Logging external HTTP Requests with Laravel Telescope

The biggest issue with working with third-party APIs is that we have very little visibility. We integrate them into our code base and test them – but we have no idea how often we use them unless the API we are integrating with has metrics we can use. I have been quite frustrated with this for quite some time – but there is something we can do.

Laravel Telescope is a debugging assistant for your application, which means that it will log and give you insight into what is going on from a high level. We can tap into this and add custom watchers to enable more debugging and logging, and this is what we will do in this short tutorial.

Once you have installed Laravel Telescope, make sure you publish the configuration and migrate the database, we can start to create our watcher for Guzzle – the client underneath the Http facade. The most logical place to keep these classes, at least for me, is inside app/Telescope/Watchers as the code belongs to our application – but we are extending Telescope itself. But what does a standard watcher look like? I will show you a rough outline of the base requirements below:

1class YourWatcher extends Watcher

2{

3 public function register($app): void

4 {

5 // handle code for watcher here.

6 }

7}

This is a rough outline. You can add as many methods as you need to add the watcher that works for you. So without further ado, let us create a new watcher app/Telescope/Watchers/GuzzleRequestWatcher.php, and we will walk through what it needs to do.

1declare(strict_types=1);

2 

3namespace App\Telescope\Watchers;

4 

5use GuzzleHttp\Client;

6use GuzzleHttp\TransferStats;

7use Laravel\Telescope\IncomingEntry;

8use Laravel\Telescope\Telescope;

9use Laravel\Telescope\Watchers\FetchesStackTrace;

10use Laravel\Telescope\Watchers\Watcher;

11 

12final class GuzzleRequestWatcher extends Watcher

13{

14 use FetchesStackTrace;

15}

We first need to include the trait FetchesStackTrace as this allows us to capture what and where these requests are coming from. If we refactor these HTTP calls to other locations, we can make sure we call them how we intend to. Next, we need to add a method for registering our watcher:

1declare(strict_types=1);

2 

3namespace App\Telescope\Watchers;

4 

5use GuzzleHttp\Client;

6use GuzzleHttp\TransferStats;

7use Laravel\Telescope\IncomingEntry;

8use Laravel\Telescope\Telescope;

9use Laravel\Telescope\Watchers\FetchesStackTrace;

10use Laravel\Telescope\Watchers\Watcher;

11 

12final class GuzzleRequestWatcher extends Watcher

13{

14 use FetchesStackTrace;

15 

16 public function register($app)

17 {

18 $app->bind(

19 abstract: Client::class,

20 concrete: $this->buildClient(

21 app: $app,

22 ),

23 );

24 }

25}

We intercept the Guzzle client and register it into the container, but to do so, we want to specify how we want the client to be built. Let’s look at the buildClient method:

1private function buildClient(Application $app): Closure

2{

3 return static function (Application $app): Client {

4 $config = $app['config']['guzzle'] ?? [];

5 

6 if (Telescope::isRecording()) {

7 // Record our Http query.

8 }

9 

10 return new Client(

11 config: $config,

12 );

13 };

14}

We return a static function that builds our Guzzle Client here. First, we get any guzzle config – and then, if telescope is recording, we add a way to record the query. Finally, we return the client with its configuration. So how do we record our HTTP query? Let’s take a look:

1if (Telescope::isRecording()) {

2 $config['on_stats'] = static function (TransferStats $stats): void {

3 $caller = $this->getCallerFromStackTrace(); // This comes from the trait we included.

4 

5 Telescope::recordQuery(

6 entry: IncomingEntry::make([

7 'connection' => 'guzzle',

8 'bindings' => [],

9 'sql' => (string) $stats->getEffectiveUri(),

10 'time' => number_format(

11 num: $stats->getTransferTime() * 1000,

12 decimals: 2,

13 thousand_separator: '',

14 ),

15 'slow' => $stats->getTransferTime() > 1,

16 'file' => $caller['file'],

17 'line' => $caller['line'],

18 'hash' => md5((string) $stats->getEffectiveUri())

19 ]),

20 );

21 };

22}

So we extend the configuration by adding the on_stats option, which is a callback. This callback will get the stack trace and record a new query. This new entry will contain all relevant things to do with the query we can record. So if we put it all together:

1declare(strict_types=1);

2 

3namespace AppTelescopeWatchers;

4 

5use Closure;

6use GuzzleHttpClient;

7use GuzzleHttpTransferStats;

8use IlluminateFoundationApplication;

9use LaravelTelescopeIncomingEntry;

10use LaravelTelescopeTelescope;

11use LaravelTelescopeWatchersFetchesStackTrace;

12use LaravelTelescopeWatchersWatcher;

13 

14final class GuzzleRequestWatcher extends Watcher

15{

16 use FetchesStackTrace;

17 

18 public function register($app): void

19 {

20 $app->bind(

21 abstract: Client::class,

22 concrete: $this->buildClient(

23 app: $app,

24 ),

25 );

26 }

27 

28 private function buildClient(Application $app): Closure

29 {

30 return static function (Application $app): Client {

31 $config = $app['config']['guzzle'] ?? [];

32 

33 if (Telescope::isRecording()) {

34 $config['on_stats'] = function (TransferStats $stats) {

35 $caller = $this->getCallerFromStackTrace();

36 Telescope::recordQuery(

37 entry: IncomingEntry::make([

38 'connection' => 'guzzle',

39 'bindings' => [],

40 'sql' => (string) $stats->getEffectiveUri(),

41 'time' => number_format(

42 num: $stats->getTransferTime() * 1000,

43 decimals: 2,

44 thousands_separator: '',

45 ),

46 'slow' => $stats->getTransferTime() > 1,

47 'file' => $caller['file'],

48 'line' => $caller['line'],

49 'hash' => md5((string) $stats->getEffectiveUri()),

50 ]),

51 );

52 };

53 }

54 

55 return new Client(

56 config: $config,

57 );

58 };

59 }

60}

Now, all we need to do is make sure that we register this new watcher inside of config/telescope.php, and we should start seeing our Http queries being logged.

1'watchers' => [

2 // all other watchers

3 App\Telescope\Watchers\GuzzleRequestWatcher::class,

4]

To test this, create a test route:

1Route::get('/guzzle-test', function () {

2 Http::post('<https://jsonplaceholder.typicode.com/posts>', ['title' => 'test']);

3});

When you open up Telescope, you should now see a navigation item on the side called HTTP Client, and if you open this up, you will see logs appear here – you can inspect the headers, the payload, and the status of the request. So if you start seeing failures from API integrations, this will help you massively with your debugging.

Did you find this helpful? What other ways do you use to monitor and log your external API requests? Let us know on Twitter!

Credit: Source link

Fast Paginate for Laravel | Laravel News

Laravel Fast Paginate is a fast implementation of offset/limit pagination for Laravel by Aaron Francis:

Using Fast Paginate, you can use the same pagination API available in Laravel via the fastPaginate() method:

1// Traditional built-in pagination

2Model::query()->paginate()

3 

4// Fast paginate has the same method signature

5Model::query()->fastPaginate()

6 

7// Relationships are supported

8User::first()->posts()->fastPaginate();

If you’d like to learn more about the techniques used in this package, check out Efficient Pagination Using Deferred Joins by Aaron Francis.

You can learn more about this package, get full installation instructions, and view the source code on GitHub.


Credit: Source link

Encrypt and Decrypt Eloquent Model Fields in Laravel Apps

Laravel Ciphersweet is a package by Spatie to integrate searchable field-level encryption in Laravel applications. The package’s readme explains the problem Ciphersweet can help solve as follows:

In your project, you might store sensitive personal data in your database. Should an unauthorised person get access to your DB, all sensitive can be read which is obviously not good.

To solve this problem, you can encrypt the personal data. This way, unauthorized persons cannot read it, but your application can still decrypt it when you need to display or work with the data.

This package is a wrapper for Ciphersweet to integrate its features into Laravel models easily. Here’s an example of a model from the readme’s setup instructions that illustrates what a model looks like using Ciphersweet:

1use SpatieLaravelCipherSweetContractsCipherSweetEncrypted;

2use SpatieLaravelCipherSweetConcernsUsesCipherSweet;

3use ParagonIECipherSweetEncryptedRow;

4use IlluminateDatabaseEloquentModel;

5 

6class User extends Model implements CipherSweetEncrypted

7{

8 use UsesCipherSweet;

9 

10 public static function configureCipherSweet(EncryptedRow $encryptedRow): void

11 {

12 $encryptedRow

13 ->addField('email')

14 ->addBlindIndex('email', new BlindIndex('email_index'));

15 }

16}

This allows you the encrypt a user’s email to keep it safe from unauthorized people reading the data, but give you the ability to decrypt the data to display it or work with it.

Once you have configured this package and set up a model, you can search encrypted data in the database using blind indexes:

1$user = User::whereBlind('email', 'email_index', 'rias@spatie.be');

This package also aids in generating encrypting keys and encrypting model attributes to speed up integration with Ciphersweet.

I want to point out that you should not use this package blindly without understanding the ins and outs of the use case you are trying to solve. You can learn more about CipherSweet on this page, which has many linked resources.

CipherSweet also has PHP-specific documentation to help get you up to speed with the underlying PHP package.

I would also recommend reading Rias’ post, Encrypting Laravel Eloquent models with CipherSweet.

To get started with this package, check it out on GitHub at spatie/laravel-ciphersweet.

Credit: Source link

Laravel A/B Testing and Feature Flag Package

The Laravel A/B Test package helps you quickly set up A/B tests and feature flags in a Laravel app. The package uses the self-hosted ABRouter service (which also has paid hosted options) to power the A/B and feature flag settings.

Here’s an example controller using this package to A/B test UI features, in this case, a button color:

1use AbrouterClientClient;

2 

3class ExampleController

4{

5 public function __invoke(Client $client)

6 {

7 $userId = auth()->user()->id;

8 $buttonColor = $client->experiments()->run($userId, 'button_color');

9 return view('button', [

10 'color' => $buttonColor->getBranchId(),

11 ]);

12 }

13}

Another useful feature is feature flags, which help you roll experimental/new features out to customers incrementally, toggling on/off without a code redeploy:

1$isEnabledButton = $client->featureFlags()

2 ->run('enabled_button_feature_flag');

3 

4view('featureFlags', [

5 'enabledButtonFeatureFlag' => $isEnabledButton,

6]);

You’ll need to set up the ABRouter service to use this package. You can learn more about this service from the ABRouter Docs. You can learn about this package, get full installation instructions, and view the source code on GitHub.

Credit: Source link

Add Comments to Correlate User Code with SQL Queries in Laravel

Laravel SQL Commenter is a package by Spatie to add comments to SQL queries made by Laravel. It uses sqlcommenter to augment SQL statements about the code:

1/* typical query */

2select * from users

3 

4/* comments added by this package */

5select * from "users"/*controller='UsersController',action='index'*/;

Once this package is installed, SQL comments get automatically included, and you can control what things are added to the comments and disable comments dynamically.

1use SpatieSqlCommenterSqlCommenter;

2 

3SqlCommenter::addComment('foo', 'bar');

4 

5// select * from "users"/*foo='bar'*/;

This package has the concept of a “commenter” class, which add useful information to queries, such as the controller and route information. Out of the box, this package includes the following commenters:

  • ControllerCommenter
  • RouteCommenter,
  • JobCommenter
  • FileCommenter
  • CurrentUserCommenter

You can also add custom commenter classes using the provided Commenter interface from this package.

You can learn about this package, get full installation instructions, and view the source code on GitHub. The author, Freek Van der Herten, wrote a post titled Add comments to SQL queries made by Laravel if you’d like to dive deeper into the background of this package.

Credit: Source link