Laravel features you may not know about

Laravel features you may not know about Thumbnail image

Laravel features you may not know about Table of contents

  1. Introduction
  2. Laravel tips and tricks that you might not know
    1. Use the artisan down command to put the Laravel app down, but allow access to certain IP(s)
      1. --message="something here"
      2. --retry
    2. Need to do a query builder 'where' query on a couple of columns? Try this clean, one-method way:
    3. Eloquent's find() can accept multiple rows
    4. Did you know that relations such as belongsTo can provide a default model if the relationship returns null?
    5. Use the render() method on Exceptions to render a HTTP response to the user
    6. The cache's rememberForever($key, function() { return 'something'; }) function
    7. You can pass a file path to your route's group() to include that file
    8. Eloquent: get a fresh version (fresh()) of the current model and duplicate rows (replicate())
    9. Some Routing static methods that are underused...
      1. The main and commonly used routing methods...
      2. Redirecting URLs from within your routes file (web.php)
      3. Does your controller method just return a view? Skip the controller, return the view from the routes file...
      4. Route name prefixes
      5. Fallback Routes
      6. Want to find more?
    10. Use wildcard (*) checks for the current route
    11. Auth::id() instead of Auth::user()->id
    12. Simple Pagination
    13. Laravel's tap() method
    14. Most of Artisan's make: commands will create files in subdirectories you tell it to (and will correctly namespace it)
    15. Quickly log things with logger() and info()
    16. Set model properties such as $perPage
    17. Eloquent Relationships: Only return models if they actually have a relationship row with has()
    18. optional()
    19. Do you use View Composers?
    20. Use array_wrap() to ensure that you are working with an array
    21. Eloquent: Add data to the output from toArray() (or toJson())
    22. Pluralise strings with str_plural()
    23. Automatically fire events in your Eloquent models
    24. Use URL::signedRoute() to make Laravel verify a URL has not been modified since it was created
    25. Use Pagination's onEachSide(5) to define how many links either side of the current page are displayed
    26. Use withCount('relation_name') to get the count(*) for a relation
    27. Use action() to generate a URL to a controller's action
    28. Use Blade Components/slots, Component Aliases and Include Aliases
      1. Blade Component Aliases:
      2. Include Aliases
    29. Include a view in Blade for every item in an array (or collection) with @each
    30. View::first([...]) to return the first view from an array that exists (and Blade's @includeFirst([...]) similar directive)
    31. Injecting services into your blade files, from within a blade file
    32. Use the fluent dump() method within your chained Collection calls
    33. Use Form::open with a route param, even if there are parameters for that route (Form::open(['route' => ['route.name.here', 'param-here']))
      1. Other Laravel Collective's Form Snippets
    34. Laravel's Dump Server
    35. Custom if statements in Blade
    36. Use the prepareForValidation() in your Form Requests
    37. Some other useful functions in Requests
      1. Request::create()
      2. Request::method()
      3. Request::fullUrl()
      4. Request::segment()
      5. Request::path()
      6. Request::ajax()
      7. Request::hasPreviousSession()
      8. Request::getClientIp()
      9. Request::getUserInfo()
  3. Ok, that's it!

    Seen this article before? skip straight to the newer tips that I've added in Feb 2019. Last updated: 2nd Feb 2019.

    Introduction

    Here are some features from Laravel that you may not be aware of. They're not exactly anything advanced or hidden, but I don't really notice that these functions get used or referenced very often. (Maybe they are though, and I've just not noticed it.)

    I decided to post this as a couple of people replied to a comment on Reddit that I made saying that they were not aware of the php artisan down option to whitelist allowed IP addresses/ranges. I'll try and keep this list updated as I come up with new ideas for this. Last updated: 2nd Feb 2019.

    Laravel tips and tricks that you might not know

    Use the artisan down command to put the Laravel app down, but allow access to certain IP(s)

    artisan down --allow=123.123.123.123

    The php artisan down command is useful to use when you need to put your Laravel site down for maintenance or when upgrading the system. However, without using the --allow option you have no real way to test it as a regular user. To get around this you can provide a whitelist of IP addresses to allow.

    You can also provide multiple IP addresses, or even IP ranges:

    php artisan down --allow=127.0.0.1 --allow=192.168.0.0/16

    Some other useful options for the down command include:

    Screenshot of the message: you can write anything here. It shows on the Laravel down page
    --message="something here"

    You can provide a custom message to show users, by using the --message="..." option. This will show that message to your users while the site is down. You can modify it further by copying the 503.blade.php to your views dir. The default message is 'Sorry, we are doing some maintenance. Please check back soon.'.

    php artisan down --message="You can write anything here"

    --retry

    You can set the retry value, which will be used for the Retry-After HTTP header's value.

    php artisan down --retry=60

    Need to do a query builder 'where' query on a couple of columns? Try this clean, one-method way:

    Let's say you want to do the following SQL query:

    1.     SELECT * FROM `users`
    2.     WHERE
    3.         `name` = 'some_name'
    4.         AND `email` = 'some_email'
    5.     LIMIT 1

    You can achieve this with one 'where' method call. Eloquent will work out that the "and" in the middle means two seperate where clauses:

    1. \App\User::whereNameAndEmail('some_name','[email protected]')->first();
    2.  
    3. // the above has the exact same result as:
    4. \App\User::where('name', 'some_name')->where('email', '[email protected]_email')->first();
    5. // also same as:
    6. \App\User::where(['name' => 'some_name', 'email' => '[email protected]'])->first();

    (change Name and Email as required - the important part is the And between them. Laravel will automatically work out what columns your where statement refers to)

    And as well as "and", you can also do an "or" like this:

    1.  \App\User::whereFooOrBar('foo value','bar value')->first();

    Which will produce the following SQL query:

    1.     SELECT * FROM `users`
    2.     WHERE
    3.         `foo` = 'foo value'
    4.         OR `bar` = 'bar value'
    5.     LIMIT 1
    Eloquent's find() can accept multiple rows

    You can use the query builder's find() method in the following way:

    1. $rows = SomeEloquentModel::find([1,4,6,11]);

    If find() is passed an array, it will return a collection of Eloquent model rows with those IDs (of course, if those rows exist...).

    Also applies to findOrFail(). They both (eventually) just call findMany() if passed an array of IDs to find.

    Did you know that relations such as belongsTo can provide a default model if the relationship returns null?

    I'm sure many others are aware of this, but I only found out about this very recently.

    If you have a relation (a belongsTo, MorphOne or HasOne type), it might sometimes return null. In that case, you can provide a default with the withDefault() method.

    It will automatically work out what type of object to return, but you can also set some default attributes.

    Here are a few examples:

    1. public function user()
    2. {
    3.     return $this->belongsTo('App\User')->withDefault();
    4. }
    5.  
    6.     //or
    7.  
    8. public function user()
    9. {
    10.     return $this->belongsTo('App\User')->withDefault([
    11.         'name' => 'Guest Author',
    12.     ]);
    13. }
    14.  
    15.     // or
    16.  
    17. public function user()
    18. {
    19.     return $this->belongsTo('App\User')->withDefault(function ($user) {
    20.         $user->name = 'Guest Author';
    21.     });
    22. }

    For the implementation details check out SupportsDefaultModels.php.

    Use the render() method on Exceptions to render a HTTP response to the user

    If you run the following command:

    php artisan make:exception --render SomeExceptionName

    It will create a new exception class in your exceptions directory, with a blank render($request, Exception $exception) method.

    If an exception is thrown and handled by your app/Exceptions/Handler.php file, it will check for method_exists($exception, 'render'). If it exists, it will call that method on the exception and return it to the user.

    Here is an example of a render() method on an exception, which will return a view response:

    1. /**
    2. * Render the exception into an HTTP response.
    3. *
    4. * @param    \Illuminate\Http\Request
    5. * @return  \Illuminate\Http\Response
    6. */
    7. public function render($request)
    8. {
    9.     response()->view('errors.custom.myexception', [], 500);
    10. }

    You can use this method to return a response (such as a view) that will be displayed to the user.

    Before I knew about this, I would hard code some logic in app/Exceptions/Handler.php when I needed to show a certain response to the user for a particular exception. It is much neater, in my opinion, to hard code it in the exception itself than to have a messy Handler.php file.

    The cache's rememberForever($key, function() { return 'something'; }) function

    If you use the cache feature, you almost certainly are doing something like this:

    1. $value = Cache::remember('users', $minutes, function () {
    2. return DB::table('users')->get();
    3. });

    But did you know you can also do this:

    1. $value = Cache::rememberForever('users', function () {
    2. return DB::table('users')->get();
    3. });

    This will get an item from the cache, or store the default value forever.

    You can pass a file path to your route's group() to include that file

    If your routes file is a bit messy, you can stick them in their own file and include the file with a call to group().

    1. Route::middleware(SomeMiddleware::class)
    2.     ->group(__DIR__"/SomeGroup.php")

    As far as I can tell this isn't mentioned in the docs (or I'm being blind at 1 AM and can't find it). Check out the group($callback) method on RouteRegistrar.

    (You could just do a normal require() call as well...)

    Eloquent: get a fresh version (fresh()) of the current model and duplicate rows (replicate())

    Use ->fresh() to query the database and get a fresh version of the current item.

    1. $user = \App\User::first();
    2. $user->name = "Something new";
    3. $user = $user->fresh(); // note that it returns the fresh value, it doesn't affect the current model
    4. dump($user->name); // the original name, not 'something new'

    If you want to rehydrate the existing model, then use refresh():

    1. $flight = App\Flight::where('number', 'FR 900')->first();
    2. $flight->number = 'FR 456';
    3. $flight->refresh();
    4. $flight->number; // "FR 900"

    If you need to duplicate a Eloquent object, use:

    1. $new = SomeModel::first()->replicate();
    2. $new->save()

    The replicate method lets you provide an array of attributes it should ignore when duplicating ($except=[]), for example:

    1. $new = User::first()->replicate(['password']); // replicate everything APART FROM the password attribute
    2.  
    3. $new->save(); // $new will have all of the attributes as the first User row, but the 'password' attribute won't be there.
    Some Routing static methods that are underused...
    The main and commonly used routing methods...

    There are 6 'main' routing methods (which almost every web app uses):

    1. Route::get($uri, $callback);
    2. Route::post($uri, $callback);
    3. Route::put($uri, $callback);
    4. Route::patch($uri, $callback);
    5. Route::delete($uri, $callback);
    6. Route::options($uri, $callback); // rarely used (apart from CORS?) in my experience

    Plus the following (quite commonly used in my experience):

    • Route::match(['get', 'post'], '/', $callback); if you want the same URI to be available for multiple HTTP methods,
    • Route::any('foo', $callback); if you want any and all HTTP methods to apply for a route

    All of the above, plus a few others such as Route::group(...) are pretty common. But there are a whole bunch of other very useful routing methods that I tend to not really notice very often. They include the following:

    Redirecting URLs from within your routes file (web.php)

    I've seen some pretty big Laravel projects that had half a dozen routes pointing to their own methods on a RedirectionController controller, that simply returned a redirection response. But you can redirect direct in the routes file:

    1. // if someone visits /here, they will get a HTTP redirect to /there
    2. Route::redirect('/here', '/there', 301); // 301 Moved Permanently

    Note: the third param is optional, and it defaults to 302 (temporary) redirect.

    Does your controller method just return a view? Skip the controller, return the view from the routes file...

    If your controller's method is really simple and just returns a view, then you might be able to use the following:

    1. Route::view('/welcome', 'welcome'); // same as returning view("welcome") in a controller
    2. Route::view('/welcome', 'pages.welcome', ['name' => 'WebDevEtc']); // same as returning view("pages.welcome")->withName("WebDevEtc") in a controller
    Route name prefixes

    If you have a bunch of routes, you probably name each of them with things like:

    1.  Route::get("/","[email protected]")->name("blog.show");

    You can avoid having to add the "blog." prefix to each name by using route name prefixes.

    1.     Route::name('blog.')->group(function () {
    2.         Route::get('show', function () {
    3.                     // Route assigned name 'blog.show'...
    4.             })->name('show');
    5.     });

    I have to be honest, I'm not actually keen on this, as it makes searching for a route name a tiny bit more complicated, but it can clean up a busy routes file.

    Fallback Routes

    You can use Route::fallback(...), that will be used if no other routes matched. This is normally used to show a 404 error but can be useful in other situations as well.


    Want to find more?

    There are lots of methods not mentioned here when it comes to routing. Check out the documentation on laravel.com.

    Use wildcard (*) checks for the current route

    If you have some kind of navigation and want to add a class='active', you might be tempted to do something like if( request()->is('control-panel') || request()->is('control-panel/change-email') || request()->is('control-panel/edit-profile') { ... } to check the current URI (or passing an array to is()).

    However, you can just use a wildcard:

    1.     if (request()->is("control-panel*")) { ... }

    You can use routeIs() in a similar way, but with the route name. (You can use this with the above tip (route name prefixes) to ensure that it always matches correctly.)

    Auth::id() instead of Auth::user()->id

    I see Auth::user()->id all of the time (and I had to admit that I do it myself quite a bit), but it is much quicker (to type) Auth::id() to get the ID of the user (or null if not logged in).

    As pointed out on Reddit, rather than doing \Auth::user(), you can just do auth() and use the auth helper function. (Full example: auth()->id())

    Simple Pagination

    The normal pagination will count how many total rows there are and calculate the maximum number of pages. On large datasets, this isn't a good idea. The simple pagination option will only display a previous and next link and does a far quicker query on your database (as it doesn't need a full count of the number of rows).

    You use it in the same way as the normal Laravel pagination, but just call simplePaginate() instead of paginate().

    1. $users = DB::table('users')->simplePaginate(15);

    Did you also know that it can output JSON with full pagination data?

    1. {
    2.     "total": 50,
    3.     "per_page": 15,
    4.     "current_page": 1,
    5.     "last_page": 4,
    6.     "first_page_url": "http://laravel.app?page=1",
    7.     "last_page_url": "http://laravel.app?page=4",
    8.     "next_page_url": "http://laravel.app?page=2",
    9.     "prev_page_url": null,
    10.     "path": "http://laravel.app",
    11.     "from": 1,
    12.     "to": 15,
    13.     "data":[
    14.         {
    15.             // Result Object
    16.         },
    17.         {
    18.             // Result Object
    19.         }
    20.     ]
    21. }

    I recommend reading everything on the docs if you use pagination (which most of us do), as there are quite a few options when it comes to using the built-in pagination.

    Laravel's tap() method

    Although I've noticed this helper function being used quite a bit, I think it is underused, although I have to admit that I very rarely have used it myself. The tap($val, $callable) function is normally used in a way that lets you run the $callable function with the value of $val (i.e. it will run $callable($val)). Then, whatever the returned value of $callable, it will return $value.

    Here is the source of the function, that will probably explain it better!

    1.         /**
    2.         * Call the given Closure with the given value then return the value.
    3.         *
    4.         * @param  mixed $value
    5.         * @param  callable|null $callback
    6.         * @return  mixed
    7.         */
    8.         function tap($value, $callback = null)
    9.         {
    10.             if (is_null($callback)) {
    11.                 return new HigherOrderTapProxy($value);
    12.             }
    13.  
    14.             $callback($value);
    15.  
    16.             return $value;
    17.         }

    (The HigherOrderTapProxy part was added in a more recent version of Laravel - see this Laracasts for a great explanation video)

    An example of where it is used is in the query builder in Laravel:

    1.     public function create(array $attributes = [])
    2.     {
    3.         return tap($this->newModelInstance($attributes), function ($instance) {
    4.             $instance->save();
    5.         });
    6.     }

    Of course, you could just do something like:

    1.     public function create(array $attributes = [])
    2.     {
    3.         $instance = $this->newModelInstance($attributes);
    4.         $instance->save()
    5.         return $instance;
    6.     }

    However, with the tap() function you can write cleaner code, turning multiple lines into nice one-liners.

    Laravel News have a nice write up (better than the official docs so I recommend giving it a read.

    Most of Artisan's make: commands will create files in subdirectories you tell it to (and will correctly namespace it)

    The following will create the directory (if it does not already exist) /app/Models/, and place an empty model PHP file in it called YourModel.php, and it will be correctly namespaced (\App\Models):

    php artisan make:model -m Models/YourModel

    The command above will also create a migration file for you (thanks to the -m option).

    Now the file will exist at app/Models/YourModel.php, and the migration file in your migrations directory.

    Quickly log things with logger() and info()

    Use info("log some info message") and logger("log a debug message"). It is slightly cleaner than app('log')->debug($message) or \Log::debug($message)...

    Set model properties such as $perPage

    Your Eloquent models almost certainly have properties such as: $fillable = ['title', 'description'], $casts = ['some_field'=>'array'] set.

    BTW, if you don't know about $casts = ['some_property' => 'array', 'size_info'=>'object'] then check it out here. There are many options: integer, real, float, double, decimal (you can use it like: decimal:2 to have max of 2 digits), string, boolean, object, array, collection, date, datetime, and timestamp

    But I rarely notice other properties set on models, including:

    $perPage: Set the (default) number of rows per page, when you use Eloquent's pagination. Of course, you can override this in the pagination call.

    1.     /**
    2.      * The number of models to return for pagination.
    3.      *
    4.      * @var  int
    5.      */
    6.     protected $perPage = 15;

    $incrementing: If you have a table without auto-incrementing ID rows then you will want to set the following to false:

    1.     /**
    2.      * Indicates if the IDs are auto-incrementing.
    3.      *
    4.      * @var  bool
    5.      */
    6.     public $incrementing = true;

    $snakeAttributes: If a model's relationships have snake-casing enabled, Eloquent will snake case the keys so that the relation attribute is snake cased in the returned array to the developers.

    1.     /**
    2.      * Indicates whether attributes are snake cased on arrays.
    3.      *
    4.      * @var  bool
    5.      */
    6.     public static $snakeAttributes = true;
    Eloquent Relationships: Only return models if they actually have a relationship row with has()

    If you have two models, let's say BlogPost and Comment. They are linked by a OneToMany relationship. If you want to get all blog posts along with the comments you could do this:

    1.  $postsWithComments = BlogPost::with("comments")->get();

    However, this will return blog posts that have 0 comments. If you only want to get posts that have > 0 comments, you can do this (has()):

    1.  $postsWithComments = BlogPost::with("comments")->has("comments")->get();
    optional()

    I have to admit that optional() is used very often, but I thought I'd just mention it anyway.

    It accepts any value and lets you access properties (or call methods) on that method. If null was given then it will return null for any property or method call.

    You use it like this:

    1.     $a = null;
    2.     $b = new Model();
    3.  
    4.     optional($a)->doSomething(); // null
    5.     optional($a)->someProperty; // null
    6.  
    7.     optional($b)->doSomething(); // calls doSomething on the Model object
    8.     optional($b)->someProperty; // the someProperty on the model object (if it exists)

    It is useful when you don't know if something will have a value or not. I have used it in the past when dealing with an Eloquent row's relation - it might not exist. Rather than wrap some code in if/else, you can do something like this:

    1.     $blogPost = BlogPost::first();
    2.     echo "<div class='author_box'>"
    3.          . optional($blogPost->author)->name
    4.         . "</div>";

    (simplified example... but it would either print the author name, or nothing)

    Do you use View Composers?

    View composers will bound data to a view every time that view file is rendered.

    Or, to put another way: You can tell Laravel to send an array of parameters to a view, every time the view is rendered.

    Let's say you had a alert.error view (resources/views/alerts/error.blade.php), and inside that you wanted a specific variable $error_prefix (some string - but the details don't matter here).

    Inside a service provider:

    1. View::composer('dashboard', function (View $view) {
    2.     $view->with(
    3.         'error_prefix',
    4.         'Some prefix that you needed in your blade file'
    5.     );
    6. });

    (You can also set up a whole composer class, and run View::composer('error_prefix', ErrorComposer::class), and within that ErrorComposer have a compose(View $view) method)

    Use array_wrap() to ensure that you are working with an array

    Sometimes you need to work with an array, but the supplied data might be a different data type - in which case you will want to wrap it in an array (it will be the single element in the array). This is simple to do:

    1. // could return an array, could return a string, etc. It could be anything
    2. $value = foo();
    3.  
    4. if (!is_array($value)) {
    5.     // if it turns out it isn't an array, wrap it in an array
    6.     $value = [$value];
    7. }
    8.  
    9. // now you can be sure you have an array, so do whatever you need:
    10. foreach($value as $row) { /* ... */ }

    And although the snippet above could be simplified (and written on one line), it is a bit neater to use Laravel's helper function array_wrap()

    1. // could return an array, could return a string, etc. It could be anything
    2. $value = array_wrap(foo());
    3.  
    4. // now you can be sure you have an array, so do whatever you need:
    5. foreach($value as $row) { /* ... */ }

    More examples:

    1. $value = array_wrap(['a','b']); // ['a', 'b'] (no change)
    2. $value = array_wrap('asdf'); // ['asdf'] (wrapped the string in an array)
    Eloquent: Add data to the output from toArray() (or toJson())

    Let's say you have a User model, and you know that the user with ID of 1 is admin. When you do a toArray() or export it to JSON, you might want to output something to indicate if it is an admin. With a little bit of code you can get Eloquent to automatically add this.

    You can do something like this:

    1. class User extends Model
    2. {
    3.  
    4.     // append the 'is_admin' attribute when doing toArray()/toJson()...
    5.     protected $appends = ['is_admin'];
    6.  
    7.     // and this is where it will get that data from:
    8.     public function getIsAdminAttribute()
    9.     {
    10.         // returns a bool, true if user.id === 1
    11.         return $this->attributes['id'] === 1;
    12.     }
    13. }

    Note: This assumes that there was no is_admin column on the database table that this row came from. Otherwise it would have been included anyway.

    You can also decide to append data at run time like this:

    1. return $user->append('is_admin')->toArray();
    2.  
    3. // or
    4.  
    5. return $user->setAppends(['is_admin'])->toArray();

    See more here.

    Pluralise strings with str_plural()

    I think that this function is quite commonly used - it is quite a handy and useful function.

    Sometimes you need to output something like "5 active posts" (plural), or "1 active post" (singular). You can write some quick if/else logic (or use the ternary operator) and handle it that way. Or use str_plural().

    1. $my_bottles = [1,2,3];
    2. echo "I have " . count($my_bottles). " " . str_plural('bottle', count($my_bottles));
    3.  
    4. $your_bottles = [1];
    5. echo "You have " . count($your_bottles). " " . str_plural('bottle', count($your_bottles));

    The above would output 'I have 3 bottles' (plural!) and 'You have 1 bottle' (singular!). The function handles uncountable words (see the Pluralizer class) such as 'sheep', 'software' so you won't end up with 'sheeps' and 'softwares'. But of course it isn't perfect so you should always check it works correctly with your input. And it only handles English words correctly. I did assume that there might be some extensions to this function to support other languages, but I was unable to find any.

    There is also the opposite function: str_singular()

    1. $singular = str_singular('cars');
    2. // car
    3.  
    4. $singular = str_singular('children');
    5. // child
    Automatically fire events in your Eloquent models

    There are a bunch of events that happen in your eloquent objects:

    • retrieved
    • creating
    • created
    • updating
    • updated
    • saving
    • saved
    • deleting
    • deleted
    • restoring
    • restored

    If you have an event (for example /app/Events/ContactWillBeDeleted.php), you might want to fire it when the 'deleting' eloquent event is fired. To easily to this, add ContactWillBeDeleted to your model's $dispatchesEvents array. See the example:

    1.     protected $dispatchesEvents = [
    2.         'deleting' => \App\Events\ContactWillBeDeleted::class,
    3.     ];

    How this specific example works: When you run Eloquent's delete() method, it runs the following code:

    1.     if ($this->fireModelEvent('deleting') === false) {
    2.         return false;
    3.     }

    (The return false part isn't relevant, but might be interesting to be aware of)

    The fireModelEvent($event, $halt = false) method (see here) will then eventually call $this->fireCustomModelEvent(...).

    fireCustomModelEvent() runs the following code (i've rewritten it, but it does the same thing for the purposes of this explanation. See here for the actual implentation):

    1. /** @var  string */
    2. $eventClassType = $this->dispatchesEvents[$event]; // remember, this was 'deleting' => \App\Events\ContactWillBeDeleted::class, so if $event was 'deleting' then $eventClassType === \App\Events\ContactWillBeDeleted::class
    3.  
    4. /** @var  object */
    5. $eventObj = new $eventClassType($this); // this is now an instance of \App\Events\ContactWillBeDeleted in our example
    6.  
    7. // static::$dispatcher is an instance that implements \Illuminate\Contracts\Events\Dispatcher
    8. // i.e. it is the event dispatcher
    9. static::$dispatcher->$method($eventObj); // this is where it fires the event

    ($method will be either 'until' or 'dispatch'. See the comment on the fireModelEvent method, or look at Dispatcher.php for details - see here)

    To see how this works in more detail, see this line in Eloquent/Concerns/HasEvents.php.

    Use URL::signedRoute() to make Laravel verify a URL has not been modified since it was created

    Laravel allows you to easily create "signed" URLs to named routes. These URLs have a "signature" hash appended to the query string which allows Laravel to verify that the URL has not been modified since it was created. Signed URLs are especially useful for routes that are publicly accessible yet need a layer of protection against URL manipulation.

    For example, you might use signed URLs to implement a public "unsubscribe" link that is emailed to your customers. To create a signed URL to a named route, use the signedRoute method of the URL facade:

    1. use Illuminate\Support\Facades\URL;
    2.  
    3. return URL::signedRoute('unsubscribe', ['user' => 1]);

    If you would like to generate a temporary signed route URL that expires, you may use the temporarySignedRoute method:

    1. use Illuminate\Support\Facades\URL;
    2.  
    3. return URL::temporarySignedRoute(
    4.     'unsubscribe', now()->addMinutes(30), ['user' => 1]
    5. );

    You must remember to validate. You can either do this it in your controllers by calling hasValidSignature:

    1. Route::get('/unsubscribe/{user}', function (Request $request) {
    2.     if (! $request->hasValidSignature()) {
    3.         abort(401);
    4.     }
    5.  
    6.     // ...
    7. })->name('unsubscribe');

    Or you can add the Illuminate\Routing\Middleware\ValidateSignature middleware to your route.

    1. Route::post('/unsubscribe/{user}', function (Request $request) {
    2.     // ...
    3. })->name('unsubscribe')->middleware('signed');

    (This is used by Laravel when creating the email verification link (see here)


    New things, added 2019 - for anyone who has read this article before, here are the new and updated parts that have been updated/sent in from readers:
    Use Pagination's onEachSide(5) to define how many links either side of the current page are displayed

    If you use the very useful and handy ->paginate($per_page) method on the Eloquent Query Builder (to help generate pagination links), you might sometimes wish you could easily set how many links to show each side of the 'current' page.

    Well, now you can with the very easy to use and understand onEachSide() method.

    1.     $posts = BlogPost::paginate(15)->onEachSide(2);
    Use withCount('relation_name') to get the count(*) for a relation

    If you have the following code in your class:

    1.     class BlogPost extends Model {
    2.  
    3.         function comments() {
    4.             return $this->hasMany(Comment::class);
    5.         }
    6.     }

    And given a BlogPost object, you can find out how many comments it has with:

    1.     $blogPosts = BlogPosts::withCount('comments')->get();
    2.     foreach($blogPosts as $blogPost) {
    3.        echo $blogPost->comments_count;
    4.     }

    For more details see here

    Use action() to generate a URL to a controller's action

    If you know that you have a controller named BlogViewController, with a method show, you could execute the following to get a clickable link:

    1. $url =  action(
    2.     ['id' => $blogPost->id]
    3. );
    4. echo '<a href="' . $url . '">View post</a>';
    5.  
    6. // or the following way:
    7. $url_alternative = action(
    8.         [BlogViewController::class, 'show'],
    9.         ['id'=>$blogPost->id]
    10.     );
    11. echo '<a href="' . $url_alternative .'">View post</a>';

    If there is no action deblog_indexfined in your routes that matches that controller/method, then you will get a InvalidArgumentException exception thrown.

    View details here.

    Use Blade Components/slots, Component Aliases and Include Aliases

    Blade's Components are a feature that are similar to how you use sections and extending layouts. They are meant to be used for commonly used parts of your layout, and use the $slot variable for the content that you wish to inject into it.

    Here is an example of a defining a component (notice the $slot variable that is echoed out):

    1. <!-- /resources/views/alert.blade.php -->
    2.  
    3. <div class="alert alert-danger">
    4.     {{ $slot }}
    5. </div>

    And here is how to use it:

    1. @component('alert')
    2.     Uh oh! A bad error just occurred!
    3. @endcomponent

    This would return output similar to:

    1. <div class="alert alert-danger">
    2.     Uh oh! A bad error just occurred!
    3. </div>

    If this doesn't make sense, then you could think of the @component part as working similar to the following:

    1.     @include('alert', ['slot'=>'Uh Oh! A bad error!'])
    Blade Component Aliases:

    The component files don't have to reside in the base directory of resources/views - it works like normal blade views (so you could call @component('layouts.components.alert') ...

    However, if they reside in a subdirectory (like the example in the previous paragraph, which lives in resources/views/layouts/components) you may wish to alias it. In the boot() method of AppServiceProvider you can add the following:

    1.     use Illuminate\Support\Facades\Blade;
    2.     Blade::component('layouts.components.alert', 'alert');

    Then you can just refer to it as @component('alert')...

    Include Aliases

    This could be included as it's own item on this list, but it work in a similar way as blade component aliases.

    If your Blade includes are stored in a sub-directory, you may wish to alias them for easier access. For example, imagine a Blade include that is stored at resources/views/includes/input.blade.php with the following content:

    1. <input type="{{ $type ?? 'text' }}">

    In AppServiceProvider's boot method, you could add the following:

    1. Blade::include('include.input', 'input');

    Then you can use that view (via it's alias) with:

    1. @input(['type' => 'email'])
    Include a view in Blade for every item in an array (or collection) with @each

    If you have an iterable list (such as an array or a Laravel collection), you might wish to include a view for each item:

    1. @foreach(BlogPost::all() as $blogPost)
    2.     @include('blogposts.loop', ['post'=>$blogPost])
    3. @endforeach

    But a much cleaner way to do this is:

    1. @each('blogposts.loop', 'blogPost', 'post')

    Take note of where I used 'post', 'blogPost' in both examples.

    If you like to use

    1. @forelse($rows as $row)
    2.     {{ $row->name }}
    3. @empty
    4.     Sorry, nothing found
    5. @endforelse
    (which will run a foreach, but if it has nothing to loop through it will go to the 'empty' section)

    Well, you can do something similar with the each directive (errors.none_found in the example below is a blade file):

    1. @each('blogposts.loop', 'blogPost', 'post', 'errors.none_found')
    An important thing to note: When you use @each, the views do not share or have access to any other variables from the parent view. If they need access to those, then you should use the normal @foreach, along with regular @include.
    View::first([...]) to return the first view from an array that exists (and Blade's @includeFirst([...]) similar directive)

    Both of these work in a similar way. If you have an array, such as ['actions.something_specific','actions.default'] (assuming that you are hoping that resources/views/actions/something_specific.blade.php exists, but you know that resources/views/actions/default.blade.php exists), you could do the following to try and show the something_specific view first, but falling back to the default one if not.

    1.     // in some controller:
    2.     $data = ['message' => 'something here'];
    3.  
    4.     return view()->first(
    5.     ['actions.something_specific','actions.default'], $data
    6.     );

    Or to do the same thing in a blade file:

    1.     @includeFirst(['actions.something_specific','actions.default'], $data)

    (This could also be achieved by checking if View::exists('actions.something_specific') returns true)

    Some other useful blade directives to check out:

    • @includeIf('view_name', ['some'=>'data']) - include the view if it exists
    • @includeWhen($boolean_value, 'view_name', ['some'=>'data']) - include the view if the first param ($boolean_value) is true
    Injecting services into your blade files, from within a blade file

    Say you have a service SomeCoolService, and you wish to call it's someFunction method inside blade.

    You could use a view composer to set it up so you send a SomeCoolService to that view. Or, you could do everything from within your blade files:

    1. @inject('someCoolService', 'App\Services\SomeCoolService')
    2.  
    3. <div>
    4.     Monthly Revenue: {{ $someCoolService->someFunction() }}.
    5. </div>
    Use the fluent dump() method within your chained Collection calls

    If you are sorting and filtering a Collection, you might do something like this during debugging:

    1. $collection = collect(['John Doe', 'Jane Doe'])
    2.     ->filter($some_filtering_func);
    3.     // check what is in the collection
    4. dump($collection);
    5. $collection->map($some_mapping_func);

    But there is a method on the Collection class called dump that can make it a bit cleaner:

    1.     $collection = collect(['John Doe', 'Jane Doe'])
    2.         ->filter($some_filtering_func)
    3.         ->dump($collection) // <<
    4.         ->map($some_mapping_func);

    Obviously, don't use this in production.

    Use Form::open with a route param, even if there are parameters for that route (Form::open(['route' => ['route.name.here', 'param-here']))

    This is not part of the core Laravel code, but from the Laravel Collective Form package. I use this package on almost all (non API) Laravel projects, it is very common so I'm including it here

    This isn't going to save much time or make anything easier to read, but it always annoyed me a little that you could do Form::open() with a parameter named 'route', but it only worked when a route had no required route parameters.

    However, you can call it! Just put it in an array (as the title of this section shows)

    1. {{ Form::open( [ 'route' => ['route.name', $post->id] ] }}
    2.     {{-- same as: --}}
    3. {{ Form::open( [ 'url' => route('route.name', $post->id) ] }}
    Other Laravel Collective's Form Snippets

    While I'm on the topic of this useful package, some other small snippets that might be interesting:

    1. // see the action() function I mentioned above:
    2. {{ Form::open(['action' => '[email protected]']) }}
    3.  
    4. // Get the form CSRF token (automatically added when you use Form::open()
    5. {{ Form::token() }}
    6.  
    7. // Form model binding - this is the same as calling
    8. // Form::setModel($user) then calling Form::open([..])
    9. {{ Form::model($user, ['route' => ['user.update', $user->id]]) }}

    And one really useful feature relating to Form::model() are form model accessors.

    If you use the Form::model opener, you might wish to define how some attributes are used. You can set what it will use by using the FormAccessible trait, and setting an accessor method called formNameAttribute($value) method (of course, change the method name to something that matches your attributes).

    1.     // your model
    2.  
    3. class BlogPost extends Model {
    4.     use Collective\Html\Eloquent\FormAccessible;
    5.  
    6.     // this will only be called for items within the Form::model()
    7.     public function formNameAttribute($value)
    8.     {
    9.         return strtoupper($value);
    10.     }
    11. }

    I don't want to write too much about something that isn't part of the core Laravel code, so for a full example and a better explanation please visit the official docs.

    Laravel's Dump Server

    This isn't really a hidden or secret feature of Laravel - it is very well publicised. However, it is quite new so there might be some developers who haven't used it.

    Run php artisan dump-server.

    Then every time your code encounters a dump(...) or dd(...) call, it won't be displayed in the request but will appear in your terminal (where the php artisan dump-server command is running).

    This can be a huge time saver when debugging with APIs and webhooks.

    Just don't forget that it is running and get confused why your dd() calls are showing a blank page!

    Custom if statements in Blade

    In your AppServiceProvider's boot method you can add the following:

    1. Blade::if('env', function ($environment) {
    2.     return app()->environment($environment);
    3. });

    Then inside your blade files you can just use @env('production') ...@elseenv('testing') ...@endenv

    Change 'env' to whatever you wish!

    Use the prepareForValidation() in your Form Requests

    If you wish to modify data from a Form Request object before validation, you can use the prepareForValidation() method.

    The prepareForValidation() method is called before validation happens, and if your request calls a method such as $this->merge(['some_key'=>'a new value']) then when you later call something like $request->all(), it will contain an item with key 'some_key' and value 'a new value'.

    1. public function prepareForValidation()
    2. {
    3.     $this->merge(
    4.         [
    5.             'name' => $this->get('first_name') . ' ' . $this->get('last_name'),
    6.             'email' => strtolower($this->get('email')),
    7.             'random_position' => rand(0,100),
    8.         ]
    9.     );
    10. }

    Source: here

    Some other useful functions in Requests

    Here is a quick overview of some methods that you may not be aware of on the main Request class. Some are quite common, most are obvious what they do just from the method name. There are many that I've not included, so I recommend you check out the Request class yourself.

    (I will refer to it as Request::something(), but you will probably very often use it as $request->something() if you type hint a Request parameter in your controllers. If you use it exactly as it is below, remember to add Use Request (or prepend the calls below with a slash))

    Request::create()

    Request::create(...) is useful when you need to create a request (I use it in testing sometimes). Here is how to set it up:

    1. $created = Request::create(
    2.         '/forum/thread/123', // url
    3.         'POST', //method
    4.         ['reply_body' => 'Some reply to a thread'], // attributes
    5.         [ /* cookies */ ],
    6.         [ /* files */ ],
    7.         [ /* server */ ],
    8.         null /* content */
    9.     );

    To see more about this check out the source here)

    Request::method()

    Request::method() (alias for Request::getMethod() will return an uppercase string of the method ('GET', 'POST', 'PUT', etc). Because of limitations in browsers when it comes to HTTP requests (only being able to do GET/POST), Laravel 'fakes' the DELETE, PATCH (etc) requests. If you want to find the 'real' request (either GET or POST) then you can use getRealMethod().

    Request::fullUrl()

    Request::fullUrl() returns the full url ('http://yoursite/segment1/seg2')

    Request::segment()

    Request::segments() returns an array of all the segments for the request path. You can also use Request::segment(3,'some-optional-default') to get the third segment (or provide a default if it does not exist)

    Request::path()

    Request::path() returns the path (so for this request: blog/laravel-features-you-may-not-know-about)

    Request::ajax()

    Request::ajax() and Request::isXmlHttpRequest(): Both of these return true if the request is a XMLHttpRequest. It determines this by checking for the X-Requested-With header, which is set by almost all JS libraries that make new XmlHttpRequests. Also related: Request::pjax() which checks for the X-PJAX header. I have never heard of this - I think it relates to this.

    Request::hasPreviousSession()

    Request::hasPreviousSession() - returns a boolean value that indicates if the request contains a Session which was started in one of the previous requests.

    Request::getClientIp()

    Request::getClientIp() returns the first IP from Request::getClientIps(), which itself returns an array of IP addresses. The first IP from getClientIps() is the 'most trusted' one. Unless a request is coming via a proxy of some kind, you will probably only have one IP in getclientIps()

    Request::getUserInfo()

    Request::getUserInfo() has nothing to do with Laravel's authenticated user. This is part of the Symfony Request class, and will return what is effectively $_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW'], which gets it's values from Basic HTTP Authentication. See here.

    Ok, that's it!

    I'm almost certain that most Laravel developers will be aware of a bunch of the above tips (maybe all of them). Almost all of the above tips were directly in the official docs. But maybe some people will find a new feature of Laravel from the list above.

    Whenever I start a new language, framework or library I always try and skim through the documentation, use it, then give it a full read. I'm sure I quickly forget most of what I read, but there are always lots of features in big libraries/frameworks which are underused.

    Please let me know in the comments if there are any useful features of Laravel that you think are underused, that I've not mentioned. I'm sure there are tons!

    Subscribe to my spam free newsletter for other Laravel updates like this

    I never spam, and only email when I have a good in-depth post published on my site (mostly about Laravel). You can also follow me on social media to get updates.

    webdevetc profile pic
    webdevetc

    I'm a late 20's web developer, specialising in Laravel, but work with other PHP frameworks and associated technologies (such as JS). I mostly write about PHP here. Contact me here. Need to hire or work with a contract PHP software developer in London, UK (or freelance)? Contact me and check my availability.

    Leave a Comment