/ laravel

Useful Laravel Snippets

Laravel has a ton of useful features built in. Most of the documentation however, is geared towards a classic application setup. They assume you're going to use Laravel's own view system. These snippets I'm about to share are a valuable addition to any app, but may be geared towards those who have Laravel act as an API and nothing more.

With that being said, I've only been working on Laravel apps for a short time. These are just a few of the coolest things I have come up with in the past couple months. As I work more with Laravel, I'll be updating this post :)

CORS middleware

If you're using laravel as an api, you probably need to setup CORS.

Create a new file: app\Http\Middleware\Cors.php

<?php

namespace App\Http\Middleware;

use Closure;

class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', '*')
            ->header('Access-Control-Expose-Headers', 'authorization');
    }
}

Add this: \App\Http\Middleware\Cors::class, to the $middelware array in app\Http\Kernal.php and you now have cors headers set. This is useful if you are building an api in Laravel and need to accept requests from other sites, or possibly a frontend application on a separate domain. If you need to add other cors headers, you can review all of them here

Request Logging

One app I'm working on requires logging every action on the site. This can be accomplished with another middleware:

<?php

namespace App\Http\Middleware;

use Closure;
use Auth;
use App\Models\Log;
use App\Services\RequestLogger;

class LogRequest
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        Log::create([
            'type' => $type,
            'ip' => $request->ip(),
            'url' => $request->url(),
            'user_id' => Auth::user() ? Auth::user()->id : null,
            'request_body' => json_encode($request->input()),
            'request_method' => $request->method(),
            'responded_with' => $response->status(),
            'user_agent' => $request->header('User-Agent'),
            'success' => $response->status() === 200,
        ]);
        return $response;
    }
}

Edit: A few people have mentioned that storing the request input would store the user's password on the login page. This is true and something I wasn't thinking about. You should be able to use $request->except(['password']) to prevent this issue.

Add this to your global middleware, create a Log model, and you're all set!

This can be extended much further. For instance, I'm also using this table to block users after x login attempts in a certain time period.

Time ago in words

Every app has some sort of user inserted data. Chances are, you don't want to display a long timestamp to your users. Here's how quickly you can do it in a model:

Model.php

protected $appends = [
    'created_at_ago',
    'updated_at_ago'
];

public function getCreatedAtAgoAttribute() {
    return $this->created_at->diffForHumans();
}

public function getUpdatedAtAgoAttribute() {
    return $this->updated_at->diffForHumans();
}

Awesome, now when we hit our api endpoint with this model included, model.created_at_ago will return a readable time, like 1 day ago.

Handling Roles

I've found this to be a nice way to implement a roles system, along with policies:

First, let's create a trait called Roles. This can be anywhere you'd like. Generally I make a traits folder inside of my app folder and put them there.

<?php

namespace App\Traits\User;

use App\Models\Role;
use App\Models\Permission;
use Illuminate\Database\Eloquent\ModelNotFoundException;

trait Roles {
    public function roles()
    {
        return $this->hasMany('App\Models\Role');
    }


    //attach a role by name to a user
    public function attach($role_name) 
    {
        try{
            $role = Role::where('name', '=', $role_name)->firstOrFail();

            //if the role already exists on a user, return false
            if($this->isRole($role->name)) {
                return false;
            }

            Permission::create([
                'user_id' => $this->id,
                'role_id' => $role->id,
            ]);

            return true;
        }
        catch(ModelNotFoundException $e) {
            return false;
        }
    }

    //check if a user is a certain role
    public function isRole($role_name) 
    {
        try{
            $role = Role::where('name', '=', $role_name)->firstOrFail();
            Permission::where([
                'user_id' => $this->id,
                'role_id' => $role->id,
            ])->firstOrFail();

            return true;
        }
        catch(ModelNotFoundException $e) {
            return false;
        }
    }
}

This assumes you have a Permission model with user_id and role_id. The role_id would link to a Role model that includes a name and anything else associated with the role.

Now, we can very easily assign and check for roles, assuming you added this trait to the User model. Adding use \Roles to the top of the model will implement it.

Checking for a role inside a policy:

/**
     * Determine whether the user can view the post.
     *
     * @param  \App\User  $user
     * @param  \App\Study  $study
     * @return mixed
     */
    public function view(User $user, Post $post)
    {
        return $user->isRole('admin');
    }

This keeps policies nice and clean. Of course, attaching a role is just as easy: $user->attach('admin')

Turning off Validation Redirects

Laravel has automatic redirects for validation rules. This is nice if you're using Laravel for generating your views, but in the case of an api only app, it might be better to always return the errors without redirecting anywhere. We can do this by making our own BaseRequest in the app\Http\Requests folder.

<?php
namespace App\Http\Requests;

use Illuminate\Http\JsonResponse;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exception\HttpResponseException;

abstract class BaseRequest extends FormRequest
{    
    /**
     * Handle a failed validation attempt.
     *
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     * @return mixed
     */
    protected function failedValidation(Validator $validator)
    {
        throw new HttpResponseException($this->response([
            'errors' => $this->formatErrors($validator)
        ]));
    }
}

Now, set all of your requests to extend from BaseRequest instead of FormRequest, and you're good to go!

Conclusion

I hope you found at least one of these things to be useful. There's many incredibly useful things Laravel allows you to do. Feel free to leave a comment with your favorite additions to Laravel below!

On a huge side note, I have a huge problem. I'm generally not the type to show OCD symptoms, but this bothers me:

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Exception\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;

You know what makes me feel better? This:

use Illuminate\Http\JsonResponse;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exception\HttpResponseException;

Yes, I order use statements from shortest to longest. It looks cleaner. Does anyone else have this problem? Or do you have this problem now because I brought it up. I'd love to know in the comments.

PS: I just started this blog. If you want to see more posts like this, please subscribe. Thanks :)