در حال بارگزاری ...

آموزش آپلود فایل در لارول

توسط مریم مهربان
آخرین به روز رسانی شنبه 16 آذر 1398

زمانی که در لاراول عکسی را آپلود می‌کنید، در واقع عکس خود را در یک رسانه ذخیره سازی مانند سیستم فایل محلی، Amazon S3 یا حتی منبع ذخیره سازی ابری Rackspace ذخیره می‌کنید. آن چیزی که در پایگاه داده ذخیره می‌شود، مسیر نگهداری فایل عکس است.

 به این منظور، می‌توانید ستونی به نام avatar_path را به جدول user اضافه کرد و مایگرشن موردنیاز را با دستور زیر ایجاد کنید.

vagrant@homestead:~/Code/forumio$ php artisan make:migration add_avatar_path_to_users_table --table=users
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddAvatarPathToUsersTable extends Migration
{
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('avatar_path')->nullable();
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }
}
vagrant@homestead:~/Code/forumio$ php artisan migrate
Migrating: 2018_02_27_193627_add_avatar_path_to_users_table
Migrated:  2018_02_27_193627_add_avatar_path_to_users_table

اضافه کردن ستون avatar_path

آپلود فایل در لاراول

اکنون کلاسی به نام AddAvatarTest را برای تست ایجاد می‌کنیم و تابع test_a_user_may_add_an_avatar_to_their_profile را در این کلاس تعریف می‌کنیم. 

public function test_a_user_may_add_an_avatar_to_their_profile()
{
    $this->signIn();

    Storage::fake('public');

    $this->json('POST', 'api/users/' . auth()->id() . '/avatar', [
        'avatar' => $file = UploadedFile::fake()->image('avatar.jpg')
    ]);

    $this->assertEquals('avatars/' . $file->hashName(), auth()->user()->avatar_path);

    Storage::disk('public')->assertExists('avatars/' . $file->hashName());
}

 اگر برنامه بالا را اجرا کنیم، با خطای زیر مواجه می‌شویم:

agrant@homestead:~/Code/forumio$ phpunit --filter test_a_user_may_add_an_avatar_to_their_profile
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 1.36 seconds, Memory: 10.00MB

There was 1 error:

1) Tests\Feature\AddAvatarTest::test_a_user_may_add_an_avatar_to_their_profile
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: POST http://localhost/api/users/1/avatar

 این خطا بیانگر نبود route موردنیاز است. به این منظور، یک route جدید به فایل web.php اضافه می‌کنیم.

Route::post('api/users/{user}/avatar', 'Api\UserAvatarController@store')->middleware('auth')->name('avatar');

 اگر دوباره برنامه را اجرا کنیم، خطای اول رفع می‌شود. 

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_user_may_add_an_avatar_to_their_profile
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 1.23 seconds, Memory: 10.00MB

There was 1 error:

1) Tests\Feature\AddAvatarTest::test_a_user_may_add_an_avatar_to_their_profile
ReflectionException: Class App\Http\Controllers\Api\UserAvatarController does not exist

خطای بالا، به این دلیل ایجاد شده که کلاس  UserAvatarController  وجود ندارد.

vagrant@homestead:~/Code/forumio$ php artisan make:controller Api/UserAvatarController
Controller created successfully.

برای رفع خطای دوم، باید تابع store()  را در کنترلر آن ایجاد کرد. در این تابع بررسی می‌کنیم که آیا کاربر، یک عکس را آپلود کرده است یا خیر. سپس بررسی می‌شود که آیا کاربر اجازه آپلود فایل را دارد یا خیر و در نهایت، مسیر نگهداری فایل را برای ذخیره سازی فایل عکس آپلود شده کاربر را تعیین می‌کنیم. 

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;

class UserAvatarController extends Controller
{
    public function store()
    {
        $this->validate(request(), [
            'avatar' => ['required', 'image']
        ]);
        auth()->user()->update([
            'avatar_path' => 'storage/' . request()->file('avatar')->store('avatars', 'public')
        ]);
        return response([], 204);
    }
}

برنامه بالا با خطای زیر مواجه می‌شود که علت آن، خالی بودن ستون avatar_path است. به این منظور، در مدل User.php، تابع مدیریت استثنا اضافه می‌کنیم.

<?php

namespace App;

use Carbon\Carbon;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password', 'avatar_path'
    ];
}

  ایجاد یک سیاست کاربری

برای ایجاد مجوز به روزسانی عکس پروفایل، می‌توان از یک شی policy استفاده کرد. البته هر فرد تنها باید اجازه تغییر پروفایل کاربری خود را داشته باشد.

php artisan make:policy UserPolicy

 اگر شناسه کاربر وارد شده به سیستم با شناسه کاربری که اطلاعات وی در حال به روزرسانی برابر باشد، به روزرسانی مجاز است.

<?php

namespace App\Policies;

use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy
{
    use HandlesAuthorization;

    public function update(User $signedInUser, User $user)
    {
        return $signedInUser->id === $user->id;
    }
}

 هر زمان که سیاستی جدید ایجاد می‌کنید، باید آن را در AuthServiceProvider ثبت کنید.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        //'App\Model' => 'App\Policies\ModelPolicy',
        'App\Thread' => 'App\Policies\ThreadPolicy',
        'App\Reply' => 'App\Policies\ReplyPolicy',
        'App\User' => 'App\Policies\UserPolicy',
    ];

    public function boot()
    {
        $this->registerPolicies();
    }
}

با این سیاست کاربری، ما می‌توانیم ناحیه آپلود فایل را تنها برای کاربرانی که مجاز هستند، نشان دهیم. اما پیش از آن، کدهایی را به کلاس AddAvatarTest اضافه می‌کنیم که اطمینان حاصل می‌کند که تنها کاربران مجاز می‌توانند عکس آپلود کنند و فایل آپلود شده، یک عکس معتبر است.

public function test_only_members_can_add_avatars()
{
    $this->withExceptionHandling();

    $this->json('POST', 'api/users/1/avatar')
        ->assertStatus(401);
}

public function test_a_valid_avatar_must_be_provided()
{
    $this->withExceptionHandling()->signIn();

    $this->json('POST', 'api/users/' . auth()->id() . '/avatar', [
        'avatar' => 'not-an-image'
    ])->assertStatus(422);
}

 مرتبط کردن دایرکتوری ذخیره سازی به دایرکتوری عمومی

برای دسترسی به تصاویر، لازم است به دایرکتوری public دسترسی داشته باشید. اما در لاراول، تصاویر در دایرکتوری storage، ذخیره می‌شوند. برای ارتباط دادن این دو دایرکتوری به یکدیگر، می‌توانید از لینک‌های نمادین استفاده کرد. برای ایجاد لینک‌های نمادین باید به صورت دستی، این کار را انجام دهید. برای این کار، مشابه زیر عمل کنید.

C:\>mklink /D C:\localdev\forumio\public\storage C:\localdev\forumio\storage\app\public
symbolic link created for C:\localdev\forumio\public\storage <<===>> C:\localdev\forumio\storage\app\public

 

ایجاد آواتار پیش فرض

زمانی که کاربری در وب سایت ثبت نام می‌کند، می‌توان از یک تصویر پیش‌فرض به عنوان عکس پروفایل وی استفاده کرد. به این منظور، تابعی را به مدل User.php اضافه می‌کنیم. این تابع، تلاش می‌کند عکس پروفایل کاربری را دریافت کنید و اگر این عکس وجود نداشته باشد از عکس پیش‌فرض استفاده می‌کند.

public function getAvatarPathAttribute($avatar)
{
    return asset($avatar ?: 'images/avatars/default.png');
}

برای ایجاد کامپوننت فرانت اند برای آپلود فایل، ابتدا کامپوننت ImageUpload.vue را ایجاد می‌کنیم.

image-upload

 این کامپوننت، شامل تگ‌های زیر است:

<template>
    <input type="file" accept="image/*" @change="onChange">
</template>

<script>
    export default {
        methods: {
            onChange(e) {
                if (! e.target.files.length) return;

                let file = e.target.files[0];

                let reader = new FileReader();

                reader.readAsDataURL(file);

                reader.onload = e => {
                    let src = e.target.result;

                    this.$emit('loaded', { src, file });
                };
            }
        }
    }
</script>

 

<template>
    <div>
        <div class="level">
            <img :src="avatar" width="50" height="50" class="mr-1">

            <h1 v-text="user.name"></h1>
        </div>

        <form v-if="canUpdate" method="POST" enctype="multipart/form-data">
            <image-upload name="avatar" class="mr-1" @loaded="onLoad"></image-upload>
        </form>

    </div>
</template>

<script>
    import ImageUpload from './ImageUpload.vue';

    export default {
        props: ['user'],

        components: {ImageUpload},

        data() {
            return {
                avatar: this.user.avatar_path
            };
        },

        computed: {
            canUpdate() {
                return this.authorize(user => user.id === this.user.id);
            }
        },

        methods: {
            onLoad(avatar) {
                this.avatar = avatar.src;

                this.persist(avatar.file);
            },

            persist(avatar) {
                let data = new FormData();

                data.append('avatar', avatar);

                axios.post(`/api/users/${this.user.name}/avatar`, data)
                    .then(() => flash('Avatar uploaded!'));
            }
        }
    }
</script>

 ثبت کامپوننت Vue

لازم است کامپوننت AvatarForm.vue را در فایل app.js ثبت کنید.

require('./bootstrap');

window.Vue = require('vue');

Vue.component('flash', require('./components/Flash.vue'));

Vue.component('paginator', require('./components/Paginator.vue'));

Vue.component('user-notifications', require('./components/UserNotifications.vue'));

Vue.component('avatar-form', require('./components/AvatarForm.vue'));

Vue.component('thread-view', require('./pages/Thread.vue'));

const app = new Vue({
    el: '#app'
});

تنظیم عکس پیش فرض

به منظور ارجاع به عکس پیش فرض، لازم است آن را روی سرور دیگری ذخیره کنیم. برای نمونه، آن را در مسیر public/images/avatars ذخیره کنید.

تنظیم عکس آواتار

نمایش عکس آواتار

برای نمایش این عکس، لازم است در فایل threads/show.blade.php به ان ارجاع دهید.

@extends('layouts.app')

@section('head')
    <link rel="stylesheet" href="/css/vendor/jquery.atwho.css">
@endsection

@section('content')
    <thread-view :initial-replies-count="{{ $thread->replies_count }}" inline-template>
        <div class="container">
            <div class="row">
                <div class="col-md-8">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <div class="level">
                                <img src="{{ $thread->creator->avatar_path }}"
                                     alt="{{ $thread->creator->name }}"
                                     width="25"
                                     height="25"
                                     class="mr-1">
                                <span class="flex">
                                <a href="{{ route('profile', $thread->creator) }}">{{ $thread->creator->name }}</a> posted:
                                    {{ $thread->title }}
                            </span>

اضافه کردن قابلیت Image Uploader به صفحات پروفایل

کامپوننت Vue <avatar-form> را به فایل profiles/show.blade.php اضافه می‌کنیم. با این فرم، کاربر به سادگی عکس موردنظر خود را انتخاب می‌کند و این عکس به صورت آنی، روی سرور ذخیره می‌شود.

@extends('layouts.app')

@section('content')

    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="page-header">
                    <avatar-form :user="{{ $profileUser }}"></avatar-form>
                </div>
                @forelse($activities as $date => $activity)
                    <h3 class="page-header">{{ $date }}</h3>
                    @foreach($activity as $record)
                        @if (view()->exists("profiles.activities.{$record->type}"))
                            @include("profiles.activities.{$record->type}", ['activity' => $record])
                        @endif
                    @endforeach
                @empty
                    <p>No activity for this user</p>
                @endforelse
            </div>
        </div>
    </div>

@endsection

 کامپوننت آپلودر عکس Vue چگونه کار می کند؟

کاربر، عکسی را انتخاب می‌کند و انتخاب عکس باعث ایجاد رخداد change می‌شود و تابع onChange را فراخوانی می‌کند.

<input type="file" accept="image/*" @change="onChange">

در این تابع، بررسی می‌‌کند که فایلی آپلود شده است. در این صورت، فایل در متغیر file ذخیره می‌شود:

if (!e.target.files.length) return;

let file = e.target.files[0];

سپس از شی File Reader جاوا اسکریپت، برای خواندن محتوای فایل استفاده می‌شود. به طور خاص، از تابع readAsDataURL() برای تبدیل محتوای فایل به کدگذاری base64 استفاده می‌شود.

let reader = new FileReader();
reader.readAsDataURL(file);

پس از آن، رخداد loaded که AvatarForm.vue به آن گوش می‌دهد، ایجاد می‌شود که منجر به فراخوانی onLoad می‌شود.

this.$emit('loaded', {src, file});

<image-upload name="avatar" class="mr-1" @loaded="onLoad"></image-upload>

این تابع، منبع عکس را مشخص می‌کند و مسیر فایل را در پایگاه داده ذخیره می‌کند.

onLoad(avatar) {
    this.avatar = avatar.src;

    this.persist(avatar.file);
}

تابع persist، از واسط FormData جاوا اسکریپت برای نمایش فیلدهای فرم و مقادیر آن‌ها در قالب‌های کلید و مقادیر استفاده می‌کند. این اطلاعات در قالب یک درخواست با تابع post به سرور ارسال می‌شود.

persist(avatar) {
    let data = new FormData();

    data.append('avatar', avatar);

    axios.post(`/api/users/${this.user.name}/avatar`, data)
        .then(() => flash('Avatar uploaded!'));
}

مقالات بیشتر در لیداوب:

  امیدوارم این مقاله آموزشی برای شما مفید باشد. با سایر مقالات ما در لیداوب همراه باشید.

دیدگاه ها

دیدگاه ها : 0


متاسفانه فقط اعضای سایت قادر به ثبت دیدگاه هستند

رایگان

اشتراک گذاری در
ثبت امتیاز
5 (1 رای)

   لطفا صبر کنید ...