How to upload files to Google Cloud using Laravel
Tags: laravel, googlecloud, pdf, api
Published: 2021-03-19 23:04:19

---
title: How to upload files to Google Cloud using Laravel
published: true
description: Building an API that generates an API and uploads it to Google Cloud Storage
tags: laravel, googlecloud, pdf, api 
cover_image: https://images.pexels.com/photos/2114014/pexels-photo-2114014.jpeg?cs=srgb&dl=pexels-magda-ehlers-2114014.jpg&fm=jpg
---
The aim of this tutorial is to accomplish these tasks
- [Prequisities](#prequisities)
- [Create a Laravel API that creates and stores PDF locally](#create-a-laravel-api-that-creates-and-stores-pdf-locally)
    - [INITIALIZE PROJECT](#initialize-project)
    - [USE COMPOSER PACKAGE](#use-composer-package)
    - [CREATE SYMBOLIC LINK](#create-symbolic-link)
    - [CREATING THE BLADE TEMPLATE FILE](#creating-the-blade-template-file)
    - [CREATE CONTROLLER](#create-controller)
    - [ROUTING THE REQUEST](#routing-the-request)
    - [TESTING OUR CODE](#testing-our-code)
    - [SUCCESSFUL RESPONSE](#successful-response)
- [Uploading files to Google Cloud Storage](#uploading-files-to-google-cloud-storage)
    - [CREATING THE BUCKET](#creating-the-bucket)
    - [CONFIGURATION FILES AND VARIABLES](#configuration-files-and-variables)
    - [USING THE GOOGLE CLOUD PACKAGE](#using-the-google-cloud-package)
- [Send Email Notification](#send-email-notification)


## Prequisities
- Install [Composer](
Composergetcomposer.org)
- Get a [Google Cloud](https://console.cloud.google.com/) account
- Install [Postman](https://www.postman.com/) or your preferred API testing tool
- [Mailtrap](https://mailtrap.io/) account


## Create a Laravel API that creates and stores PDF locally

#### INITIALIZE PROJECT
First, we will initialize a new Laravel project into a `pdf-generator` directory using composer:
```bash
composer create-project laravel/laravel pdf-generator
```


#### USE COMPOSER PACKAGE
The composer package we will be using is [laravel-dompdf](https://github.com/barryvdh/laravel-dompdf). To use this package in our project we will use this command:
```bash
composer require barryvdh/laravel-dompdf
```
From the `laravel-dompdf` documentation:
Add the ServiceProvider to the providers array in `config/app.php`
```bash
Barryvdh\DomPDF\ServiceProvider::class,
```
You can optionally use the facade for shorter code. Add this to your facades:
```bash
'PDF' => Barryvdh\DomPDF\Facade::class,
```

#### CREATE SYMBOLIC LINK
To store our generated file, we need to create a [symbolic link](https://laravel.com/docs/8.x/filesystem#the-public-disk).

From the Laravel Documentation:
>To make these files accessible from the web, you should create a symbolic link from public/storage to storage/app/public. Utilizing this folder convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io/).

To create the symbolic link, you may use the `storage:link` Artisan command:
```bash
php artisan storage:link
```

Now, we will create a `pdf` directory in the `public/storage` directory. Every directory and file created/stored here will be mirrored in the `storage/app/public` directory.

#### CREATING THE BLADE TEMPLATE FILE
Our next step is to create a [`blade template file`](https://laravel.com/docs/8.x/blade) for our pdf view. This view will display two values: the `$firstname` and `$email` of the user. We will name ours: `pdfdocument.blade.php`

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Laravel PDF demo</title>
</head>
<body>
  <h1>Laravel PDF demo</h1>
  <p>Firstname: {{$firstname}} </p>
  <p>Email: {{$email}} </p>
</body>
</html>
```
>Blade template files are created in the `resources/views` directory

#### CREATE CONTROLLER
At this stage we have
<ol>
<li>Initialized a laravel project</li>
<li>Pulled in the laravel-dompdf package to generate pdf</li>
<li>Created a symbolic link and folder to store the generated pdf</li>
<li>Created a blade template file which will be used for the pdf</li>
</ol>

Now, let us create the controller to bring it all together.
We will create a Controller in an `Api` folder with this artisan command:
```bash
php artisan make:controller Api/PdfGeneratorController 
```


```php
<?php 

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\App;

class PdfGeneratorController extends Controller
{

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function store(Request $request) : JsonResponse
    {
        $validatedData = $request->validate([
            'firstname' => ['required', 'min:3', 'max:50'],
            'email' => ['required', 'email'],
        ]);

        $pdf = App::make('dompdf.wrapper');
        $pdf->loadView('pdfdocument', $validatedData);
        $filename = $request['firstname'] . '.pdf';
        $fileStoragePath = '/storage/pdf/' . $filename;
        $publicPath = public_path($fileStoragePath);

        $pdf->save($publicPath);

        return response()->json([
            "status" => "success",
            "message" => "PDF saved successfully ",
            "data" => [
                "url" => url($fileStoragePath)
            ]
        ]);


    }

}

```

>You can use the [laravel-jsend](https://github.com/shalvah/laravel-jsend) package to generate [JSend-compliant](https://labs.omniti.com/labs/jsend) responses for your Laravel app

#### ROUTING THE REQUEST
Next, we will create a route in our `api.php` file that will point to the `store()` method in the `PdfGeneratorController`. 

>The `api.php` file is located in the `pdf-generator/routes` directory

The line below ensures that only post requests are made to the `store` method with a [slug](https://devdojo.com/devdojo/what-is-a-slug) of `generate-pdf`
```php
Route::post('/Api/generate-pdf', [PdfGeneratorController::class, 'store']);
```

#### TESTING OUR CODE
For testing, we will use Postman or any API testing tools of your choice. We will make a post request to the store method
```bash
[POST] http://127.0.0.1:8000/api/Api/generate-pdf
```
Set the `Headers` to 
```bash
KEY     VALUE
Accept  application/json
```
And the body of the request to `JSON`
```json
{
    "firstname" : "YourFirstName",
    "email" : "yourEmailAddress@domain.com"
}
```

#### SUCCESSFUL RESPONSE
A successful response returns this `JSON`
```json
{
    "status": "success",
    "message": "PDF saved successfully ",
    "data": {
        "url": "url-of-the-generated-pdf-file"
    }
}
```

## Uploading files to Google Cloud Storage
#### CREATING THE BUCKET
>[Buckets](https://cloud.google.com/storage/docs/key-terms#buckets) are the basic containers that hold your data. Everything that you store in Cloud Storage must be contained in a bucket. You can use buckets to organize your data and control access to your data, but unlike directories and folders, you cannot nest buckets.

To create a bucket on Google Cloud Storage, you can follow this tutorial and skip to `02:13 to 03:44` to first create a Service Account and then skip to `05:21 to 05:31` to create Cloud Storage.

<!-- dev.to format-->

 {% youtube hJdN2TUQr_4 %} 


<!-- hashnode format -->
<!-- %[https://www.youtube.com/watch?v=hJdN2TUQr_4] -->

Optionally, you can also check out this slide on [How to create a bucket on Google Cloud](https://docs.google.com/presentation/d/e/2PACX-1vRgaRIMIuYpCSqQOFOh3WDmb1sNUpipwpKsdHQJWu38EpHV8Lz5Cv1jZBH6t-dDBsMKb1Be02FE82do/pub?start=false&loop=false&delayms=3000) that contains a guide similar to the YouTube video above

<!-- does not work on dev.to -->
<!-- <iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRgaRIMIuYpCSqQOFOh3WDmb1sNUpipwpKsdHQJWu38EpHV8Lz5Cv1jZBH6t-dDBsMKb1Be02FE82do/embed?start=false&loop=false&delayms=3000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> -->

#### CONFIGURATION FILES AND VARIABLES
Rename the json key generated to `googlecloud.json` and move/copy it into the `config` directory like so: `config/googlecloud.json`.

Next, in the `.env` file, create add these [environment variables](https://laravel.com/docs/8.x/configuration#environment-configuration):

```bash
# GOOGLE CLOUD 
GOOGLE_CLOUD_PROJECT_ID=laravel-tutorial
GOOGLE_CLOUD_STORAGE_BUCKET=laravel-pdf-bucket
```

Now, we will create a [configuration file](https://laravel-news.com/creating-configuration-in-laravel) that will access the environment variables in the `.env` file. Create a file in in the `config` directory and name it `googlecloud.php` like so: `config/googlecloud.php` and pass in this code:
```php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Google Cloud  configuration
    |--------------------------------------------------------------------------
    |
    | This file is for storing the credentials for Google Cloud 
    | 
    | 
    | 
    |
    */

    'project_id' => env('GOOGLE_CLOUD_PROJECT_ID'),
    'storage_bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET'),


];

```

Run this command after making the configuration changes:
```bash
composer dump-autoload 
php artisan config:clear
php artisan config:cache
```

#### USING THE GOOGLE CLOUD PACKAGE
Our next step is to use the [Google Cloud Storage Client for PHP](https://packagist.org/packages/google/cloud-storage).

We pull in the package with this composer command
```bash
composer require google/cloud-storage
```

Next, we will add the class 
```php
use Google\Cloud\Storage\StorageClient;
```

And now we will upload our generated file to Google Cloud and return the google cloud storage url in the `response()->json()`
```php
$googleConfigFile = file_get_contents(config_path('googlecloud.json'));
$storage = new StorageClient([
    'keyFile' => json_decode($googleConfigFile, true)
]);
$storageBucketName = config('googlecloud.storage_bucket');
$bucket = $storage->bucket($storageBucketName);
$fileSource = fopen($publicPath, 'r');
$newFolderName = $request['firstname'].'_'.date("Y-m-d").'_'.date("H:i:s");
$googleCloudStoragePath = $newFolderName.'/'.$filename;
/* Upload a file to the bucket.
  Using Predefined ACLs to manage object permissions, you may
  upload a file and give read access to anyone with the URL.*/
$bucket->upload($fileSource, [
  'predefinedAcl' => 'publicRead',
  'name' => $googleCloudStoragePath
]);

return response()->json([
  "status" => "success",
  "message" => "PDF saved successfully ",
  "data" => [
      "url" => url($fileStoragePath),
      "google_storage_url" => 'https://storage.cloud.google.com/'.$storageBucketName.'/'.$googleCloudStoragePath
  ]
]);
```

If you see this error: `ext-json is missing in composer.json`, this solves it:

```json
"require": {
  "ext-json": "*"
},
```


## Send Email Notification
After signing up on mailtrap.io, you can get your configuration settings from your inbox by clicking the settings icon:

![image](https://user-images.githubusercontent.com/22425217/111836668-f5141b00-88f6-11eb-8329-d68e5e6d10c1.png)

And selecting your desired language from the dropdown:

![image](https://user-images.githubusercontent.com/22425217/111836866-4b815980-88f7-11eb-8a34-481b1f2c6eb8.png)

Your smtp settings will be displayed below the dropdown. Copy the settings and add them to your `.env` file.

Run this command after making the configuration changes:
```bash
composer dump-autoload 
php artisan config:clear
php artisan config:cache
```

Now we will create a nofication using Laravel's Artisan command
```bash
php artisan make:notification PdfNotification
```
This command will place a fresh notification class in the `app/Notifications` directory.

In the `__constructor()` add this code:
```php
<?php
/**
* @var string
*/
public $filePath;
/**
* @var string
*/
public $fileUrl;

/**
* Create a new notification instance.
*
* @param string $filePath
* @param string $fileUrl
*/
public function __construct(string $filePath, string $fileUrl)
{
  $this->filePath = $filePath;
  $this->fileUrl = $fileUrl;
}
```

In the `toMail()` method, add this code:
```php
<?php

/**
* Get the mail representation of the notification.
*
* @param  mixed  $notifiable
* @return MailMessage
*/
public function toMail($notifiable): MailMessage
{
return (new MailMessage)
    ->subject('Laravel Notification')
    ->from('youremail@domain.com')
    ->greeting("Dear " . $notifiable->name . ",")
    ->line("Please find attached your document")
    ->line('Thank you for using our application!')
    ->action('Download File', url($this->fileUrl))
    ->attach($this->filePath);
}
```

In the `PdfGeneratorController`, add these classes:
```php
use App\Models\User;
use Illuminate\Support\Facades\Notification;
```

Next, inside the `store()` method, before the returning the `response()->json()` create a `User` and send them a notification:
```php
<?php
$user = new User([
    'email' => $request['email'],
    'name' => $request['firstname']
]);
Notification::send($user, new PdfNotification($publicPath, url($fileStoragePath)));
```

The email should look like this:
![image](https://user-images.githubusercontent.com/22425217/111843594-c485ae80-8901-11eb-94b8-4075231f0ff9.png)

<!--
## Code Repository
Fork the [code repository](https://github.com/aghwotu/pdf-generator) here
-->