Lessons from Laracasts, Part 1

Let'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.