Blog

5 Awesome Code Snippets To Enhance Your Next PHP Laravel Project

Don't reinvent the wheel Laravel code snippets

For the uninitiated among you, Laravel, the brain-child of Taylor Otwell, is an open source PHP framework, architected to follow the MVC design pattern.

Since joining Klyp twelve months ago, I’ve been working exclusively with this awesome framework to deliver a number of different client projects. But for all it can do, Laravel still doesn’t do everything (I’ve got to write some code, right?).

So, to help save my developer brethren some valuable time I’ve chosen five of the code snippets that I re-use across all of my Laravel apps. I’ll endeavour to explain what each snippet does and how you’d use it; and perhaps it’ll save you from having to reinvent the wheel.


1. Versioned Assets

First up, we’ll start with a very common, yet basic problem - generating asset URLs.

Laravel already includes an asset() helper function for generating a URL (either HTTP or HTTPS) to an application asset. Amongst other things, I typically use this helper to link to compiled CSS or JavaScript assets in my application’s layout template. But then I kept having an issue where old assets were being cached by the user’s browser after I’d pushed an update to one of my applications.

This little helper function solves this problem by simply appending the file modification timestamp to the end of the asset URL. As soon as your asset is updated, the asset URL will change too, creating a newly unique file path, prompting your browser to download the latest version of the file. Too easy, right?


This helper function should be used in exactly the same way as the existing asset helper.




2. Attributable Trait

Next up, we’re working with models.

I sometimes want to grab a subset of attributes from one of my models or conversely, all but a subset of the attributes. Sounds pretty easy, right? And you’d be right!

You may already be aware of the array_only() and array_except() helper functions, and have used them before. I wrote the Attributable trait to exploit these helper functions within my model classes to return mutated subsets of the model’s attributes.

The methods as they’re written now will return you the mutated attribute values. If you instead want the raw attribute values, simply replace calls to $this->toArray() with $this->getAttributes().

toArray(), $attributes);
    }

    /**
     * Get a subset of attributes from the model except those specified.
     *
     * @param  array|string $attributes
     * @return array
     */
    public function except($attributes)
    {
        $attributes = is_string($attributes) ? func_get_args() : $attributes;
        return array_except($this->toArray(), $attributes);
    }
}

To use this trait, you simply need to include it inside your model or better yet, your parent model class.

only('first_name', 'last_name', 'email');

dd($data);
// ['first_name' => 'Rob', 'last_name' => 'Simpkins', 'email' => 'engage@klyp.co'];


3. Responsible Trait

Sticking with models, I often find that in addition to tracking when a model is created, updated or deleted, it’s also useful to know who performed each of those operations.

I wrote the Responsible trait to record who performed each of these operations using Eloquent’s inbuilt saving and deleting events. The current user ID is ascertained from the Auth facade and set on the model by the appropriate event listener.

created_by = $model->created_by ?: $userId;
            $model->updated_by = $userId;
        }, 10);

        // Register deleting event listener
        static::deleting(function ($model) use ($userId) {
            $model->deleted_by = $userId;
            $model->save();
        }, 10);
    }
}

Simply include the trait inside your model or a parent model class and add created_by, updated_by and deleted_by integer type columns to your model’s database schema.

 'Rob',
    'last_name'  => 'Simpkins',
    'email'      => 'engage@klyp.co',
]);

dd($user->created_by, $user->updated_by);
// '2016-06-20 12:34:56', '2016-06-20 12:34:56'


4. Validatable Trait

Sticking with models still further, we’re going to tackle every developer’s favourite data manipulation task - validation!

I don’t know about you, but I like to keep my model validation rules with their respective models. And that’s exactly what this Validatable trait does.

The trait allows you to define validation rules and optional messages using the rules() and messages() method, which can be overridden in your model. Using Eloquent’s inbuilt saving event, an event listener runs your model data through a Validator instance and against your specified validation rules when you call save(), create() or update().

If the data is valid, the model saves the data. If the data is invalid however, a ModelValidationException is thrown. You can catch this exception and access the validator error messages either from the exception or the model itself.

validate()) {
                // Prepare error
                $error = sprintf('Unable to save %s model. One or more attribute values are invalid.', get_called_class());

                // Log error and validation errors when in debug mode
                if (env('APP_DEBUG')) {
                    Log::error($error, $model->toArray());
                }

                throw new ModelValidationException($model->validator(), $error);
            }
        });
    }

    /**
     * Validate data or model attributes against internal validation rules.
     * The internal validation rules and error messages may be overridden.
     *
     * @param  array  $data
     * @param  array  $rules
     * @param  array  $messages
     * @return boolean
     */
    public function validate(array $data = [], array $rules = [], array $messages = [])
    {
        // Prepare data and validation rules
        $data = $data ?: $this->attributes;
        $rules = $rules ?: $this->rules();
        $messages = $messages ?: $this->messages();

        // Prepare validator and set errors
        $this->validator = Validator::make($data, $rules, $messages);

        return $this->validator->passes();
    }

    /**
     * Get model validation rules.
     *
     * @return array
     */
    public function rules()
    {
        return [];
    }

    /**
     * Get model validation messages.
     *
     * @return array
     */
    public function messages()
    {
        return [];
    }

    /**
     * Get model validator.
     *
     * @return \Illuminate\Validation\Validator
     */
    public function validator()
    {
        return $this->validator;
    }

    /**
     * Get model validation errors.
     *
     * @return array
     */
    public function errors()
    {
        return $this->validator ? $this->validator->errors()->toArray() : [];
    }

    /**
     * Get model validation errors.
     *
     * @return array
     */
    public function attributesToArray()
    {
        // Call parent
        $attributes = parent::attributesToArray();

        // Append validation errors
        if ($this->errors()) {
            $attributes['_errors'] = $this->errors();
        }

        return $attributes;
    }
}


validator = $validator;
    }

    /**
     * Get model validator.
     *
     * @return \Illuminate\Validation\Validator
     */
    public function validator()
    {
        return $this->validator;
    }

    /**
     * Get model validation errors.
     *
     * @return array
     */
    public function errors()
    {
        return $this->validator->errors();
    }
}

Simply include the trait inside your model or a parent model class and define your validation rules and optional messages. Then wrap your model save operations in a try/catch block.

 ['required', 'exists:users,id'],
            'comment'    => ['required', 'max:2000'],
            'created_by' => ['sometimes', 'numeric', 'exists:users,id'],
            'updated_by' => ['sometimes', 'numeric', 'exists:users,id'],
            'deleted_by' => ['sometimes', 'numeric', 'exists:users,id'],
        ];
    }
}


all());
            $comment->user()->associate($this->_user);

            // Save comment
            $status = $comment->save();
        }
        catch (ModelValidationException $exception) {
            // Prepare status and validation errors
            $status = false;
            $errors = $comment->errors();

            return response()->json(compact('status', 'errors'), 400);
        }

        // Prepare model and message for response
        $model = $comment;
        $message = trans('lang.comment.create.' . intval($status));

        return response()->json(compact('status', 'message', 'model'));
    }


5. Authorizable Trait

Finally, we’re going to look at authorization, and how we can streamline these checks in our controllers.

If you haven’t already explored the Laravel authorization documentation, I would highly recommend it. If you’re not already using it, there’s a huge reservoir of potential going untapped. In a nutshell, through the use of policies, Laravel allows you to explicitly state who can do what. A simple example would be, who can create, update or delete a comment.

I wrote the Authorizable trait to try and streamline these checks for the currently authorized user when performing actions on a single model, or a collection of models.

Essentially these methods just grab the current user, and using the Laravel Authorizable trait, check whether the user is permitted to perform the specified action on the specified model(s). If all is well, the methods will return true. If not, an AuthorizationException is thrown.

can($ability, $model)) {
            return true;
        }

        throw new AuthorizationException('You are not authorised to access the requested resource.');
    }

    /**
     * Check whether current user is authorised to perform given ability on all models in collection.
     *
     * @throws \Illuminate\Auth\Access\AuthorizationException
     *
     * @param  string $ability
     * @param  \App\Collections\Collection $collection
     * @return boolean
     */
    protected function authorizeCollection($ability, Collection $collection)
    {
        // Check whether current user is authorised to perform given ability on each model
        $collection->each(function ($model) use ($ability) {
            $this->authorizeModel($ability, $model);
        });

        return true;
    }
}

Include the trait inside your controller or a parent controller class, and call the authorization methods as required.

I tend to use a global exception handler to catch an AuthorizationException when thrown, but you could catch this in your controller too, by adding another catch block.

id === $comment->user_id || $current->is_admin);
    }

    /**
     * Check whether the comment can be updated by the current user.
     *
     * @param  \App\Models\User $current
     * @param  \App\Models\Comment $comment
     * @return boolean
     */
    public function update(User $current, Comment $comment)
    {
        return ($current->id === $comment->user_id || $current->is_admin);
    }

    /**
     * Check whether the comment can be deleted by the current user.
     *
     * @param  \App\Models\User $current
     * @param  \App\Models\Comment $comment
     * @return boolean
     */
    public function delete(User $current, Comment $comment)
    {
        return ($current->id === $comment->user_id || $current->is_admin);
    }
}


authorizeModel('update', $comment);

            // Update comment
            $status = $comment->update($request->all());
        } 
        catch (ModelValidationException $exception) {
            // Prepare status and validation errors
            $status = false;
            $errors = $comment->errors();

            return response()->json(compact('status', 'errors'), 400);
        }

        // Prepare model and message for response
        $model = $comment;
        $message = trans('lang.comment.update.' . intval($status));

        return response()->json(compact('status', 'message', 'model'));
    }
}

If you have any go-to code snippets that you use across your projects, or you have any questions or comments about the snippets covered in this post, please do give me a shout. I’m always keen to learn and to help others do the same :) hit me up at engage@klyp.co!


Rob Simpkins Rob Simpkins

About the author:
Rob is one of Klyp's awesome web developers and supreme overlord of cardboard forts. He dislikes writing blog posts, so if you've reached this far, well done (unless you skipped to the bottom)! Connect with him on Linkedin if you too like building cardboard forts (or web development) :)