Lessons from Laracasts, Part 1
24 March 2018Let's Build a Forum with Laravel and TDD is a mega-tutorial (102 lessons) by Jeffrey Way on his Laracasts site. It's a massive, time-intensive beast that contains lots of useful info, and not least gives the opportunity to watch someone build a fairly complex website from scratch. But its size means that a lot of little tricks and tips are hidden away inside the videos. While each lesson's title reflects a particular feature Jeffrey Way wants to build, I found that there are also particular technical nuggets of info to be found among the broader strategies. So this is a list of any little snippets that cropped up in the course, just minor points I didn't know or found useful. This post contains 50 tips, covering lessons 1-42. See Part 2 here!
1.
When making a model in artisan, and -mr
to make the migration (m
), and a resourceful controller (r
). Or use c
for a plain controller. [Source]
2.
For testing we can use an SQLite database in memory. In phpunit.xml
set DB_CONNECTION
to sqlite
and DB_DATABASE
to :memory:
. [Source]
3.
latest()
is an Eloquent relation that is basically an alias for orderBy('created_at', 'desc')
. [Source]
4.
Put code common to all tests in a test file in a setUp
method that calls parent::setUp();
. [Source]
5.
Call diffForHumans()
on Carbon instances (like created_at
fields in Eloquent models) to output something like "2 days ago". [Source]
6.
Add the --filter
flag to phpunit
to run only one test. [Source]
7.
Instead of specifying mass-assignable fields in a model with fillable
, you can set the $guarded
variable to block specific fields, or set it to an empty array to make every field assignable. [Source]
8.
back()
is a helper function to return the user to the previous location. [Source]
9.
Note the distinction between create
and make
model factory methods: create
persists the model to the database, while make
only stores it in memory. [Source]
10.
The raw
model factory method returns an array of the model data, rather than the Eloquent model. [Source]
11.
Add only()
to restrict middleware to particular methods in a controller. E.g. in the constructor:
$this->middleware('auth')->only('store');
[Source]
Or use except()
for the opposite. [Source]
12.
Use the exists
validation rule to check whether a given field exists in a specified database table. [Source]
13.
You can use whereSlug
and whereId
as aliases for where('slug', $slug)
and where('id', $id)
in Eloquent calls. [Source]
14.
To change the key used in route-model binding, override the getRouteKeyName
method in your model. E.g.
public function getRouteKeyName()
{
return 'slug';
}
[Source]
15.
If you need some data to be passed to all your views, you don't need to use a view composer, you can use the much simpler share
method. [Source]
16.
JW says that he uses "query objects" - classes that correspond to a complex SQL query - in the Laracasts codebase. E.g. App/Queries/ThreadsQuery
. [Source]
17.
You can retrieve only a portion of the request data using $request->only(['foo', 'bar'])
. It saves requesting everything then filtering through it. [Source]
18.
Performing count()
on a relationship given as a dynamic property (e.g. $thread->replies->count()
) requires a redundant SQL query that will fetch all the replies. If we substitute the method for the property (e.g. $thread->replies()->count()
) the SQL query only gets the count of the replies. As the docs say: "all types of Eloquent relationships also serve as query builders, allowing you to continue to chain constraints onto the relationship query before finally executing the SQL against your database". [Source]
19.
The Eloquent load
method permits "lazy eager loading", i.e. eager loading a relationship after the model has been retrieved. [Source]
20.
The Eloquent withCount
method is a way of counting the results of a relationship without loading them. [Source]
21.
Eloquent global query scopes
define constraints applicable to all queries on that model. [Source]
22.
Laravel's str_plural
helper returns the appropriate plural form of a string. [Source]
23.
The toSql
method returns the SQL query of a given Eloquent query. [Source]
24.
The whole section on polymorphic relations was new to me. [Source]
25.
In database migrations, create a unique combined index with unique
; e.g. $table->unique(['user_id', 'favorited_id', 'favorited_type']);
. [Source]
26.
JW: "One of the downsides" of Eloquent is its tendency to hide the presence of multiple SQL queries when accessing relationships between models. [Source]
27.
Global eager loading in an Eloquent model is possible by overriding the $with
property. E.g.
protected $with = ['owner']
[Source]
28.
You can use model events to (among other things) delete related models when a model is deleted. E.g. in the boot
method of a model:
static::deleting(function($thread) {
$thread->replies()->delete();
});
[Source]
29.
The method_field helper spoofs HTTP verbs in forms. E.g.
{{ method_field('DELETE') }}
[Source]
30.
Use @forelse
and @empty
to show messages when a collection of data to be looped through is empty. [Source]
31.
The artisan make:policy
command generates an empty policy class. The flag --model=Foo
adds some boilerplate. [Source]
32.
The @can
and @cannot
Blade directives check whether the logged in user can perform a given action. [Source]
33.
To give a super admin permission to perform all actions, either use the before
method on a policy, or to apply this to all policies use the Gate class in the boot
method of AuthServiceProvider
. E.g.:
Gate::before(function($user) {
if ($user->name === 'super admin') return true;
});
[Source]
34.
Not Laravel specific, but to get the short name of a class (i.e. without the namespace), this can be done with the ReflectionClass method getShortName
. E.g.:
$name = (new \ReflectionClass($thread))->getShortName();
[Source]
35.
You can move boot
methods from specific models to traits they use with the function boot[TraitName]
in the trait. E.g.:
protected static function bootRecordsActivity()
{
}
[Source]
36.
You can pass a closure to the groupBy
method. [Source]
37.
Chain the with
method to a redirect response to flash data to the session. [Source]
38.
Use the @includeIf
Blade directive to only include a view if it exists. Inspired by (this video).
39.
Use inline templates to use a Blade file (or other HTML) as a Vue component's template. [Source]
40.
Use the v-cloak
directive, in combination with a CSS rule like [v-cloak] { display: none; }
, to hide a Vue component until it's loaded. [Source]
41.
Use the appends
property to add Eloquent attributes to a model's array or JSON representation. [Source]
42.
Use the Eloquent fresh
method to reload a model from the database. [Source]
43.
For a model event (e.g. deleting
) to fire, the model has to be deleted (or updated etc.), rather than building an SQL command with the query builder. I.e. this:
$this->favorites()->where($attributes)->get()->each(function($favorite) {
$favourite->delete();
});
instead of
$this->favorites()->where($attributes)->delete();
[Source]
44.
Higher order messages are "properties" to make performing tasks on collections more readable. E.g., using the each
higher order message:
$this->favorites()->where($attributes)->each->delete();
[Source]
45.
The Vue v-for
directive can take a second argument that supplies the index of an item. E,g.:
<div v-for="(reply, index) in items"
<reply :data="reply" @deleted="remove(index)"></reply>
</div>
[Source]
46.
Use Vue mixins for creating reusable functionality. [Source]
47.
A Vue watcher can be used to perform some new function when a component's data changes. [Source]
48.
In Vue, the .prevent
event modifier will perform the equivalent of preventDefault()
. E.g. @click.prevent
. [Source]
49.
increment
and decrement
are database query builder helpers to increment or decrement the value of a column. [Source]
50.
To append an attribute to a single instance of an Eloquent model (rather than to every instance as in 41 above), use the append
method. [Source]
Click here for Part 2, covering the second half of the series.