Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Laravel Cheatsheet

Related Posts

Table of Contents

  1. Getting Started
    1. Create New Project
    2. Running Locally
  2. Artisan CLI Tool
    1. Create Controller
    2. Create RESTful Controller
    3. Create Middleware
    4. Create Exception
    5. Create App Key
    6. Create Command
    7. List
    8. Down (Maintenance Mode)
  3. Routing
    1. Path Variables
    2. Patterns
    3. Catch All
  4. Middleware
    1. Apply Middleware To Specific Route
    2. Apply Middleware To Group Of Routes
    3. Apply MIddleware and Prefix To Group Of Routes
    4. Apply Middleware To All Routes In Controller Except Specific Ones
    5. Apply Middleware To Every Route
    6. Apply Middleware To All Routes Except Those In Exception List
  5. Views
  6. Database Migrations
    1. About
    2. Example
    3. Create Migration
    4. Using Raw SQL
    5. Run Migrations
  7. Models
    1. Don't Use Within Migrations
    2. Fetch All
  8. Validation
    1. Example
    2. Required But Nullable
    3. Further Resources
    4. Link To All Validation Rules
    5. Regular Expressions
    6. Error Bags
  9. Form Handling
    1. Redirect Back With Errors
    2. HTML To Show Errors
  10. Pagination
    1. Creating Paginated Object
  11. API
    1. Returning JSON
    2. Returning Models
    3. Disable Throttling
    4. Returning Null Attributes
    5. Make Hidden Data Visible
    6. Custom Exception And Missing Route Handling
  12. Sessions
    1. Set
    2. Get
    3. Delete
  13. String Functions
    1. Generate Random String
    2. Generate UUID
  14. Making Requests - HTTP Client
    1. Basic GET Request
    2. POST Request - JSON Body
    3. POST Request - Form Fields
    4. Multi-Part Request (File Uploads)
    5. Request Headers
    6. Response Object
    7. Laravel Docs
  15. Commands
    1. Create A Command
    2. Boilerplate Command File
    3. Signature
    4. Description
    5. Handle
    6. Running A Command
  16. Debugging / Gotchas
    1. Visibility
    2. CRSF - 419 Expired
  17. Testing
  18. Misc
    1. Disabling CSRF
  19. References

Getting Started  

Create New Project  

mkdir my-project-name
cd my-project-name
composer create-project laravel/laravel . --prefer-dist

To make sure you have the necessary packages on Ubuntu 16.04, you may need to run:

sudo apt update && \
sudo apt install -y php7.0-mbstring php7.0-xml

Running Locally  

You can test locally by running:

sudo php -S localhost:80 public/index.php

Then just go to localhost in your browser.

Artisan CLI Tool  

Create Controller  

php artisan make:controller MyController

Create a RESTful Resource Controller  

php artisan make:controller MyController --resource

Create Middleware  

php artisan make:middleware MyMiddlewareClass

Create Exception  

php artisan make:exception NameOfMyExceptionClass

Create App Key  

head /dev/urandom | head -c 32 | base64 | awk '$0="base64:"$0'

... or if you have openssl installed:

openssl rand -base64 32 | awk '$0="base64:"$0'

... or if you have want to use Laravel's artisan tool:

php artisan key:generate --show

This will output something like:

base64:9J5F/51QKxxhAxbHvmvJtLIoop6ywOywiev9hWF3wLY=

You need to include the whole thing as the value. E.g. including the base64: part.

Create Command  

php artisan make:command NameOfMyCommand

The file for the command will be generated within app/Console/Commands

List  

Use the list command to list all the available commands that you can use with artisan.

php artisan list

This will output all of the default commands that come with laravel, as well as any custom commands you may have added.

Down (Maintenance Mode)

The down command will put the site in maintenance mode. This can be useful when you are about to move the site etc.

php artisan down

In theory, one should be able to specify a custom message with:

php artisan down --message='The site is down for maintenance.'

However, I found that this resulted in the error message:

The "--message" option does not exist. 

This may be because I was testing on an old version of Laravel, or perhaps one has to perform some code changes to make this work.

Routing  

There are several routing files under the top level routes directory:

  • api.php - routes for an API.
  • channels.php - for websockets and broadcasting.
  • console.php - routes for if you are building a CLI application.
  • web.php - routes for a traditional web application that isn't an API.

There are two main ways to define a route. The first is by actually passing a callback that gets executed.

Route::get('/', function () {
    return view('welcome');
});

The alternative "shorthand" is to pass a string that represents the controller and method to execute.

Route::get('/my/web/path', 'MyController@method');

Official routing docs.

Path Variables  

If you have routes with variables in the path, then you can use something like the following to pick them up.


Route::get('users/{name}', function ($name) { $userController = new App\Http\Controllers\UserController(); return $userController->getUserByNAME($name); });

Patterns  

If you need to use a regular expression on the path variables, such as to match against a UUID you can do that like so:

Route::pattern('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');

Route::get('users/{uuid}', function ($uuid) {
    $userController = new App\Http\Controllers\UserController();
    return $userController->getUser($uuid);
});

Alternatively, you can use this alternative syntax to achieve the same result:

Route::get('users/{uuid}', function ($uuid) {
    $userController = new App\Http\Controllers\UserController();
    return $userController->getUser($uuid);
})->where('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');

Catch All  

You can take the regular expression logic further, creating a "catch all" for catching all routes after a certain prefix. E.g.

# All defined routes first.
Route::get('users/{id}', function ($id) {
    $userController = new App\Http\Controllers\UserController();
    return $userController->fetchUser($id);
});

# Put "catch-all" in last to catch any route that didnt match above but still starts with /users.
Route::pattern('path', '.*');

Route::get('users/{path}', function ($path) {
    $userController = new App\Http\Controllers\UserController();
    return $userController->catchAll($path);
});

Middleware  

Apply Middleware To Specific Route  

Route::get('', function () {
    $controller = new \App\Http\Controllers\AccountController();
    return $controller->getAccounts();
})->middleware(\App\Http\Middleware\MiddlewareApiAuth::class);

... or if you want to run multiple middlware:

Route::get('', function () {
    $controller = new \App\Http\Controllers\AccountController();
    return $controller->getAccounts();
})->middleware([\App\Http\Middleware\MiddlewareClass1::class, \App\Http\Middleware\MiddlewareClass2::class]);

Apply Middleware To Group Of Routes  

Route::middleware([\App\Http\Middleware\MiddlewareClass1::class, \App\Http\Middleware\MiddlewareClass2::class])->group(function () {
    Route::get('/', function () {
        //...
    });

    Route::get('user/profile', function () {
        // ...
    });
});

Apply MIddleware and Prefix To Group Of Routes  

Route::group(['prefix' => 'admin', 'middleware' => \App\Http\Middleware\MiddlewareLoggedInAsAdminAuth::class], function(){

    // admin view for viewing posts in the system
    Route::get('articles', function () {
        $controller = new \App\Http\Controllers\ArticleController();
        return $controller->showCreateArticlePage();
    });

    // admin view for editing/viewing a particular post
    Route::get('articles/{uuid}', function ($uuid) {
        $controller = new \App\Http\Controllers\ArticleController();
        return $controller->editArticle($uuid);
    });

});

Apply Middleware To All Routes In Controller Except Specific Ones  

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
    public function __construct(){
        $this->middleware('auth',['except' => ['login','setup','setupSomethingElse']]);
    }
}

Apply Middleware To Every Route  

Open your app/Http/Kernel.php file and add your middleware to the $middleware property of the class like so:

protected $middleware = [
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\TrustProxies::class,
    Middleware\MyMiddlewareClass::class,
];

If you apply middleware this way, you cannot rely on getting the route name for an "exceptions list", as the route will not have been evaluated yet. E.g. $request->route() will return null from within the middleware. See here for more info.

Apply Middleware To All Routes Except Those In Exception List  

Unfortunately, there is not a built-in "Laravel way" to do this right now. I found that the solution for me was to apply the middleware to every route as shown above, and then add logic to the middleware class'es body like so:

public function handle($request, Closure $next)
{
    $authPassed = false;
    $path = $_SERVER['REQUEST_URI'];

    foreach (PATHS_WHERE_HEADERS_NOT_POSSIBLE as $pattern)
    {
        $match = preg_match($pattern, $path);

        if ($match === 1)
        {
            $authPassed = true;
        }
    }

    // ... other checks here if authPassed is false

    if ($authPassed === true)
    {
        $response = $next($request);
    }

    return $response;
}

I defined my exception paths like so:

$uuidRegexp = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';

define('PATHS_WHERE_HEADERS_NOT_POSSIBLE', [
    '|^' . $uuidRegexp . '/resource/my-action$|'
]);

I am using | character to represent the start and end of the regular expression because I needed to use / in the path. However, if you need | for a regexp OR, then you will need to use something else.

Database Migrations  

About  

  • Database migrations can be found under database/migrations.
  • By default, you will have two migrations in there that create the users and password_resets tables.
  • Migration filenames are prefixed with the year, month, day, and time in order for the migration tool to know which order to run the migrations.
    • Using the date/time rather than an incrementing integer should hopefully reduce the likelihood of an issue arising when merging two different branches that each created a migration script.

Example  

The following example will create a table that links sections to user-groups and will automatically delete rows in the table when either user-groups or content items are deleted.

public function up()
{
    Schema::create('content_user_group', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('content_id')->unsigned()->nullable(false);
        $table->integer('user_group_id')->unsigned()->nullable(false);
        $table->foreign('content_id')->references('id')->on('content')->onDelete('cascade');
        $table->foreign('user_group_id')->references('id')->on('user_group')->onDelete('cascade');
        $table->timestamps();
    });
}

Below is an example that makes use of UUIDs as the identifiers, and also has a composite unique key (e.g. a user can only be mapped to an organization once).

Schema::create('organisation_users', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->uuid('user_id')->nullable(false);;
    $table->uuid('organization_id')->nullable(false);;
    $table->foreign('user_id')->references('id')->on('users');
    $table->foreign('organization_id')->references('id')->on('organizations');
    $table->unique(['user_id', 'organization_id']);
    $table->timestamps();
});

Create Migration  

The following command will create a migration script in your migrations table for creating a table called x.

php artisan make:migration create_x_table

If you don't use create_tableName_table naming convention, then you won't get the starting code within the up/down methods for creating/destroying the table.

Using Raw SQL  

If you need use raw SQL, you can just use DB::statement() like below:

public function up()
{
    $createTableQuery =
        "CREATE TABLE `broker` (
            `id` int unsigned NOT NULL AUTO_INCREMENT,
            `name` varchar(255) NOT NULL,
            `redirect_url` text NOT NULL,
            `secret` varchar(30) NOT NULL,
            `modified_timestamp` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8";

    DB::statement($createTableQuery);
}

If you need to get data out of the database though, you will need to use something like below:

$query =
    "SELECT * FROM score_history WHERE user_id IN (" .
        "SELECT uuid FROM users WHERE account_id= :accountUuid" .
    ")";

$result = \DB::select($query, ['accountUuid' => $accountUuid]);

The trouble with using \DB::select() is that you will be returned an array of stdClass objects instead of objects of your model. In order to get an array of your model objects, you would need to use the hydrate method like:

$query = "SELECT * FROM scores WHERE someColumnName = :someValue";
return Score::hydrate(\DB::select($query, ['someValue' => $someValue]))->all();

Run Migrations  

You can execute migrations with:

php artisan migrate

If you're just getting started, don't forget to edit the .env file with your database connection details.

Models  

Create Model Objects  

Once you have created your model class, you can instantiate objects with:

$newModelObject = new \App\Models\MyModel([
    'name' => 'Programster',
    'age' => '93',
]);

$newModelObject->save();

Alternatively, you can technically do this as well:

$newModelObject = new \App\Models\MyModel();

// set the attributes
$newModelObject->name = "Programster";
$newModelObject->age = 93;

// save to the database.
$newModelObject->save();

Don't Use Within Migrations  

When one starts using Laravel, it can be a bit confusing about whether to create/insert data in the database through the use of creating a model, or through a database table operation. E.g. below is what I call a "model operation" that will add an entry to the database through the creation of a Flight model:

$flight = Flight::create([
    'name' => 'London to Paris',
]);

$flight->save()

And below are examples of "database operations" that will likely achieve the same thing:

# Raw SQL
DB::insert('INSERT into flight (name) values (?)', ['London to Paris']);

# Query Builder
DB::table('flight')->insert(['name' => 'London to Paris']);

There are lots of subtle differences between the two, but the biggest reason why you should avoid using model operations within your database migrations is that your models can evolve over time, but your migrations need to keep working consistently no without updates. The easiest way to realize this is to think about what would happen if you decided to delete one of your models?

Mass Creating  

MyModel::create(
    ['name' => 'Timmy', 'age' => 40],
    ['name' => 'Jimmy', 'age' => 50],
    ['name' => 'Kimmy', 'age' => 60],
);

Mass Inserting  

MyModel::insert(
    ['name' => 'Timmy', 'age' => 40],
    ['name' => 'Jimmy', 'age' => 50],
    ['name' => 'Kimmy', 'age' => 60],
);

Insert vs Create  

"Create" will create new model objects and save them to the database whereas "Insert" is just responsible for building and sending a query (Eloquent Query Builder) to the database to insert some rows. Thus, when using "create", one will need to set the $fillable attribute on the attributes you wish to mass create objects with before one can use them, which you won't need to do if using "insert". This also means that any logic that derives fields as part of the model logic will not get executed if using "insert" like it would with "create". The most noticeable result of this is that if one uses "insert", the timestamp fields will be set to null, instead of having values.

Thus, the best rule of thumb in my opinion, is to use the "create" method instead of "insert", wherever possible.

More info on Stack Overflow

Mass Inserting Existing Model Objects  

If you have created a bunch of model objects that have not yet been inserted into the database, you can do them in one go like so:

$insertRows = array();

foreach ($models as $myModel)
{
    $insertRows[] = $myModel->attributesToArray();
}

\App\MyModel::insert($insertRows);

Re-Inserting Models  

Laravel models have an exists attribute. If you have a model in your variables, you can re-save it to your database after deletion if you change it's exists value to false.

For example:

$id='4b25d6bc-3225-4c19-8b63-4b2e981d550c';

$account = Account::findOrFail($id);
$accountDupe = Account::findOrFail($id);
$account->delete();

# A second model that is a copy of the one that just got deleted.
$accountDupe->exists = false;
$accountDupe->save();

It's beyond the scope of this tutorial to explain when this is useful, but it is worth remembering that often models are composed of other models and that sometimes you need to cast some models to other models.

Fetch All  

Use the following if you wish to fetch all of a certain model in the database.

$models = MyModel::all()

Views  

Validation  

Example  

Here is a snippet example of something that would go in a controller. It will verify that:

  • if an id is supplied, it is a UUID, but it doesn't have to be provided.
  • a name is required and must be a string no longer than 255 characters
  • a gender string is required and must be male, female, or other (enum in the database).
$validationRules = [
    'id' => 'uuid',
    'name' => 'required|max:255',
    'gender' => 'required|in:male,female,other',
];

$data = request()->all();
$validator = \Validator::make($data, $validationRules);

if ($validator->fails())
{
    $messageBag = $validator->getMessageBag();
    // do something with it here.
}

Validation support for uuid was apparently added in Laravel 5.7.

Required But Nullable  

If you require a field to be sent across, but null is a valid value for it, Laravel can get a bit "confused" and won't work as you might expect if you were to use: required|nullable, instead, just use present. E.g.

$validationRules = [
    'id' => 'uuid',
    'name' => 'required|max:255',
    'date_of_birth' => 'required',
    'date_of_death' => 'present', // Required, but should be null if not dead yet.
];

Github thread on this issue.

Link To All Validation Rules  

Here is a link to all of the available validation rules.

Regular Expressions  

Here is an example rule that uses a regular expression to validate that the id field is provided, and consists of 9 numbers (possibly staring with 0).

$validationRules = [
    'id' => ['required','regex:/^[0-9]{9}$/'],
];

Error Bags  

When working with Blade templates you may come across using $errors like so:

@if ($errors->has('email'))
    <span class="help-block">
        <strong>{{ $errors->first('email') }}</strong>
    </span>
@endif

The $errors will be an instance of the \Illuminate\Support\MessageBag or the \Illuminate\Support\ViewErrorBag classes.

Form Handling  

Redirect Back With Errors  

If you had a form that was posting somewhere else, but there were validation failures etc, then you can just redirect back with the inputs that were provided and the error messages like so:

catch (\App\Exceptions\SomeCustomException $e)
{
    $response = \redirect()->back()->withInput()->withErrors(["Something custom I checked for went wrong"]);
}

If Laravel validation failed, then you can use the validator's message bag:

catch (\App\Exceptions\ExceptionLaravelValidationFailed $validationFailedException)
{
    $response = \redirect()->back()->withInput()->withErrors($validationFailedException->getValidator()->getMessageBag());
}

HTML To Show Errors  

If you add snippets like this below your form input fields:

@if ($errors->has('form_input_name'))
    <span class="help-block">
        <strong>{{ $errors->first('form_input_name') }}</strong>
    </span>
@endif

... then your errors from laravel validation failing will show up by the input fields they relate to.

Pagination  

If you are working with a paginated resource, you may find it easier if you typehint it like so:

/* @var $paginatedAssets \Illuminate\Pagination\AbstractPaginator */

Creating Paginated Object  

Sometimes you may have an array of objects that you need to paginate. For this, you may find the LengthAwarePaginator useful.

$paginatedAssets = new \Illuminate\Pagination\LengthAwarePaginator(
    $assets, // array of asset objects
    count($assets),
    10,
    null,
    ['path' => '/assets']
);

Sessions  

Set  

$request->session()->put('key', 'value');

Get  

$value = $request->session()->get('key');

Delete  

$request->session()->forget('key');

API  

Returning JSON  

If you wish to return a custom JSON response, then you can do like so:

return response()->json([
    'name' => 'Abigail',
    'state' => 'CA',
]);

Returning Models And Collections  

A lot of the time when building an API, you just want to return a model, or a Laravel collection of models. E.g a GET request to /users should just fetch the users in your system. There are two aspects of Laravel that make this easier:

  • Models and collections are converted to JSON when cast to a string, so you can return Eloquent objects directly from your application's routes or controllers.
  • Laravel will automatically serialize your Eloquent models and collections to JSON when they are returned from routes or controllers:
Route::get('users', function () {
    return User::all();
});

Alternatively:

Route::get('users', function () {
    return (string) User::all();
});

Alternatively, if you wish for a more explicit way, then you could:

Route::get('users', function () {
    $users = User::all();
    return $users->toJson(JSON_PRETTY_PRINT);
});

You don't have to include the JSON_PRETTY_PRINT.

Reference documenatation.

Disable Throttling  

If you are building an API that is going to hit more than 60 requests per second, you will want to remove or increase the default rate-limiting of requests. You can do this by finding the following text in the /app/Http/Kernel.php file and commenting out or increasting the throttle line.

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

Custom Exception And Missing Route Handling  

If you're building an API and you want unhandled exceptions/errors to be returned in JSON, or want to do something custom for when routes are not found (404 pages), then edit the app/Exceptions/Handler.php file according to these Stack Overflow posts:

Returning Null Attributes  

If you are building an API and you have a model whose attributes may have a null value, then those attributes will not be returned in requests by default. If you would prefer to get the attribute back in your JSON, but with a value of null, then you need to override the toArray() method like so:

public function toArray() : array
{
    return array(
        'uuid' => $this->uuid,
        'name' => $this->name,
        'email' => $this->email,
        'status' => $this->status,
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
        'deleted_at' => $this->deleted_at,
    );
}

Make Hidden Data Visible  

Sometimes you need to return data in a JSON response that is usually hidden (and thus in the $hidden member variable of a model). This might include a user's email or password etc. To make this data get returned, make use of the makeVisible() method. Refer here.

String Functions  

Generate Random String  

$randomString = \Illuminate\Support\Str::random($length=16)

Generate UUID  

\Illuminate\Support\Str::orderedUuid()->toString();

Commands  

Create A Command

To create a new command, run the artisan command to create a command. Once you do this, Laravel will create the boilerplate command file with the same name within app/Console/Commands.

Boilerplate Command File  

After using the artisan command to create a command, Laravel will create the boilerplate command file like so:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class MyTestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return 0;
    }
}

Signature  

Change the $signature variable to match what the command should be when you run the command from artisan. E.g. if you were set it to:

protected $signature = 'cognito:sync';

... then you would be able to run this command by running:

php artisan cognito:sync

You can add arguments/options to the command through the signature as well. For example, the following would add an optional --send-emails option, that if added is set to true, and if left out is set to false.

protected $signature = 'cognito:sync 
    {--send-emails : Tell the command to send emails for new users.}';

The description for the option will show in the output of php artisan help my:command.

To retrieve the user's options, use $this->option() within the handle() function like below:

$sendEmails = $this->option('send-emails');

Description  

Change the value of the $description variable in order to provide a description for the command. This will be shown to the user when they list the commands.

protected $description = 'Syncs the service with Amazon Cognito.';

Handle  

The handle() function is the entry-point for execution when the command is called. Fill this in with what you want to happen. By default it returns 0, which means the command was successful. If you return a string, or an integer other than 0, then this will be interpreted as the command erroring out in some way.

Running A Command  

In order to run a command, just call from artisan with the signature.

php artisan my:command

Debugging / Gotchas  

Visibility  

I had a table that had a column for visible that was true/false for whether the item should be visible or not. Unfortunately visibility is also something built into Laravel, to do with whether an attribute should be returned in a JSON response. This causes random issues, especially when I created the getVisible() accessor, which was overriding Laravel's getVisible() method. The solution here was to change to is_visible and change the accessor to getIsVisible().

CRSF - 419 Expired  

If you get a 419 "Page Expired" message after posting a form, make sure you have a hidden input field with the CRSF token like so:

 <input type="hidden" name="_token" id="csrf-token" value="<?= csrf_token(); ?>">

Making Requests - HTTP Client  

Laravel has an in-built HTTP client that is a wrapper to help you out whenever you need your backend to make web/api requests to other services. Without Laravel, one would traditionally use a service like Guzzle (or my Guzzle Wrapper))

The laravel HTTP client is actually a wrapper around Guzzle as well. Although the Guzzle code is PSR-18 compliant, I am not sure if the wrapper is.

Basic GET Request  

$queryParameters = [
    'param1' => 'someValue',
    'param2' => 'someValue',
];

$response = Http::get('http://example.com/path/to/resource', $queryParameters);

POST Request - JSON Body  

The default request type is application/json, so when sending a request to a JSON API, you don't need to do anything extra/special.

$postData = [
    'param1' => 'someValue',
    'param2' => 'someValue',
];

$response = Http::post('http://example.com/path/to/resource', $queryParameters);

POST Request - Form Fields  

Since the default request type is application/json we have to tell the HTTP client that we want it to be sending form fields for an application/x-www-form-urlencoded request type.

$postData = [
    'param1' => 'someValue',
    'param2' => 'someValue',
];

$response = Http::asForm()->post('http://example.com/users', $queryParameters);

Multi-Part Request (File Uploads)  

When uploading files, one needs to make a multi-part request. One can do this like so:

$photoFileHandle = fopen('photo.jpg', 'r');
$attachmentName = 'photo.jpg';

$response = Http::attach('attachment', $photoFileHandle, $attachmentName)
    ->post('http://someDomain.com/upload');

Request Headers  

If you need to add headers to the request, one needs to use Http::withHeaders($headers). The example below shows how to perform a GET request, where we provide the bearer auth token in the header:

$headers = [
    'Authorization' => "Bearer {$accessToken}",
];

$url = 'https://mydomain.com/oauth2/userInfo';
$queryParameter = [];
return \Http::withHeaders($headers)->get($url, $queryParameters);

Response Object  

All of the requests shown return a Illuminate\Http\Client\Response object, which has the following useful methods for retrieving the response body:

// gets the response body as string.
$response->body() : string;

// Get the JSON decoded body of the response as an array or scalar value.
$response->json($key = null, $default = null) : array|mixed;

// Get the JSON decoded body of the response as an stdClass object.
$response->object() : object;

// Get the JSON decoded body of the response as a collection.
$response->collect($key = null) : Illuminate\Support\Collection;

Also, there are these helper methods for quickly checking the response code.

// get the http status code of the response.
$response->status() : int;

// returns true if the HTTP status code was 200 (be careful as there are
// other 2XX codes).
$response->ok() : bool;

// returns true if the http code is in the 2XX range.
$response->successful() : bool;

// returns true if the http status code is in the 3XX range
$response->redirect(): bool;

// returns true if http status code in 4XX range
$response->clientError() : bool;

// returns true if http status code is >=400 (e.g. this also includes
// the 5XX range etc).
$response->failed() : bool;

// returns true if http status code >= 500
$response->serverError() : bool;

Finally, if you need any of the response headers...

// get a specific header from the response
$response->header($header) : string;

// get the headers of the response
$response->headers() : array;

Laravel Docs  

Here is a link to the Laravel docs HTTP client

Testing  

Run testing with this command from inside your application where the vendor directory is under.

vendor/bin/phpunit

Filenames  

Remember that all test filenames need to end with Test. E.g. MyTest.php, and not TestUsers.php.

Misc  

Disabling CSRF  

If you need to disable CSRF on certain routes, edit the file that should already exist at:

App\Http\Middleware\VerifyCsrfToken

Then fill in the routes in the $except array in the block that should look like this:

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        //
    ];
}

References  

Last updated: 2nd September 2022
First published: 27th August 2018