Category: Laravel

Gainslog now makes daily backups to Google Drive

Thanks to this great tutorial on how to extend Laravel’s storage to Google Drive, Gainslog now pushes daily updates to Google Drive.

Following the tutorial, one issue people run into is a File not found error when running the backup command.

You can get around it by setting the backup folder name to an empty string – but that will place all the backup zip files directly into the storage/app folder.

One way to make sure all backups go into a single folder is to setup a new local disk inĀ filesystem.php and update the backup.php config to use that one instead.


// config/filesystems.php
'local' => [
    'driver' => 'local',
    'root' => storage_path('app'),
],

'local-backups' => [
    'driver' => 'local',
    'root' => storage_path('app/backups'),
],

// config/backup.php
'destination' => [

    /*
     * The filename prefix used for the backup zip file.
     */
    'filename_prefix' => 'backup-',

    /*
     * The disk names on which the backups will be stored.
     */
    'disks' => [
        'local-backups',
        'google'
    ],
],

The build cycle of a Laravel application feature

I wrote about having a crossword approach to writing software, but I haven’t given any real-world examples. I only said it’s better because it doesn’t let you wander around and dip your fingers into everything. It keeps you focused on what’s needed now.

Let’s say you’re building a content management system using Laravel. Here are the steps I’d take to build it:

1. Write the code to display the posts in some kind of list. You can get this done by just having a seeder in place and a simple query on the index method. Don’t worry about filtering or anything like that.

2. Write the code to show, store, update, and delete posts. Completely ignore any validation and authorization for now.

3. Notice I haven’t said anything about writing views. No html&css should be written so far. Views can wait.

4. Go back to the create and update methods and add some validation rules.

5. Sprinkle some authorization too. Don’t forget about the delete method.

6. If you need to filter the posts in any way, now is probably a good time to do it. Don’t forget to write tests that cover every filter.

7. Now that all the logic is in place and thoroughly tested, we can start focusing on the views. Style the index and the show views.

8. Add the create and edit post endpoints and style their views too.

9. Sprinkle some javascript where’s needed. Don’t over do it.

10. Pick the next feature. Make sure it touches as few things as possible.

Rinse and repeat.

Eloquent trick #1: Replace conditionals with “when”

It happens very often that we want to apply certain eloquent query conditions based on what a request sends in. Sometimes it’s a “search by name” thing, other times we just need to filter the records based on a status column.

Usually it looks like this:

public function index(Request $request) 
{
    $posts = Post::newQuery();

    if ($request->term) {
        $posts->where('name', 'LIKE', "%{$request->term}%");
    }

    if ($request->status) {
        $posts->where('status', $request->status);
    }

    return view('posts.index', ['posts' => $posts->paginate()])
}

I recently discovered there’s an alternative to using conditionals. The “when” method executes a callback (second parameter) when the first parameter evaluates to true.

public function index(Request $request) 
{
    $posts = Post::when($request->term, function($q, $term) {
        $q->where('name', 'LIKE', "%{$term}%");
    })->when($request->status, function($q, $status) {
        $q->where('status', $status);
    });

    return view('posts.index', ['posts' => $posts->paginate()])
}

It’s not all that better if you ask me. Yes, it hides the conditionals, but it also makes the code harder to read. Especially if you have more conditions to add.

I’d use this “when” approach for single conditions only.

public function index(Request $request) 
{
    $posts = Post::when($request->term, function($q, $term) {
        $q->where('name', 'LIKE', "%{$term}%");
    });

    return view('posts.index', ['posts' => $posts->paginate()])
}