Features

Developers

Create your own version of Instagram using api.video and Laravel

September 28, 2021 - Erikka Innes in PHP

If you've ever wanted to take the time to learn how to build your own social media platform, this tutorial will get you started. Using the incredible Laravel PHP framework tutorial from Victor Gonzalez, you can set up the basics for Instagram. However, his tutorial doesn't include video support. That's the tweak I'm providing today with this tutorial.

If you want a complete version of Instagram, things you'll need to add yourself are:

  • More robust population for a person's page when it opens, for example linking to their profile instead of typing the number (which you'll know since there won't be many users when you set it up) to go to your profile or someone else's.
  • Ability to delete images and videos.
  • Search capability.
  • Algorithm for what to prioritize showing someone in their feed if there's lots of followers.
  • More robust database.
  • Move email out of test mode to actual email that's sent.

Let's get started!

Installation

For this project, you should be familiar with Laravel already, or willing to go through Victor's 4 hour tutorial to learn everything you'll need. Make sure you have these things ready:

To get the tutorial version of the code to work with, go to https://github.com/coderstape/freeCodeGram. If you want the code with the api.video tweaks included, and the tutorial completely put together, then go here: https://github.com/apivideo/freeCodeGram.

The next part of installation will require you to clone the github repository and then configure the project. There is a great walkthrough here: How to Setup a Laravel Project You Cloned from Github.com. There are some steps you won't need, the steps you need to follow are: 1, 2, 4, 5 (I recommend not using yarn), 6, 7, 8, 9 and 10.

For step 8, this project uses a flat SQLite file for the database. To set this up, go into your project and type:

vim database/database.sqlite

After it's created, save it without typing anything into the file.

Then go to step 9, and make sure the information is filled out appropriately for the database (it should be). The correct set up is just to have DBCONNECTION=sqlite. There should be no other DB items listed.

Follow step 10. You're done with the walkthrough! There's just a couple more configuration things to do.

Authentication

You'll need to go into php artisan and run the ui:auth command:

php artisan ui:auth

Storage

You will need to go into freeCodeGram/storage/public and add an uploads folder.

You may need to run the storage:link command as well:

php artisan storage:link

Set up your php.ini file

If you want to handle videos that are 10 MB or more, you have to change the settings for what you can and can't upload. To do this, check the status and location of your file first:

php --ini

This will bring back the location of the file, in the line: Configuration File (php.ini) Path: /etc
or something similar.

It'll also bring back the line: Loaded Configuration File: with details after it. The details will either list the configuration file that's loaded, or it will show that there isn't one at all.

It's recommended that you use a virtual environment like homestead or similar.

If you don't have a php.ini file

You need to add one. An easy way to do that is to use the available default. The command would look like this if you use a virtual environment: cp /etc/php.ini/default /etc/php.ini

If you add it and don't have a virtual environment, you may be denied installation privileges unless you use sudo to install. The command would look like this: sudo cp /etc/php.ini/default /etc/php.ini

Then provide your credentials to complete installation.

Next, you'll need to go into the file to modify it. If you're in a virtual environment, you won't need sudo, but otherwise you may need sudo included in the command to make the modifications. Type: sudo nano /etc/php.ini

Leave sudo off for virtual environments, or if you're able to leave it off. Once in the php.ini file, you need to look for two variables:

  • upload_max_filesize
  • post_max_size

Don't make these too big, or it will cause you problems, but you can increase upload_max_filesize up to 20M and post_max_size can be increased to 21M. post_max_size is the size of your video file plus anything else you're sending in the form with the video. In our case, a caption, so not too much extra data.

Save your file when you're done making the changes.

Set up .env file

You're going to use api.video, so you'll want to add your API key to your .env file. Open the file and where it says APP_APIVIDEO=yourkeyhere, remove yourkeyhere and put your key instead. You don't need to add quotes to show it's a string. You can find your API key in your dashboard here: https://my.api.video after you log in. If you need a new account, then click Get Started to sign up.

What we'll be doing

The project is designed to teach you many of the key features of Laravel. It shows you how to store images, and then retrieve them with the help of an image repository and a database containing details about each image that can be used for retrieval. We are going to tweak things so that when you upload a video, it's sent to api.video. Then an embed link is stored in your database. This information is pulled from the database and displayed in your profile feed in an iframe.

You'll need to have these things:

The client can be installed by going into your project and typing the command: composer require api-video/php-api-client

Make sure you also have the symfony http-client: composer require symfony/http-client

Run composer update to make sure everything ends up in your project.

You can then add the API as needed. We're going to modify the following files in the project:

  • PostsController.php
  • posts/index.blade.php
  • posts/show.blade.php
  • profiles/index.blade.php

Everything is already set up for you in the repository. This walkthrough just shows you the modifications. The big change is setting things up so you can accept videos as well as images. It's limited to .mp4 and .mov files. The way the demo works before tweaks, is you upload an image and store it in storage/app/public. Then you reference it in a table in your database and retrieve images for displays as needed.

We change things so that you can upload a video to api.video, then store a link to the mp4 in the database. When you retrieve a link, you'll check if it starts with http or not. If it doesn't, it's an image file in the storage/app/public folder. If it does, it's a video.

What follows are the code samples for the different files.

PostsController.php

This controller handles everything to do with posts, from creating to displaying to storing them. We're going to modify the store() function. The code sample shows everything added into the file - you'll also need the rest of the file as it's set up. I've just pulled out the relevant section for our discussion.

use Symfony\Component\HttpClient\Psr18Client;
use ApiVideo\Client\Client;
use ApiVideo\Client\Model\VideoCreationPayload;

public function store()
    {

        $extension = request('image')->extension();
        if ($extension == 'mp4' || $extension == 'mov') {
            $data = request()->validate([
                'caption' => 'required',
                'image' => ['required', 'mimes:mp4, mov']
            ]);

            $httpClient = new Psr18Client();
            $client = new Client(
                'https://ws.api.video',
                env('APP_APIVIDEO'),
                $httpClient
            );
            $file = request()->file('image');
            $fileName = $file->getClientOriginalName();
            $payload = (new VideoCreationPayload())
                ->setTitle($fileName);

            $video = $client->videos()->create($payload);

            $filePath = request()->file('image')->getRealPath();

            $response = $client->videos()->upload(
                $video->getVideoId(),
                new \SplFileObject($filePath)
            );

            $response = json_decode($response);
            $imagePath = $response->assets->player;

        }

        else {
            $data = request()->validate([
                'caption' => 'required',
                'image' => ['required', 'mimes:jpg, png'],
            ]);
            $imagePath = request('image')->store('uploads', 'public');

            $image = Image::make(public_path("storage/{$imagePath}"))->fit(1200, 1200);
            $image->save();
        }

        auth()->user()->posts()->create([
            'caption' => $data['caption'],
            'image' => $imagePath,
        ]);

        return redirect('/profile/' . auth()->user()->id);
    }

Here, we're modifying the code for 'image'. We check 'image' to see if it has a .mp4 or .mov extension. If so, we run validation checking it that's to do with video. If everything checks out with the file, we send a request to api.video to upload a new video. We create a video container and upload our file into the container. Then we retrieve the embed link for the mp4 and send that path to be saved in the database for posts. Finally, we return the profile page, where the new post will be displayed.

posts/index.blade.php

For this section, inside the @foreach statement we update the display tags to handle image or video, depending on what we retrieve from our database.

        <div class="row">
            <div class="col-6 offset-3">
                    @if (str_contains($post->image, 'http'))
                        <a href="/profile/{{ $post->id }}">
                            <iframe src="{{ $post->image }}" class="w-100" ></iframe>
                        </a>

                    @else
                        <a href="/profile/{{$post->id}}">
                            <img src="/storage/{{$post->image}}" class="w-100">
                        </a>
                    @endif
                </div>

        </div>

Here we can see that if we have a link that starts 'http' we choose to display with an iframe because we know it will be a video. If we have a link that starts with anything else, we know it's an image and display with an image tag.

posts/show.blade.php

When we show a post, we want to make sure we display it with an iframe for a video, and an img tag for an image.

        <div class="col-8">

                @if (str_contains($post->image, 'http'))
                    <a href="/p/{{ $post->id }}">
                        <iframe src="{{ $post->image }}" class="w-100"></iframe>
                    </a>

                @else
                    <a href="/p/{{$post->id}}">
                        <img src="/storage/{{$post->image}}" class="w-100">
                    </a>
                @endif
            </div>

Here, using an @if statement in blade, we set up a way to handle a post if it's video or if it's an image. Videos only use half the space of an image.

profiles/index.blade.php

This is more of the same tweak. We want to be able to handle and display an image or a video properly. The code looks about the same:

        <div class="row pt-5">
            @foreach($user->posts as $post)
                <div class="col-4 pb-4 ">
                    @if (str_contains($post->image, 'http'))
                       <a href="/p/{{ $post->id }}">
                           <iframe src="{{ $post->image }}" class="h-100"></iframe>
                       </a>

                    @else
                        <a href="/p/{{$post->id}}">
                            <img src="/storage/{{$post->image}}" class="w-100">
                        </a>
                    @endif
                </div>
            @endforeach
        </div>

More tweaks

There's a lot more you can add to this project. You can make it so you can navigate to your profile without having to know what number you are. You could consider learning to add a search bar, so you can hunt for users. You can add tagging and ways to display pictures that match certain tags. There's a lot of ways to build on what we have here, but this will get you started!

Erikka Innes

Developer Evangelist

Create your free account

Start building with video now