Lessons from Laracasts, Part 2

This is part 2 of my collection of tips I've taken from Let's Build a Forum with Laravel and TDD, the mega-tutorial (102 lessons) by Jeffrey Way on his Laracasts site. Part 1 is here. This post contains 51 tips, covering lessons 43-102.

51.

The artisan notifications:table command creates a migration for a table to hold database notifications. [Source]

52.

The tap helper, inspired by Ruby can help refactoring by removing temporary variables. [Source]

53.

Use the artisan event:generate command to create events and listeners. [Source]

54.

The Notification::fake and Notification:assertSentTo methods allow you to test notifications without triggering their usual behaviour. [Source]

55.

JW uses [identifier]CacheKey as a method to supply a single point of truth for a cache's key. [Source]

56.

An example of using Laravel's container when refactoring spam inspection methods to their own classes: [Source], around 06:35.

57.

The resolve method resolves a class out of Laravel's container. [Source]

58.

JW uses a 422 Unprocessable Entity HTTP status code when catching exceptions thrown during AJAX calls. [Source]

59.

Artisan's make:rule command will scaffold the necessary code for custom validation rules. [Source]

60.

The throttle middleware rate limits access to Laravel routes. But JW notes that failing validation would make this solution problematic. [Source]

61.

Use form requests for more complex form situations requiring authorization and validation. [Source]

62.

The collect helper converts arrays to collections. [Source]

63.

When the filter method is called without an argument on a collection it removes falsey values. [Source]

64.

In the tutorial, JW uses the At.js library to add mentions autocomplete functionality to the forum. [Source]

65.

Use the pluck method to get the values for a given key from a collection. JW uses it to return values from a collection retrieved by Eloquent:

return User::where('name', 'LIKE', '$search%')->take(5)->pluck('name');

[Source]

66.

One way to grab CSS from an NPM package: use the copy method in your webpack.mix.js file:

mix.js('resources/assets/js/app.js', 'public.js')
   .sass('resources/assets/sass/app.scss', 'public.css')
   .copy('node_modules/[path_to_src_css_file]', '[path_to_dest_css_file]');

[Source]

67.

Another way to grab a package's CSS: import it in your main scss file. [Source]

68.

Another way to grab a package's CSS: just manually copy it to your css directory. [Source]

69.

To import CSS files depending on the view, use a @yield('head') directive, then in the template you want to load this file, include the link tag in a @section('head') directive. [Source]

70.

The UploadedFile::fake method allows you to fake a file (or image) for testing:

UploadedFile::fake()->image('filename.jpg', $width, $height)

[Source]

71.

The Storage::fake method will create a fake storage disk for testing. E.g.:

Storage::fake('public');

$this->json('POST', 'api/upload', [
  'file' => UploadedFile::fake()->image('filename.jpg')
]);

Storage::disk('public')->assertExists('filename.jpg');

[Source]

72.

hashName returns the filename for an uploaded file. [Source]

73.

Compare Eloquent models with is and isNot. [From the comments to Source]

74.

How did I not know about this? The input tag has an accept attribute which takes a comma-separated list of content type specifiers, either file extensions, MIME types or audio/*, video/* or image/*. [Source]

76.

Any native attribute (e.g. name on a form element) specified on a Vue component instance will be passed to that component, when the root element of the component is a single form element. [Source]

77.

As of PHP 7, you can use the null coalescing operator to assign a default value. E.g.:

return Redis::get($this->cacheKey()) ?? 0;

[Source]

The steps to create a custom middleware:

  1. php artisan make:middleware NameOfMiddleware

  2. In the handle method of the new middleware write the code to filter access to the route:

if (! $request->user()->confirmed) {
  return redirect('/threads');
}
  1. List the middleware class in app/Http/Kernel.php:
protected $routeMiddleware = [
  'name-of-middleware' => NameOfMiddleware::class
];
  1. Attach the middleware to a route:
Route::post('threads', 'ThreadsController@store')->middleware('name-of-middleware');

[Source]

79.

To simulate a new user registration in a test you can call the event that's called by the register method in the RegistersUsers trait:

event(new Registered($user));

[Source]

80.

Faking emails while testing can be done with the Mail::fake method.

Mail::fake();

[test code here]

Mail::assertSent(MailableName::class);

[Source]

81.

Eloquent models have a $cast property that, in the words of the docs, "provides a convenient method of converting attributes to common data types". Useful, for example, for converting 0 or 1 to booleans. [Source]

82.

The forceCreate Eloquent method is like create except that it ignores any mass-assignment rules. [Source]

83.

You can define modifications of your model factories with the state method. [Source]

84.

Laravel's str-slug helper converts strings to slugs. [Source]

85.

Use the query builder's max method to return the highest value of a given model field. E.g.:

$max = Book::wherePublisher('foobar')->max('pages');

[Source]

86.

As of PHP 7, you can access characters in strings using an offset in array-type brackets. To access the final character of string, use -1:

$final = $slug[-1];

[Source]

87.

Use the abort_if helper to throw an exception under a given boolean condition. [Source]

88.

You can render an array as JSON in a blade template with the @json directive. [Source]

89.

MySQL has a SET NULL action that can be set for UPDATE and DELETE operations on a foreign key. In a Laravel migration, this reads as:

$table->foreign('user_id')
  ->references('id')
  ->on('users')
  ->onDelete('set null');

[Source]

90.

If you get a "Cannot add foreign key constraint" error when migrating, that can be because the migration where you're setting the foreign key has a timestamp earlier than the migration for the table you're referencing in your foreign key. JW fixes this by manually altering the timestamp of the migration to a later date. [Source]

91.

A potential error when using foreign keys and testing with an SQLite database: foreign keys are not enabled by default. To enable them, in your test include:

DB::statement('PRAGMA foreign_keys = ON');

[Source]

NB. A comment to this video notes that instead you can use:

Schema::enableForeignKeyConstraints();

92.

Use double negation (!!) to cast variables to a boolean. [Source]

93.

Use the @php directive to embed PHP code in Blade views. [Source]

94.

It is recommended not to reference the .env file within the application. Instead, pull environment variables from a configuration file (e.g. config/services.php) which itself pulls from .env. [Source]

95.

Instead of getting the remote IP address with $_SERVER['REMOTE_ADDR'], use request()->ip(). [Source]

96.

Use app()->runningUnitTests() for behaviour conditional on whether you're running your tests. [Source]

97.

As of Laravel 5.5, validation returns the validated data, which makes it possible to create concise flows like this:

$thread->update(request()->validate([
  'title' => 'required',
  'body' => 'required'
]));

[Source]

98.

Using an external search service like Algolia can slow down tests. To circumvent this, set SCOUT_DRIVER to null in your test environment variables (phpunit.xml). For tests where you want to use the search service, you can update the config for that specific test:

config(['scout.driver' => 'algolia']);

[Source]

99.

For tests which use a search service, remember to remove any created elements at the end of the test to avoid false positives, with the unsearchable method on a collection. [Source]

100.

Specify the data sent to your search service by overriding toSearchableArray in your model. [Source]

101.

Any property passed to a Vue component that isn't accepted as a property in that component, will be assigned to the top-level element in that component's template. [Source]

102.

When you want to display sanitized data in a Vue (or other front-end framework) component, and you want to sanitize it with PHP, you can do it with a custom accessor. [Source]