DARK MODE 

Posted on Monday, June 6, 2022 by

Create Instagram Clone with Laravel (Part 13): Setting Default Profile Image and Create Follow Button with Vue Component

In this post, I covered from minute 3:12:56 until 3:33:55 of this video.

When the user first creates the profile, there is no profile image at all. So we do need a default profile image. 

Displaying default profile image
First upload the default photo to your profile. Then, copy the link because we just wanna get the filename. After that, we create a new method in the profiles model. In this new method. we say that if the image is set, then we're going to return /storage/ and then that image. Otherwise, let's return the default image which in my case, profile/MdCtU1BFI5rfrDfViQ6UeoYb22wcKkg0n0ClxFYU.jpg .

14
15
16
17
18
    public function profileImage()
    {
        $imagePath = ($this->image) ? $this->image : 'profile/MdCtU1BFI5rfrDfViQ6UeoYb22wcKkg0n0ClxFYU.jpg';
        return '/storage/' . $imagePath;
    }
app > Models > Profile.php

Now, all we need to do is call that method in the view where we want to show the profile image.

6
7
8
        <div class="col-4 d-flex align-items-center justify-content-center">
            <img src="{{ $user->profile->profileImage() }}" class="rounded-circle w-50" style="border: 1px solid #e4e3e1">
        </div>
resources > views > profiles > index.blade.php

14
                <img src="{{ $post->user->profile->profileImage() }}" class="rounded-circle mr-3" style="width:40px;border: 1px solid #e4e3e1">
resources > views > profiles > show.blade.php


Solving problem with empty image field in edit profile form
When we try to save our profile in the edit page without uploading any profile image, we will get Undefined variable: imagePath variable. This is because the way we wrote our code before is that we assuming that there's always going to be an image path. In real world situation, the image path may not be set. And if it's not set, it just means that there is no image in the request. But what we've done in the code so far is still trying to insert that image path to the profile. Hence, it return an error.

To fix that, we extract the image path to a variable and call the variable $imageArray after we saved the image. Then. in the array_merge function, we're going to say that if the $imageArray is not set, then let's default it to an empty array, []. An empty array will not override anything in our $data. To sum it up, we're setting an $imageArray that is going to contain the image, but this is only happening if there is an image in the request. Otherwise, the only thing we're going to pass in is an empty array.

33
34
35
36
37
38
39
40
41
42
43
44
        if (request('image')) {
            $imagePath = request('image')->store('profile', 'public');

            $image = Image::make(public_path("storage/{$imagePath}"))->fit(1000, 1000);
            $image->save();
            $imageArray = ['image' => $imagePath];
        }

        auth()->user()->profile->update(array_merge(
            $data,
            $imageArray ?? []
        ));
apps > Http > Controllers > ProfilesController.php

Now you can try it! You should be able to save your profile without uploading any profile image, and your current profile image wouldn't be overwritten.

Creating follow button - UI
Let's start with the UI for the profile.

11
12
13
14
                <div class="d-flex align-items-start pb-2">
                    <h3 class="font-weight-light mr-3">{{ $user->username }}</h3>
                    <button class="btn btn-primary btn btn-primary btn-sm pl-4 pr-4 ml-3">Follow</button>
                </div>
resources > views > profiles > index.blade.php



When we click the button, we don't want the entire page to be loaded again, don't we? This can be achieve by converting the button to Vue component. As you already know, Vue is a JavaScript library. Laravel comes with a working component named ExampleComponent.vue in the resources > js > components and it is registered in the app.js. Let's rename ExampleComponent.vue to FollowButton.vue, and change the name in app.js also.

22
Vue.component('follow-button', require('./components/FollowButton.vue').default);
resources > js > app.js

We're not gonna learn deeply about Vue because this is a Laravel course. But keep in mind that the most important thing about Vue is everything needs to be wrapped in a single div tag inside template tag. It must be one. You cannot have two div because then it will tell you that there is no root div. What we need to do next is copy the follow button code that we done in index.blade.php, paste it inside the component. After that, change the follow button code in index.blade.php to follow-button tag as shown below.



1
2
3
4
5
<template>
    <div>
        <button class="btn btn-primary btn btn-primary btn-sm pl-4 pr-4 ml-3">Follow</button>
    </div>
</template>
resources > js > app.js

11
12
13
14
                <div class="d-flex align-items-start pb-2">
                    <h3 class="font-weight-light mr-3">{{ $user->username }}</h3>
                    <follow-button></follow-button>
                </div>
resources >views > profiles > index.blade.php

Now, we need to run npm run dev. However, run dev is what we would use if we want to run just one time. If we run npm run watch, it will continue to watch all of our files so whenever there's a change, it would automatically compile all of our code again. This is what we should use during development.


If you open the profile page, you won't see any difference at all.

Creating follow button - action
Next we want to do is of course, create the action for the button. We need to reach out to the server and ask it to do something when we click the button. Before we do the reach out the server part, let's try making the button to do something. Fairly easy. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<template>
    <div>
        <button class="btn btn-primary btn btn-primary btn-sm pl-4 pr-4 ml-3" @click="followUser">Follow</button>
    </div>
</template>

<script>
export default {
    mounted() {
        console.log('Component mounted.')
    },

    methods: {
        followUser() {
            alert('inside');
        }
    }
}
</script>
resources > js > app.js

If you click the button, an alert should should be popped out.




Now, because we want to follow a user, we need to get that user id and pass it to the method in component. To do that, we can just pass the id through the follow-button.

13
                    <follow-button user-id="{{ $user->id }}"></follow-button>
resources >views > profiles > index.blade.php

Then, to get the id, we write:

 8
 9

20
export default {
    props: ['userId'],
 ...   
} 
resources > js > app.js

To reach out to the server, we can do that with Axios that comes with Laravel. Axios is a library that allows you to make API call extremely easy. So inside the followUser method, we need to say, "Axios, make a POST request to an endpoint, and then display the data".

19
19
20
21
22
23
        followUser() {
            axios.post('/follow/' + this.userId)
            .then(response => {
                alert(response.data);
            });
        }
resources > js > app.js

We then need to create a route for the new axios call.

20
21
22
Auth::routes();

Route::post('/follow/{user}', [App\Http\Controllers\FollowsController::class, 'store']);
routes > web.php

Next, create the controller for Follows.

php artisan make:controller FollowsController

And add a new store method. For now, let's just return the username.

 8
 9
10
11
12
13
14
class FollowsController extends Controller
{
    public function store(User $user)
    {
        return $user->username;
    }
}
app > Http > Controllers > FollowsController.php

The button should be able to return the user's username successfully.





Creating follow button - connect Profile to User
Now, we are gonna explore the many to many relationship. Up to this point, we talked about one-to-one and one-to-many relationship, but now, many-to-many. Why? Because a profile can have many followers and a user can follow many profiles. In the previous relationship, we just add a foreign key to the table. But in many-to-many, we need to create a pivot table. A pivot table is a table that holds the ID of the two related models. If I'm right, the ERD is this.




For this one, we don't need a model, we only need the migration. To make this migration, you must follow a naming convention. For the naming convention, we first must identify what are the two models that we want to connect. We want to connect Profile and User. In Laravel, we need to put them in alphabetical order. So Profile first because P comes first and U comes later. We also need to write them in all lowercase character and put underscore in between the names. We then got profile_user - this will be the table name. And for our migration name, we use creates_profile_user_pivot_table. The final command would be

php artisan make:migration creates_profile_user_pivot_table --create profile_user


Let's open the newly created migration file and add two fields which are profile_id and user_id.

16
17
18
19
20
21
        Schema::create('profile_user', function (Blueprint $table) {
            $table->id();
            $table->foreignId('profile_id');
            $table->foreignId('user_id');
            $table->timestamps();
        });
database > migrations > Controllers > 2022_06_06_055949_creates_profile_user_pivot_table.php

Lastly, migrate the table

php artisan migrate

We now done with connecting the profile and user model. However, there is still more needs to be done for the follow button to fully functioning like Instagram. But I need to stop right here because the post is getting too long. We continue completing the button in the next part! 












1 comment: