Lessons from Laracasts, Part 2
27 April 2018This 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]
78.
The steps to create a custom middleware:
-
php artisan make:middleware NameOfMiddleware
-
In the
handle
method of the new middleware write the code to filter access to the route:
if (! $request->user()->confirmed) {
return redirect('/threads');
}
- List the middleware class in
app/Http/Kernel.php
:
protected $routeMiddleware = [
'name-of-middleware' => NameOfMiddleware::class
];
- 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]