ساخت فروشگاه اینترنتی با لاراول و Vue

How to Create An Ecommerce With Laravel & Vuejs

19 شهریور 1402
how to create an ecommerce with laravel and vuejs

در این مقاله به نحوه ساخت فروشگاه اینترنتی با لاراول و vue خواهیم پرداخت. پس از این آموزش، باید بدانید که چگونه از لاراول و ویو برای ساخت یک اپلیکیشن تحت وب استفاده کنید و اصول ساخت فروشگاه اینترنتی را بدانید.

با ظهور اینترنت، بسیاری از کارهای عادی سریعتر و آسان تر شدند. یکی از چیزهایی که بهبود یافته، تجارت است. فعالیت های تجاری انجام شده در وب به عنوان تجارت الکترونیک شناخته می شود.

در این آموزش قصد داریم مراحل ساخت یک فروشگاه اینترنتی با لاراول (Laravel) و ویو (Vue) را از صفر تا صد به شما آموزش دهیم. فقط باید دقت داشته باشید که این فروشگاه اینترنتی قابلیت های ساده ای مثل نمایش محصولات، خرید محصول و ... را دارد. در نهایت وقتی کار ما تمام شد، فروشگاهی به شکل زیر خواهیم داشت:

ساخت فروشگاه اینترنتی با لاراول و vue
ساخت فروشگاه اینترنتی با لاراول و vue

 

پیش نیازها

نکته بسیار مهم اینجاست که اگر در هر یک از زمینه های فوق دانشی ندارید کافیست روی لینک های متصل شده به عبارات کلیک کرده و دوره های آموزشی پروژه محور مرتبط با آنها را ملاحظه بفرمایید.

مقدمه

همانطور که قبلا اشاره کردیم، یک فروشگاه، فروش کالا و خدمات آنلاین را بسیار راحت می کند. فهرستی از محصولاتی را که فروشنده می‌خواهد بفروشد و قیمت‌های آنها را نشان می‌دهد، سپس صفحه‌ای را نمایش می دهد که در آن می‌توانید تمام جزئیات یک محصول انتخاب شده را مشاهده کنید، و در نهایت، هزینه محصول را پرداخت کنید و نحوه دریافت آن را مشخص کنید.

با در نظر گرفتن این موضوع، می دانیم که برای داشتن یک اپلیکیشن تجارت الکترونیکی مفید، باید موارد زیر را توسعه دهیم:

  • یک صفحه برای مشاهده همه محصولات ما
  • یک صفحه برای مشاهده اطلاعات مربوط به محصول
  • صفحه تسویه حساب که در آن خریدار می تواند آدرس تحویل را وارد کرده و صورت حساب را پرداخت کند
  • یک لیست ساده برای خریدار تا تمام محصولات خریداری شده را ببیند و بداند آیا ارسال شده است یا خیر
  • امکانی برای فروشنده برای افزودن محصولات
  • امکانی برای فروشنده برای مشاهده سفارش هایی که کاربران ارسال کرده اند
  • امکانی برای نشان دادن یک محصول توسط فروشنده برای کاربر ارسال شده است
  • امکانی برای مشاهده و به روز رسانی تمام اطلاعات محصول توسط فروشنده

طراحی ساختار برنامه

این برنامه دارای دو نوع کاربر است:

  • مدیر (ادمین) - صاحب فروشگاه
  • خریدار – شخصی که می خواهد برای یکی از دوستانش هدیه بخرد.

حتما می دانید برنامه محصولاتی دارد که باید ذخیره شوند. این محصولات سفارش داده می شوند و شما به راهی برای ذخیره و پیگیری این سفارش ها نیاز دارید. این مراحل اساس هر پلتفرم تجارت الکترونیکی را تشکیل می دهند.

جزئیات اساسی که باید در قسمت های مختلف ذخیره کنیم:

Table

Fields

User

name, email, password, is_admin

 

Product

name, description, price, units, image

Order

product, user, quantity, address, is_delivered

عملیات اساسی که برنامه باید آن ها را انجام دهد، و اینکه این کارها در کجا باید انجام شوند، عبارتند از:

Operation Controller
Login UserController
Register UserController
User profile UserController
View all orders by a single user UserController
View product listing ProductController
View a single product ProductController
Edit a product ProductController
Add a new product ProductController
Add more units to a product ProductController
Remove a product ProductController
Order product OrderController
View all orders OrderController
View a single order information OrderController
Deliver an order OrderController
Delete an order OrderController

اگر می‌خواهید کمی دقیقتر باشید، می‌توانید به اضافه کردن دسته‌ بندی ها، برچسب‌ها، اقلام موجود در انبار، انواع کاربر بیشتر و بسیاری موارد دیگر فکر کنید. با این حال، ما آنها را برای این راهنما در نظر نخواهیم گرفت، اما شما قطعا باید آن را به تنهایی امتحان کنید.

یک راه خوب برای یادگیری، تمرین است. با انجام تکلیف پاراگراف قبل می توانید ذهن خود را برای چالش های مختلف نیرومند کنید.

ساخت فروشگاه اینترنتی با لاراول

ما قصد داریم از Laravel CLI برای ایجاد یک برنامه جدید Laravel استفاده کنیم. برای ایجاد یک برنامه جدید لاراول، دستور زیر را اجرا کنید:

laravel new bigStore

سپس می توانید به پروژه ای که ایجاد کردیم وارد شوید. دستورات مربوط به لاراول که در طول مقاله اجرا می کنیم باید در پوشه روت پروژه لاراول اجرا شوند.

ایجاد (Model) مدل برای برنامه

مدل های لاراول راه بسیار مناسبی برای تعامل با دیتابیس ارائه می دهند. آنها متدهایی را برای تمام عملیات اساسی که برای اجرای جدول دیتابیس خود نیاز دارید ارائه می کنند.

بیایید مدل هایی بسازیم که با دیتابیس ما تعامل داشته باشند و منطق کسب و کار را حفظ کنند. نصب‌های جدید لاراول با مدل کاربر خارج از جعبه ارائه می‌شوند که می‌توان آن را در دایرکتوری app پیدا کرد، بنابراین بیایید دو مدل دیگر را بسازیم:

php artisan make:model Product -mr

ما فلگ mr- را به دستور make:model اضافه کرده‌ایم تا مایگریشن و کنترلری را برای مدل ایجاد کند.

php artisan make:model Order -mr

سپس فایل app/User.php را باز کنید و محتویات آن را با کدهای زیر جایگزین کنید:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;

use Illuminate\Database\Eloquent\SoftDeletes;

use Illuminate\Foundation\Auth\User as Authenticatable;




class User extends Authenticatable

{

use Notifiable, SoftDeletes;




protected $fillable = [

'name', 'email', 'password',

];




protected $hidden = [

'password', 'remember_token',

];




public function orders()

{

return $this->hasMany(Order::class);

}

}

ما از SoftDeletes استفاده می کنیم تا به ما این امکان را بدهد که یک رکورد دیتا بیس را به عنوان حذف شده علامت گذاری کنیم بدون اینکه واقعا آن را حذف کنیم. اگر می خواهید داده ها را بازیابی کنید، این کار مفید است.

ما همچنین یک آرایه به نام fillable به نام ستون‌هایی که می‌خواهیم به صورت انبوه به جدول کاربران اختصاص دهیم، داریم. تخصیص انبوه زمانی اتفاق می افتد که مدل User خود را به صورت ایستا فراخوانی کنیم و یک آرایه را به متد ایجاد آن ارسال کنیم.

فایل app/Product.php را باز کرده و به صورت زیر ویرایش کنید:

<?php

    namespace App;




   use Illuminate\Database\Eloquent\Model;

    use Illuminate\Database\Eloquent\SoftDeletes;




    class Product extends Model

    {

        use SoftDeletes;




        protected $fillable = [

            'name', 'price', 'units', 'description', 'image'

        ];




        public function orders(){

            return $this->hasMany(Order::class);

        }

    }

مدل Product کاملا شبیه مدل کاربر است. دارای آرایه fillable و همچنین یک متد orders برای ایجاد رابطه با سفارشات قرار داده شده در برنامه است. اکنون فایل app/Order.php را باز کرده و به صورت زیر ویرایش کنید:

<?php

    namespace App;




    use Illuminate\Database\Eloquent\Model;

    use Illuminate\Database\Eloquent\SoftDeletes;




   class Order extends Model

   {

        use SoftDeletes;




       protected $fillable = [

            'product_id', 'user_id', 'quantity', 'address'

        ];




        public function user()

        {

            return $this->belongsTo(User::class, 'user_id');

        }




        public function product()

        {

            return $this->belongsTo(Product::class, 'product_id');

       }




    }

مدل Order کمی متفاوت از دو مدل دیگر به نظر می رسد اما در اصل یک چیز است. ما به تازگی نوع دیگری از رابطه را ایجاد کردیم. belongsTo که نشان می دهد کدام کاربر سفارشی را انجام داده یا کدام محصول را سفارش داده است.

 تعریف migration یا مهاجرت برای برنامه

مهاجرت ها راه خوبی برای ایجاد و نگهداری دیتابیس برنامه شما هستند. آنها نحوه ایجاد یا تغییر جدول ها را مشخص می کند.

مهاجرت ها مفید هستند زیرا به شما در مدیریت جدول ها، ستون ها و کلیدهای دیتابیس کمک می کنند. می‌توانید فایل‌های مهاجرت را به جای SQL به اشتراک بگذارید، و از آنجایی که فایل‌های مهاجرت به صورت زمانی اجرا می‌شوند، کار با git را آسان می‌کنند، بنابراین برای تیم‌ها عالی است.

برای ارتقاء سطح دانش خود در زمینه SQL به شما توصیه می کنیم که دوره آموزش SQL و MySQL را حتما ببینید.

فایل create_users_table را در دایرکتوری دیتابیس/migrations باز کنید و محتوا را با موارد زیر جایگزین کنید:

<?php




   use Illuminate\Support\Facades\Schema;

    use Illuminate\Database\Schema\Blueprint;

    use Illuminate\Database\Migrations\Migration;




    class CreateUsersTable extends Migration

   {

        public function up()

        {

            Schema::create('users', function (Blueprint $table) {

                $table->increments('id');

                $table->string('name');

                $table->string('email')->unique();

                $table->boolean('is_admin')->default(false);

                $table->string('password');

                $table->rememberToken();

                $table->timestamps();

                $table->softDeletes();

            });

        }




        public function down()

        {

            Schema::dropIfExists('users');

        }

    }

لاراول کاملا خوانا است و احتمالا می توانید با خواندن کد متوجه شوید که چه اتفاقی در حال رخ دادن است. ما تعریف کردیم که کدام ستون ها و ویژگی های آنها باید در جدول وجود داشته باشند. در کلاس Blueprint متدهای زیادی برای مهاجرت وجود دارد. در اینجا می توانید بیشتر در مورد آن بخوانید. سپس، فایل create_products_table را در دایرکتوری database/migrations  باز کنید و کد زیر را جایگزین کنید:

<?php




    use Illuminate\Support\Facades\Schema;

    use Illuminate\Database\Schema\Blueprint;

    use Illuminate\Database\Migrations\Migration;




    class CreateProductsTable extends Migration

    {

       public function up()

        {

            Schema::create('products', function (Blueprint $table) {

                $table->increments('id');

                $table->string('name');

                $table->string('description');

               $table->unsignedInteger('units')->default(0);

                $table->double('price');

                $table->string('image');

                $table->timestamps();

                $table->softDeletes();

            });

        }




        public function down()

        {

            Schema::dropIfExists('products');

        }

    }

در نهایت، فایل create_orders_table را در دایرکتوری database/migrations باز کنید و محتویات آن را با موارد زیر جایگزین کنید:

<?php




    use Illuminate\Support\Facades\Schema;

   use Illuminate\Database\Schema\Blueprint;

    use Illuminate\Database\Migrations\Migration;




    class CreateOrdersTable extends Migration

    {

        public function up()

        {

            Schema::create('orders', function (Blueprint $table) {

                $table->increments('id');

                $table->unsignedInteger('product_id');

                $table->unsignedInteger('user_id');

                $table->unsignedInteger('quantity')->default(1);

                $table->string('address')->nullable();

                $table->boolean('is_delivered')->default(false);

                $table->timestamps();

                $table->softDeletes();

            });

        }




        public function down()

        {

            Schema::dropIfExists('orders');

        }

    }

ایجاد seeder ها برای برنامه

seeder ها یک راه عالی برای پر کردن دیتابیس ما با داده های ساختگی است. ما قصد داریم از کلاس seeder برای ایجاد حساب کاربری برای مدیریت خود استفاده کنیم.

با اجرای دستور زیر یک کلاس seeder ایجاد کنید:

php artisan make:seed UsersTableSeeder

اکنون فایل UserTableSeeder.php را در دایرکتوری database/seeds باز کرده و محتوا را با موارد زیر جایگزین کنید:

<?php




    use App\User;

    use Illuminate\Database\Seeder;




    class UsersTableSeeder extends Seeder

    {

        public function run()

        {

            $user = new User;

            $user->name = "Admin";

            $user->email = "admin@devtest.com";

            $user->password = bcrypt('secret');

            $user->is_admin = true;

            $user->save();

        }

    }

کلاس Seeder بالا یک کاربر مدیریت جدید در دیتابیس ایجاد می کند. به یاد بیاورید که وقتی مدل User را تعریف کردیم، is_admin را در ستون fillable قرار ندادیم. دلیل آن این است که ما نمی خواهیم کسی که برنامه ما را جعل کند، یک کاربر  از نوع is_admin  ایجاد کند. به همین دلیل است که ما مجبور شدیم یک نمونه کاربری در اینجا ایجاد کنیم تا بتوانیم به تمام ستون ها از جدول User دسترسی داشته باشیم.

یک کلاس Seeder دیگر برای جدول محصولات ما می سازیم:

php artisan make:seed ProductsTableSeeder

فایل database/seeds/ProductTableSeeder.php را باز کنید و محتویات آن را با موارد زیر جایگزین کنید:

<?php




   use Illuminate\Database\Seeder;




    class ProductsTableSeeder extends Seeder

   {

       public function run()

        {

            $products = [

                [

                    'name' => "MEN'S BETTER THAN NAKED & JACKET",

                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

                    tempor incididunt ut labore et dolore magna aliqua consequat.',

                    'units' => 21,

                    'price' => 200.10,

                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/mens-better-than-naked-jacket-AVMH_LC9_hero.png',

                    'created_at' => new DateTime,

                    'updated_at' => null,

                ],

                [

                    'name' => "WOMEN'S BETTER THAN NAKED™ JACKET",

                   'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

                    tempor incididunt ut labore et dolore magna aliqua consequat.',

                    'units' => 400,

                   'price' => 1600.21,

                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-better-than-naked-jacket-AVKL_NN4_hero.png',

                    'created_at' => new DateTime,

                    'updated_at' => null,

                ],

                [

                    'name' => "WOMEN'S SINGLE-TRACK SHOE",

                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

                    tempor incididunt ut labore et dolore magna aliqua consequat.',

                    'units' => 37,

                    'price' => 378.00,

                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-single-track-shoe-ALQF_JM3_hero.png',

                   'created_at' => new DateTime,

                    'updated_at' => null,

                ],

                [

                    'name' => 'Enduro Boa® Hydration Pack',

                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

                    tempor incididunt ut labore et dolore magna aliqua consequat.',

                    'units' => 10,

                    'price' => 21.10,

                   'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/enduro-boa-hydration-pack-AJQZ_JK3_hero.png',

                    'created_at' => new DateTime,

                    'updated_at' => null,

                ]

            ];




            DB::table('products')->insert($products);

        }

    }

داده هایی که ما دیتابیس را با آنها پر کردیم برای اهداف آموزشی است. ممکن است برای استفاده واقعی از تصاویر به مجوز نیاز داشته باشید. هنگامی که ساختن seeder را تمام کردید، باید دیتابیس/seeds/DatabaseSeeder.php را تغییر دهید، که در واقع seeders را فراخوانی می کند:

<?php

    use Illuminate\Database\Seeder;




    class DatabaseSeeder extends Seeder

    {

        public function run()

        {

            $this->call([

              UsersTableSeeder::class,

              ProductsTableSeeder::class,

            ]);

       }

    }

اکنون، هنگامی که دستور seed دیتابیس خود را اجرا می کنیم، کلاس DatabaseSeeder فراخوانی می شود و run را فراخوانی می کند که به نوبه خود کلاس های seeder را که برای اجرا تنظیم کرده بودیم، فراخوانی می کند. در اینجا می توانید اطلاعات بیشتری در مورد seeder ها به دست آورید.

ایجاد دیتابیس برای فروشگاه اینترنتی در لاراول

هر چیزی را که نیاز داریم دیتابیس ما داشته باشد تعریف کرده ایم. اکنون باید خود دیتابیس را تعریف کنیم. ما قصد داریم از SQLite برای این راهنما استفاده کنیم، اما شما می توانید از هر دیتابیسی که دوست دارید، استفاده کنید. اگر در محیط production هستید، باید از یک دیتابیس غیرفایلی (non-file) مانند MySQL استفاده کنید.

یک فایل دیتابیس در database/database.sqlite ایجاد کنید. سپس فایل env خود را باز کنید و خطوط زیر را جایگزین کنید:

DB_CONNECTION=mysql

DB_DATABASE=homestead

DB_USERNAME=username

DB_PASSWORD=password

با:

DB_CONNECTION=sqlite

DB_DATABASE=/full/path/to/database/database.sqlite

این کدها برای راه اندازی دیتابیس ما است. مهاجرت ها را اجرا کنید تا جدول ها دیتابیس را برای برنامه ما ایجاد کرده و آن را بسازند:

php artisan migrate --seed

تعریف و ایمن سازی endpoint ها

فروشگاه اینترنتی ما از Laravel و Vue برای ایجاد بهترین برنامه استفاده می کند. یعنی ما باید API هایی را برای ارائه کامپونت های Vue خود با داده تعریف کنیم.

لاراول به طور پیش فرض از مسیرهای وب و API پشتیبانی می کند. مسیرهای وب مسیریابی را برای صفحات ایجاد شده به صورت پویا که از یک مرورگر وب قابل دسترسی هستند، مدیریت می‌کنند، در حالی که مسیرهای API درخواست‌های مشتریانی را که معمولا به پاسخ در قالب JSON یا XML نیاز دارند، رسیدگی می‌کنند.

برنامه ما برای اکثر درخواست ها دارای API خواهد بود. ما باید API های خود را ایمن کنیم تا مطمئن شویم فقط کاربران مجاز به آن دسترسی خواهند داشت. برای این کار از لاراول passport استفاده می کنیم.

نصب passport در لاراول برای API

برای نصب passport دستور زیر را اجرا کنید:

composer require laravel/passport

Laravel Passport با مهاجرت های خود همراه است، مهاجرت ها را با استفاده از دستور زیر اجرا کنید:

php artisan migrate

سپس، دستور نصب passport را اجرا کنید تا کلیدهای لازم برای ایمن سازی برنامه شما ایجاد شود:

php artisan passport:install

دستور بالا کلیدهای رمزگذاری مورد نیاز برای تولید توکن های دسترسی ایمن به اضافه کلاینت های " personal access" و " password grant" را ایجاد می کند که برای تولید access token ها استفاده می شود.

پس از نصب، باید از ویژگی Laravel Passport HasApiToken در مدل User استفاده کنید. این ویژگی چند متد کمکی برای مدل شما ارائه می دهد که به شما امکان می دهد توکن و مجوزهای کاربر تایید شده را بررسی کنید.

فایل app/User.php را باز کرده و به صورت زیر ویرایش کنید:

<?php




    namespace App;




    [...]




   use Laravel\Passport\HasApiTokens;

    use Illuminate\Database\Eloquent\SoftDeletes;




class User extends Authenticatable

{

use Notifiable, SoftDeletes, HasApiTokens;




[...]

    }

در مرحله بعد، متد Passport::routes را در متد boot در AuthServiceProvider را فراخوانی کنید. این متد مسیرهای لازم برای ایجاد توکن های مورد نیاز برنامه شما را ثبت می کند:

آپدیت فایل app/Providers/AuthServiceProvider.php به صورت زیر است:



<?php




    [...]




    use Laravel\Passport\Passport;




    class AuthServiceProvider extends ServiceProvider

    {

        [...]




        public function boot()

        {

            $this->registerPolicies();




            Passport::routes();

        }




        [...]

   }

در نهایت، در فایل پیکربندی config/auth.php، باید گزینه driver محافظ api authentication را با passport مقداردهی کنید.

[...]




    'guards' => [




        [...]




       'api' => [

            'driver' => 'passport',

            'provider' => 'users',

        ],

    ],




    [...]

همه این کدها برای passport لاراول است. 

ساخت کنترلرهای لاراولی

در بالا، مدل های خود و کنترلرهای همراه آنها را تعریف کردیم. این کنترلرها در دایرکتوری app/Http/Controllers قرار دارند. مدل User، یک کنترلر ندارد، بنابراین ابتدا می خواهیم آن را ایجاد کنیم. برای این کار را دستور زیر را اجرا کنید:

php artisan make:controller UserController

اکنون فایل کنترلر ایجاد شده app/Http/Controllers/UserController.php را باز کنید و محتویات آن را با موارد زیر جایگزین کنید:

<?php




namespace App\Http\Controllers;




use Auth;

use App\User;

use Validator;

use Illuminate\Http\Request;




class UserController extends Controller

{

public function index()

{

return response()->json(User::with(['orders'])->get());

}




public function login(Request $request)

{

$status = 401;

$response = ['error' => 'Unauthorised'];




if (Auth::attempt($request->only(['email', 'password']))) {

$status = 200;

$response = [

'user' => Auth::user(),

'token' => Auth::user()->createToken('bigStore')->accessToken,

];

}




return response()->json($response, $status);

}




public function register(Request $request)

{

$validator = Validator::make($request->all(), [

'name' => 'required|max:50',

'email' => 'required|email',

'password' => 'required|min:6',

'c_password' => 'required|same:password',

]);




if ($validator->fails()) {

return response()->json(['error' => $validator->errors()], 401);

}




$data = $request->only(['name', 'email', 'password']);

$data['password'] = bcrypt($data['password']);




$user = User::create($data);

$user->is_admin = 0;




return response()->json([

'user' => $user,

'token' => $user->createToken('bigStore')->accessToken,

]);

}




public function show(User $user)

{

return response()->json($user);

}




public function showOrders(User $user)

{

return response()->json($user->orders()->with(['product'])->get());

}




}

در بالا چند متد برای کلاس تعریف کردیم:

  • ()index - همه کاربران را با سفارشاتشان برمی گرداند.
  • ()login - یک کاربر را احراز هویت می کند و یک access token برای آن کاربر ایجاد می کند. متد createToken یکی از روش هایی است که Laravel Passport به مدل کاربری ما اضافه می کند.
  • ()register - یک حساب کاربری ایجاد می کند، آن را احراز هویت می کند و یک access token برای آن تولید می کند.
  • ()show - جزئیات یک کاربر را دریافت کرده و آنها را برمی گرداند.
  • ()showOrders - تمام سفارش های یک کاربر را دریافت کرده و آنها را برمی گرداند.
  • ما از (binding) اتصال Route-Model لاراول برای تزریق خودکار نمونه مدل خود به کنترلر استفاده کردیم. نکته مهم این است که نام متغیر مورد استفاده برای اتصال باید همان نامی باشد که در مسیر تعریف شده است.

سپس فایل app/Http/Controllers/ProductController.php را باز کرده و محتویات آن را با موارد زیر جایگزین کنید:

<?php




namespace App\Http\Controllers;




use App\Product;

use Illuminate\Http\Request;




class ProductController extends Controller

{

public function index()

{

return response()->json(Product::all(),200);

}




public function store(Request $request)

{

$product = Product::create([

'name' => $request->name,

'description' => $request->description,

'units' => $request->units,

'price' => $request->price,

'image' => $request->image

]);




return response()->json([

'status' => (bool) $product,

'data'   => $product,

'message' => $product ? 'Product Created!' : 'Error Creating Product'

]);

}




public function show(Product $product)

{

return response()->json($product,200);

}




public function uploadFile(Request $request)

{

if($request->hasFile('image')){

$name = time()."_".$request->file('image')->getClientOriginalName();

$request->file('image')->move(public_path('images'), $name);

}

return response()->json(asset("images/$name"),201);

}




public function update(Request $request, Product $product)

{

$status = $product->update(

$request->only(['name', 'description', 'units', 'price', 'image'])

);




return response()->json([

'status' => $status,

'message' => $status ? 'Product Updated!' : 'Error Updating Product'

]);

}




public function updateUnits(Request $request, Product $product)

{

$product->units = $product->units + $request->get('units');

$status = $product->save();




return response()->json([

'status' => $status,

'message' => $status ? 'Units Added!' : 'Error Adding Product Units'

]);

}




public function destroy(Product $product)

{

$status = $product->delete();




return response()->json([

'status' => $status,

'message' => $status ? 'Product Deleted!' : 'Error Deleting Product'

]);

}

}

در ProductController بالا ما هفت متد را تعریف کردیم:

  • ()index - تمام رکوردهای محصول را واکشی و برمی گرداند.
  • ()store - یک رکورد برای محصول ایجاد می کند.
  • ()show - یک محصول خاص را واکشی و برمی گرداند.
  • ()uploadFile - تصویر محصولی را که ما ایجاد کرده ایم آپلود می کند و url محصول را برمی گرداند.
  • ()update - رکورد محصول را ویرایش می کند.
  • ()updateUnits - یونیت های جدیدی را به محصول اضافه می کند.
  • ()delete - یک محصول را حذف می کند.

سپس فایل app/Http/Controllers/OrderController.php را باز کنید و محتوای زیر را جایگزین کنید:

<?php




namespace App\Http\Controllers;




use App\Order;

use Auth;

use Illuminate\Http\Request;




class OrderController extends Controller

{

public function index()

{

return response()->json(Order::with(['product'])->get(),200);

}




public function deliverOrder(Order $order)

{

$order->is_delivered = true;

$status = $order->save();




return response()->json([

'status'    => $status,

'data'      => $order,

'message'   => $status ? 'Order Delivered!' : 'Error Delivering Order'

]);

}




public function store(Request $request)

{

$order = Order::create([

'product_id' => $request->product_id,

'user_id' => Auth::id(),

'quantity' => $request->quantity,

'address' => $request->address

]);




return response()->json([

'status' => (bool) $order,

'data'   => $order,

'message' => $order ? 'Order Created!' : 'Error Creating Order'

]);

}




public function show(Order $order)

{

return response()->json($order,200);

}




public function update(Request $request, Order $order)

{

$status = $order->update(

$request->only(['quantity'])

);




return response()->json([

'status' => $status,

'message' => $status ? 'Order Updated!' : 'Error Updating Order'

]);

}




public function destroy(Order $order)

{

$status = $order->delete();




return response()->json([

'status' => $status,

'message' => $status ? 'Order Deleted!' : 'Error Deleting Order'

]);

}

}

در OrderController بالا ما شش متد داریم:

  • ()index - تمام سفارشات را واکشی و برمی گرداند.
  • ()deliverOrder - یک سفارش را به عنوان تحویل علامت گذاری می کند.
  • ()store - یک سفارش ایجاد می کند.
  • ()show - یک سفارش ویژه را واکشی و برمی گرداند.
  • ()update - سفارش را به روز می کند.
  • ()destruct - یک سفارش را حذف می کند.

این متدها برای کنترلرهای ما است. ما کنترلر را با توجه به مشخصاتی که در قسمت اول ارائه کردیم ایجاد کرده ایم. کار بعدی که باید انجام دهیم این است که مسیرهای API خود را تعریف کنیم.

تعیین مسیرهای (route های) برنامه

اکنون که همه درخواست‌هایی را که می‌خواستیم، به‌طور کامل تعریف کردیم، اجازه دهید  APIهای ایجاد این درخواست‌ها را ایجاد کنیم. فایل routes/api.php را باز کنید و محتوای زیر را جایگزین کنید:

<?php




use Illuminate\Http\Request;




Route::post('login', 'UserController@login');

Route::post('register', 'UserController@register');

Route::get('/products', 'ProductController@index');

Route::post('/upload-file', 'ProductController@uploadFile');

Route::get('/products/{product}', 'ProductController@show');




Route::group(['middleware' => 'auth:api'], function(){

Route::get('/users','UserController@index');

Route::get('users/{user}','UserController@show');

Route::patch('users/{user}','UserController@update');

Route::get('users/{user}/orders','UserController@showOrders');

Route::patch('products/{product}/units/add','ProductController@updateUnits');

Route::patch('orders/{order}/deliver','OrderController@deliverOrder');

Route::resource('/orders', 'OrderController');

Route::resource('/products', 'ProductController')->except(['index','show']);

});

قرار دادن تعریف مسیرها در فایل routes/api.php به لاراول می گوید که آنها مسیرهای API هستند، بنابراین لاراول مسیرها را با یک پیشوند api/ در URL ایجاد می کند تا آنها را از مسیرهای وب متمایز کند.

افزودن میان‌افزار auth:api تضمین می‌کند که هر فراخوانی مسیرهای آن گروه باید احراز هویت شود.

نکته ای که باید به آن توجه داشت این است که استفاده از متد resource در کلاس Route به ما کمک می کند تا مسیرهای اضافی را بدون نیاز به ایجاد آنها ایجاد کنیم. در مورد کنترلرهای resource و routes اینجا را بخوانید.

برای مشاهده لیست کامل routes، دستور زیر را اجرا کنید:

php artisan route:list

لیست مسیرهای فروشگاه ساخته شده با لاراول

از آنجایی که قسمت فرانت اند این اپلیکیشن را با Vue خواهیم ساخت، باید مسیرهای وب را برای آن تعریف کنیم. فایل routes/web.php را باز کنید و محتویات آن را با موارد زیر جایگزین کنید:

<?php

Route::get('/{any}', function(){

return view('landing');

})->where('any', '.*');

این کد هر درخواست وب را به یک api هدایت می کند، که ورودی برنامه Vue شما خواهد بود.

راه اندازی Vue برای فروشگاه اینترنتی

Vue یک فریم ورک پیشرفته برای ساخت رابط های کاربری است. برخلاف سایر فریم ورک‌های یکپارچه، Vue از ابتدا به گونه‌ای طراحی شده است که به صورت تدریجی قابل استفاده و پذیرش در برنامه باشد / vuejs.org

لاراول با Vue سازگار است، بنابراین تنها کاری که برای دریافت Vue باید انجام دهیم نصب پکیج های noed است. دستور زیر را اجرا کنید:

npm install

در مرحله بعد، ما به VueRouter نیاز داریم تا مسیریابی بین اجزای مختلف برنامه Vue را مدیریت کند. برای نصب VueRouter دستور زیر را اجرا کنید:

npm install vue-router

در مرحله بعد، بیایید فایل ویو لندینگ را بسازیم که برنامه Vue ما را نصب می کند. فایل source/views/landing.blade.php را ایجاد کنید و کد زیر را به آن اضافه کنید:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="csrf-token" content="{{csrf_token()}}">

<title>Big Store</title>

<link href=" {{ mix('css/app.css') }}" rel="stylesheet">

</head>

<body>

<div id="app">

<app></app>

</div>

<script src="{{ mix('js/bootstrap.js') }}"></script>

<script src="{{ mix('js/app.js') }}"></script>

</body>

</html>

در کد بالا، HTML برنامه خود را داریم. اگر دقت کنید، می توانید تگ برنامه را ببینید. این نقطه ورود به برنامه Vue ما و جایی است که کامپوننت ها دانلود می شوند.

از آنجایی که ما از app.js برای راه اندازی VueRouter خود استفاده خواهیم کرد، هنوز باید Bootstrap و Axios را کامپایل کنیم. وارد (import) کردن Bootstrap و Axios در فایل bootstrap.js است، بنابراین باید آن را کامپایل کنیم.

فایل webpack.mix.js را تغییر دهید تا همه asset ها را کامپایل کند:

[...]




mix.js('resources/assets/js/app.js', 'public/js')

.js('resources/assets/js/bootstrap.js', 'public/js')

.sass('resources/assets/sass/app.scss', 'public/css');

فایل webpack.mix.js فایل‌های پیکربندی laravel-mix را در خود نگه می‌دارد رپری (wrapper) در اطراف Webpack ایجاد می‌کند. به ما امکان می دهد از توانایی های شگفت انگیز گردآوری asset وب پک بدون نیاز به نوشتن تنظیمات Webpack توسط خودمان استفاده کنیم. در اینجا می توانید درباره Webpack اطلاعات بیشتری کسب کنید.

صفحه اصلی برنامه Vue را آماده کنید. یک فایل جدید، source/assets/js/views/Home.vue ایجاد کنید و کد زیر را به فایل اضافه کنید:

<template>

<div>

<div class="container-fluid hero-section d-flex align-content-center justify-content-center flex-wrap ml-auto">

<h2 class="title">Welcome to the bigStore</h2>

</div>

<div class="container">

<div class="row">

<div class="col-md-12">

<div class="row">

<div class="col-md-4 product-box" v-for="(product,index) in products" @key="index">

<router-link :to="{ path: '/products/'+product.id}">

<img :src="product.image" :alt="product.name">

<h5><span v-html="product.name"></span>

<span class="small-text text-muted float-right">$ {{product.price}}</span>

</h5>

<button class="col-md-4 btn btn-sm btn-primary float-right">Buy Now</button>

</router-link>

</div>

</div>

</div>

</div>

</div>

</div>

</template>




<script>

export default {

data(){

return {

products : []

}

},

mounted(){

axios.get("api/products/").then(response => this.products = response.data)

}

}

</script>

در بالا تگ باز و بسته تمپلیت HTML خود، کد کامپوننت Vue خود را داریم. در آنجا محتویات محصولات را رندر می کنیم و برای هر محصول تصویر، نام، آیدی، قیمت و یونیت های موجود را نمایش می دهیم. ما از ویژگی v-html برای رندر HTML خام استفاده می کنیم که استفاده از کاراکترهای خاص در نام محصول را برای ما آسان می کند.

در تگ اسکریپت، data() را تعریف کردیم و تمام متغیرهایی را که می توانیم در تمپلیت خود استفاده کنیم را در خود جای می دهد. همچنین متد ()mounted را تعریف کردیم که پس از بارگذاری کامپوننت فراخوانی می شود. در این متد mount شده، ما محصولات خود را از API بارگذاری می کنیم و سپس متغیر products را طوری مقداردهی می کنیم که تمپلیت ما با داده های API به روز شود.

در همین فایل کد زیر را به اضافه کنید:

<style scoped>

.small-text {

font-size: 14px;

}

.product-box {

border: 1px solid #cccccc;

padding: 10px 15px;

}

.hero-section {

height: 30vh;

background: #ababab;

align-items: center;

margin-bottom: 20px;

margin-top: -20px;

}

.title {

font-size: 60px;

color: #ffffff;

}

</style>

در کد بالا، استایلی را برای استفاده در کامپوننت Welcome تعریف کرده ایم.

طبق مستندات Vue داریم:

هنگامی که یک تگ <style> دارای ویژگی scoped باشد، CSS آن فقط برای المنت های کامپوننت فعلی اعمال می شود. این شبیه به کپسوله‌سازی استایل موجود در Shadow DOM است. با برخی اخطارها همراه است اما نیازی به pollyfill ها ندارد.

سپس یک فایل دیگر، به نام resources/assets/js/views/App.vue ایجاد کنید. این فایل کانتینر برنامه خواهد بود که در آن همه کامپوننت های دیگر بارگذاری می شوند. در این فایل کد زیر را اضافه کنید:

<template>

<div>

<nav class="navbar navbar-expand-md navbar-light navbar-laravel">

<div class="container">

<router-link :to="{name: 'home'}" class="navbar-brand">Big Store</router-link>

<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">

<span class="navbar-toggler-icon"></span>

</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">

<!-- Left Side Of Navbar -->

<ul class="navbar-nav mr-auto"></ul>

<!-- Right Side Of Navbar -->

<ul class="navbar-nav ml-auto">

<router-link :to="{ name: 'login' }" class="nav-link" v-if="!isLoggedIn">Login</router-link>

<router-link :to="{ name: 'register' }" class="nav-link" v-if="!isLoggedIn">Register</router-link>

<span v-if="isLoggedIn">

<router-link :to="{ name: 'userboard' }" class="nav-link" v-if="user_type == 0"> Hi, {{name}}</router-link>

<router-link :to="{ name: 'admin' }" class="nav-link" v-if="user_type == 1"> Hi, {{name}}</router-link>

</span>

<li class="nav-link" v-if="isLoggedIn" @click="logout"> Logout</li>

</ul>

</div>

</div>

</nav>

<main class="py-4">

<router-view @loggedIn="change"></router-view>

</main>

</div>

</template>

در تمپلیت Vue در بالا، از برخی تگ‌های خاص Vue مانند router-link استفاده کردیم که به ما کمک می‌کند لینک‌هایی را برای مسیریابی صفحات تعریف‌شده در روتر خود ایجاد کنیم. ما همچنین router-view را داریم، جایی که تمام صفحات کامپوننت فرزند بارگذاری می شوند.

در ادامه، زیر تگ تمپلیت بسته شده، کد زیر را اضافه کنید:

<script>

export default {

data() {

return {

name: null,

user_type: 0,

isLoggedIn: localStorage.getItem('bigStore.jwt') != null

}

},

mounted() {

this.setDefaults()

},

methods : {

setDefaults() {

if (this.isLoggedIn) {

let user = JSON.parse(localStorage.getItem('bigStore.user'))

this.name = user.name

this.user_type = user.is_admin

}

},

change() {

this.isLoggedIn = localStorage.getItem('bigStore.jwt') != null

this.setDefaults()

},

logout(){

localStorage.removeItem('bigStore.jwt')

localStorage.removeItem('bigStore.user')

this.change()

this.$router.push('/')

}

}

}

</script>

در تعریف اسکریپت، ویژگی­های متدها را داریم و در آنجا سه متد تعریف شده داریم:

  • ()setDefaults - نام کاربر را هنگام ورود کاربر و همچنین نوع کاربری که وارد شده است را تعیین می کند.
  • ()change - وضعیت ورود فعلی را هر زمان که فراخوانی شود بررسی می کند و متد setDefaults را فراخوانی می کند.
  • ()logout - کاربر را از برنامه خارج می کند و کاربر را به صفحه اصلی هدایت می کند.

در کامپوننت router-view خود، ما رویداد loggedIn را بررسی میکنیم که متد change را فراخوانی می کند. این رویداد هر زمان که وارد سیستم می‌شویم توسط کامپوننت ما فعال می‌شود. این راهی است که به کامپوننت برنامه می‌گوییم وقتی کاربر وارد سیستم می‌شود، خودش را به‌روزرسانی کند.

سپس فایل های زیر را در دایرکتوری resources/assets/js/views ایجاد کنید:

  • Admin.vue
  • Checkout.vue
  • Confirmation.vue
  • Login.vue
  • Register.vue
  • SingleProduct.vue
  • UserBoard.vue

این فایل‌ها همه صفحاتی را که bigStore خواهند داشت، در خود جای می‌دهند. آنها باید قبل از راه اندازی VueRouter ایجاد شوند تا خطایی ایجاد نکند. برای راه‌اندازی مسیریابی برای برنامه تک صفحه‌ای Vue، فایل source/assets/js/app.js خود را باز کنید و محتوای آن را با کد زیر جایگزین کنید:

import Vue from 'vue'

import VueRouter from 'vue-router'




Vue.use(VueRouter)




import App from './views/App'

import Home from './views/Home'

import Login from './views/Login'

import Register from './views/Register'

import SingleProduct from './views/SingleProduct'

import Checkout from './views/Checkout'

import Confirmation from './views/Confirmation'

import UserBoard from './views/UserBoard'

import Admin from './views/Admin'




const router = new VueRouter({

mode: 'history',

routes: [

{

path: '/',

name: 'home',

component: Home

},

{

path: '/login',

name: 'login',

component: Login

},

{

path: '/register',

name: 'register',

component: Register

},

{

path: '/products/:id',

name: 'single-products',

component: SingleProduct

},

{

path: '/confirmation',

name: 'confirmation',

component: Confirmation

},

{

path: '/checkout',

name: 'checkout',

component: Checkout,

props: (route) => ({ pid: route.query.pid })

},

{

path: '/dashboard',

name: 'userboard',

component: UserBoard,

meta: {

requiresAuth: true,

is_user: true

}

},

{

path: '/admin/:page',

name: 'admin-pages',

component: Admin,

meta: {

requiresAuth: true,

is_admin: true

}

},

{

path: '/admin',

name: 'admin',

component: Admin,

meta: {

requiresAuth: true,

is_admin: true

}

},

],

})




router.beforeEach((to, from, next) => {

if (to.matched.some(record => record.meta.requiresAuth)) {

if (localStorage.getItem('bigStore.jwt') == null) {

next({

path: '/login',

params: { nextUrl: to.fullPath }

})

} else {

let user = JSON.parse(localStorage.getItem('bigStore.user'))

if (to.matched.some(record => record.meta.is_admin)) {

if (user.is_admin == 1) {

next()

}

else {

next({ name: 'userboard' })

}

}

else if (to.matched.some(record => record.meta.is_user)) {

if (user.is_admin == 0) {

next()

}

else {

next({ name: 'admin' })

}

}

next()

}

} else {

next()

}

})

در بالا، ما VueRouter را import کرده ایم و آن را به برنامه Vue خود اضافه کرده ایم. ما مسیرهایی را برای برنامه خود تعریف کردیم و سپس آن را در نمونه Vue ثبت کردیم تا برای همه اجزای Vue در دسترس باشد.

هر یک از اشیا route دارای یک نام است که از آن برای شناسایی و فراخوانی آن مسیر استفاده می کنیم. همچنین دارای یک مسیر است که می توانید به طور مستقیم در مرورگر خود از آن بازدید کنید. در نهایت یک کامپوننت دارد که هنگام بازدید از مسیر نصب می شود.

در برخی از مسیرها، meta را تعریف کردیم که شامل متغیرهایی است که می‌خواهیم هنگام دسترسی به مسیر بررسی کنیم. در این مورد، ما در حال بررسی هستیم که آیا مسیر نیاز به احراز هویت دارد یا خیر و آیا فقط به ادمین ها یا کاربران عادی محدود می شود یا نه.

ما میان افزار (middleware) beforeEach را برای روتر استفاده کرده ایم که هر مسیر را قبل از کار کردن بررسی می کند. متد این متغیرها را می گیرد:

  • to - مسیری که می خواهید به آن بروید.
  • from – مسیر فعلی که از آن تغییر مسیر می دهید.
  • next - متدی که در نهایت به یک مسیر تعریف شده حرکت می کند. هنگامی که بدون رد کردن مسیر فراخوانی می شود، حرکت را ادامه می دهد. اگر مسیری داده شود به آن مسیر می رود.

ما از beforeEach برای بررسی مسیرهایی که نیاز به احراز هویت دارند قبل از اینکه بتوانید به آنها دسترسی داشته باشید استفاده می کنیم. برای آن مسیرها، بررسی می کنیم که آیا کاربر احراز هویت شده است یا خیر. اگر کاربر نیست، او را به صفحه ورود می فرستیم. اگر کاربر احراز هویت شده باشد، بررسی می کنیم که آیا مسیر به کاربران ادمین یا کاربران عادی محدود شده است. ما هر کاربر را بر اساس سطح دسترسی آنها به مکان مناسب هدایت می کنیم.

حالا خطوط زیر را به انتهای فایل app.js اضافه کنید:

const app = new Vue({

el: '#app',

components: { App },

router,

});

این فایل باعث می شود که یک راه اندازی برای Vue انجام شود. در این نمونه global، ما کامپوننت App را فقط به این دلیل نصب می‌کنیم که VueRouter برای جابجایی بین سایر کامپوننت‌ها به آن نیاز دارد.

اکنون، ما آماده هستیم تا سایر ویوها را برای برنامه خود ایجاد کنیم.

ساخت صفحات authentication (احراز هویت)

در قسمت قبل، تمام فایل های view را برای برنامه Vue خود ایجاد کردیم، البته به همه آنها محتوا اضافه نکردیم. بیایید با صفحات login و register شروع کنیم.

فایل source/assets/js/views/Login.vue را باز کرده و در کد زیر قرار دهید:

<template>

        <div class="container">

            <div class="row justify-content-center">

                <div class="col-md-8">

                    <div class="card card-default">

                        <div class="card-header">Login</div>

                        <div class="card-body">

                            <form>

                                <div class="form-group row">

                                    <label for="email" class="col-sm-4 col-form-label text-md-right">E-Mail Address</label>

                                    <div class="col-md-6">

                                        <input id="email" type="email" class="form-control" v-model="email" required autofocus>

                                    </div>

                                </div>

                                <div class="form-group row">

                                    <label for="password" class="col-md-4 col-form-label text-md-right">Password</label>

                                    <div class="col-md-6">

                                        <input id="password" type="password" class="form-control" v-model="password" required>

                                    </div>

                                </div>

                                <div class="form-group row mb-0">

                                    <div class="col-md-8 offset-md-4">

                                        <button type="submit" class="btn btn-primary" @click="handleSubmit">

                                            Login

                                        </button>

                                    </div>

                                </div>

                            </form>

                        </div>

                    </div>

                </div>

            </div>

        </div>

    </template>

در بالا یک فرم داریم که هنوز عملکرد زیادی ندارد، بنابراین بیایید کد زیر را به همان فایل اضافه کنیم تا مقداری اسکریپت Vue اضافه کنیم:

<script>

        export default {

            data() {

                return {

                    email: "",

                    password: ""

                }

            },

            methods: {

                handleSubmit(e) {

                    e.preventDefault()

                    if (this.password.length > 0) {

                        let email = this.email

                        let password = this.password




                        axios.post('api/login', {email, password}).then(response => {

                            let user = response.data.user

                            let is_admin = user.is_admin




                            localStorage.setItem('bigStore.user', JSON.stringify(user))

                            localStorage.setItem('bigStore.jwt', response.data.token)




                            if (localStorage.getItem('bigStore.jwt') != null) {

                                this.$emit('loggedIn')

                                if (this.$route.params.nextUrl != null) {

                                    this.$router.push(this.$route.params.nextUrl)

                                } else {

                                    this.$router.push((is_admin == 1 ? 'admin' : 'dashboard'))

                                }

                            }

                        });

                    }

                }

            }

        }

    </script>

در بالا یک متد handleSubmit داریم که هنگام ارسال فرم فعال می شود. در این متد سعی می کنیم با استفاده از API احراز هویت کنیم. اگر احراز هویت موفقیت آمیز باشد، access token و داده‌های کاربر را در localStorage ذخیره می‌کنیم تا بتوانیم به آنها در سراسر برنامه خود دسترسی داشته باشیم.

ما همچنین یک رویداد loggedIn را منتشر می کنیم تا کامپوننت پدر نیز بتواند به روز شود. در آخر بررسی می کنیم که آیا کاربر از صفحه دیگری به صفحه ورود ارسال شده است یا خیر، سپس کاربر را به آن صفحه می فرستیم. اگر کاربر به طور مستقیم وارد سیستم شده باشد، نوع کاربر را بررسی کرده و کاربر را به درستی هدایت می کنیم.

در مرحله بعد، فایل source/assets/js/views/Register.vue را باز کرده و در قسمت زیر قرار دهید:

<template>

        <div class="container">

            <div class="row justify-content-center">

                <div class="col-md-8">

                    <div class="card card-default">

                        <div class="card-header">Register</div>

                        <div class="card-body">

                            <form>

                                <div class="form-group row">

                                    <label for="name" class="col-md-4 col-form-label text-md-right">Name</label>

                                    <div class="col-md-6">

                                        <input id="name" type="text" class="form-control" v-model="name" required autofocus>

                                    </div>

                                </div>

                                <div class="form-group row">

                                    <label for="email" class="col-md-4 col-form-label text-md-right">E-Mail Address</label>

                                    <div class="col-md-6">

                                        <input id="email" type="email" class="form-control" v-model="email" required>

                                    </div>

                                </div>

                                <div class="form-group row">

                                    <label for="password" class="col-md-4 col-form-label text-md-right">Password</label>

                                    <div class="col-md-6">

                                        <input id="password" type="password" class="form-control" v-model="password" required>

                                    </div>

                                </div>

                                <div class="form-group row">

                                    <label for="password-confirm" class="col-md-4 col-form-label text-md-right">Confirm Password</label>

                                    <div class="col-md-6">

                                        <input id="password-confirm" type="password" class="form-control" v-model="password_confirmation" required>

                                    </div>

                                </div>

                                <div class="form-group row mb-0">

                                    <div class="col-md-6 offset-md-4">

                                        <button type="submit" class="btn btn-primary" @click="handleSubmit">

                                            Register

                                        </button>

                                    </div>

                                </div>

                            </form>

                        </div>

                    </div>

                </div>

            </div>

       </div>

    </template>

در بالا HTML فرم ثبت نام را داریم. بیایید اسکریپت کامپوننت را در زیر تگ بسته تمپلیت اضافه کنیم:

<script>

    export default {

        props : ['nextUrl'],

        data(){

            return {

                name : "",

                email : "",

                password : "",

                password_confirmation : ""

            }

        },

        methods : {

            handleSubmit(e) {

                e.preventDefault()

                if (this.password !== this.password_confirmation || this.password.length <= 0) {

                    this.password = ""

                    this.password_confirmation = ""

                    return alert('Passwords do not match')

                }

                let name = this.name

                let email = this.email

                let password = this.password

                let c_password = this.password_confirmation

                axios.post('api/register', {name, email, password, c_password}).then(response => {

                    let data = response.data

                    localStorage.setItem('bigStore.user', JSON.stringify(data.user))

                    localStorage.setItem('bigStore.jwt', data.token)

                    if (localStorage.getItem('bigStore.jwt') != null) {

                        this.$emit('loggedIn')

                        let nextUrl = this.$route.params.nextUrl

                        this.$router.push((nextUrl != null ? nextUrl : '/'))

                    }

                })

            }

        }

    }

    </script>

کامپوننت register به طور مشابه با کامپوننت login عمل می کند. ما داده‌های کاربر را برای احراز هویت به API ارسال می‌کنیم و در صورت دریافت پاسخ مطلوب، token و user کاربر را در localStorage ذخیره می‌کنیم.

ساخت صفحات خرید و فروش با Vue

ما قبلا صفحه اصلی را در فصل آخر تعریف کرده بودیم و لیستی از محصولات موجود را برگردانده بودیم. اکنون، ما می‌خواهیم تمام صفحات فروشگاه دیگر را بسازیم.

فایل source/assets/js/views/SingleProduct.vue را باز کرده و در کد زیر قرار دهید:

<template>

        <div class="container">

            <div class="row">

                <div class="col-md-8 offset-md-2">

                    <img :src="product.image" :alt="product.name">

                    <h3 class="title" v-html="product.name"></h3>

                    <p class="text-muted">{{product.description}}</p>

                    <h4>

                        <span class="small-text text-muted float-left">$ {{product.price}}</span>

                        <span class="small-text float-right">Available Quantity: {{product.units}}</span>

                    </h4>

                    <br>

                    <hr>

                    <router-link :to="{ path: '/checkout?pid='+product.id }" class="col-md-4 btn btn-sm btn-primary float-right">Buy Now</router-link>

                </div>

            </div>

        </div>

    </template>




    <script>

    export default {

        data(){

            return {

                product : []

            }

        },

        beforeMount(){

            let url = `/api/products/${this.$route.params.id}`

            axios.get(url).then(response => this.product = response.data)     

        }

    }

    </script>




    <style scoped>

    .small-text { font-size: 18px; }

    .title { font-size: 36px; }

    </style>

در بالا محصول را به عنوان یک attribute داده داریم که از آن برای نمایش اطلاعات در صفحه مانند فایل Home.vue استفاده می کنیم. در اسکریپت اجزای متد Vue's beforeMount را تعریف کردیم و اطلاعات محصول را در آنجا واکشی کردیم. beforeMount قبل از رندر شدن کامپوننت فراخوانی می شود، بنابراین داده های لازم را برای رندر کردن کامپوننت واکشی می کند. اگر پس از نصب کامپوننت، داده ها را دریافت کنیم، قبل از به روز رسانی کامپوننت با خطا مواجه خواهیم شد.

سپس فایل source/assets/js/views/Checkout.vue را باز کرده و کد زیر را برای تمپلیت و استایل HTML قرار دهید:

<template>

        <div class="container">

            <div class="row">

                <div class="col-md-8 offset-md-2">

                    <div class="order-box">

                        <img :src="product.image" :alt="product.name">

                        <h2 class="title" v-html="product.name"></h2>

                        <p class="small-text text-muted float-left">$ {{product.price}}</p>

                        <p class="small-text text-muted float-right">Available Units: {{product.units}}</p>

                        <br>

                        <hr>

                        <label class="row"><span class="col-md-2 float-left">Quantity: </span><input type="number" name="units" min="1" :max="product.units" class="col-md-2 float-left" v-model="quantity" @change="checkUnits"></label>

                    </div>

                    <br>

                    <div>

                        <div v-if="!isLoggedIn">

                            <h2>You need to login to continue</h2>

                            <button class="col-md-4 btn btn-primary float-left" @click="login">Login</button>

                            <button class="col-md-4 btn btn-danger float-right" @click="register">Create an account</button>

                        </div>

                        <div v-if="isLoggedIn">

                            <div class="row">

                                <label for="address" class="col-md-3 col-form-label">Delivery Address</label>

                                <div class="col-md-9">

                                    <input id="address" type="text" class="form-control" v-model="address" required>

                                </div>

                            </div>

                            <br>

                            <button class="col-md-4 btn btn-sm btn-success float-right" v-if="isLoggedIn" @click="placeOrder">Continue</button>

                        </div>

                    </div>

                </div>

            </div>

        </div>

    </template>




    <style scoped>

    .small-text { font-size: 18px; }

    .order-box { border: 1px solid #cccccc; padding: 10px 15px; }

    .title { font-size: 36px; }

    </style>

در زیر آن موارد زیر را در اسکریپت پیست کنید:

<script>

    export default {

        props : ['pid'],

        data(){

            return {

                address : "",

                quantity : 1,

                isLoggedIn : null,

                product : []

            }

        },

        mounted() {

            this.isLoggedIn = localStorage.getItem('bigStore.jwt') != null

        },

        beforeMount() {

            axios.get(`/api/products/${this.pid}`).then(response => this.product = response.data)




            if (localStorage.getItem('bigStore.jwt') != null) {

                this.user = JSON.parse(localStorage.getItem('bigStore.user'))

                axios.defaults.headers.common['Content-Type'] = 'application/json'

                axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('bigStore.jwt')

            }

        },

        methods : {

            login() {

                this.$router.push({name: 'login', params: {nextUrl: this.$route.fullPath}})

            },

            register() {

                this.$router.push({name: 'register', params: {nextUrl: this.$route.fullPath}})

            },

            placeOrder(e) {

                e.preventDefault()




                let address = this.address

                let product_id = this.product.id

                let quantity = this.quantity




                axios.post('api/orders/', {address, quantity, product_id})

                     .then(response => this.$router.push('/confirmation'))

            },

            checkUnits(e){

                if (this.quantity > this.product.units) {

                    this.quantity = this.product.units

                }

            }

        }

    }

    </script>

در بالا متد beforeMount را تعریف کردیم که در آن اطلاعات محصول را واکشی می کنیم. سپس ما متد mounted را داریم که در آن وضعیت احراز هویت را بررسی می کنیم. در پراپرتی متد ‌ها، متد checkUnits را تعریف کردیم که بررسی می‌کند کاربر می‌خواهد چند سفارش داشته باشد و سپس متد placeOrder را تعریف می‌کنیم که سفارش را انجام می‌دهد. سپس فایل source/assets/js/views/Confirmation.vue را باز کرده و کد زیر را در آن قرار دهید:

<template>

        <div>

            <div class="container-fluid hero-section d-flex align-content-center justify-content-center flex-wrap ml-auto">

                <h2>

                    <span class="title"><strong>Thank You!</strong></span><br>

                    <span class="medium-text">Your order has been placed.</span><br>

                    <router-link :to="{name: 'userboard'}" class="small-link">

                        See your orders

                    </router-link>

                </h2>

            </div>

        </div>

    </template>




    <script>

    export default {}

    </script>




    <style scoped>

    .medium-text { font-size: 36px; }

    .small-link { font-size: 24px; text-decoration: underline; color: #777; }

    .product-box { border: 1px solid #cccccc; padding: 10px 15px; }

    .hero-section { height: 80vh; align-items: center; margin-top: -20px; margin-bottom: 20px; }

    .title { font-size: 60px; }

    </style>

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

ایجاد داشبورد (user) کاربر

داشبورد کاربر جایی است که کاربران می توانند تمام سفارشات خود را ببینند. فایل source/assets/js/views/UserBoard.vue را باز کرده و کد زیر را برای الگو و استایل قرار دهید:

<template>

        <div>

            <div class="container-fluid hero-section d-flex align-content-center justify-content-center flex-wrap ml-auto">

                <h2 class="title">All your orders</h2>

            </div>

            <div class="container">

                <div class="row">

                    <div class="col-md-12">

                        <br>

                        <div class="row">

                            <div class="col-md-4 product-box" v-for="(order,index) in orders" @key="index">

                                <img :src="order.product.image" :alt="order.product.name">

                                <h5><span v-html="order.product.name"></span><br>

                                    <span class="small-text text-muted">$ {{order.product.price}}</span>

                                </h5>

                                <hr>

                                <span class="small-text text-muted">Quantity: {{order.quantity}}

                                    <span class="float-right">{{order.is_delivered == 1? "shipped!" : "not shipped"}}</span>

                                </span>

                                <br><br>

                                <p><strong>Delivery address:</strong> <br>{{order.address}}</p>

                            </div>

                        </div>

                    </div>

                </div>

            </div>

        </div>

    </template>




    <style scoped>

    .small-text { font-size: 14px; }

    .product-box { border: 1px solid #cccccc; padding: 10px 15px; }

    .hero-section { background: #ababab; height: 20vh; align-items: center; margin-bottom: 20px; margin-top: -20px; }

    .title { font-size: 60px; color: #ffffff; }

    </style>

سپس برای script کد زیر را قرار دهید:

<script>

    export default {

        data() {

            return {

                user : null,

                orders : []

            }

        },

        beforeMount() {

            this.user = JSON.parse(localStorage.getItem('bigStore.user'))




            axios.defaults.headers.common['Content-Type'] = 'application/json'

            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('bigStore.jwt')




            axios.get(`api/users/${this.user.id}/orders`)

                 .then(response => this.orders = response.data)

        }

    }

    </script>

در کد بالا، تمام سفارش‌های کاربر را قبل از نصب کامپوننت دریافت می‌کنیم، سپس آنها را حلقه زده و در صفحه نمایش می‌دهیم.

ایجاد داشبورد ادمین (مدیریت)

داشبورد ادمین جایی است که محصولات جدید اضافه می‌شوند، محصولات موجود ویرایش می‌شوند و سفارش‌ها به صورت تحویل داده شده تنظیم می‌شوند.

برای ادمین، ما از چهار کامپوننت مختلف استفاده خواهیم کرد که آنها را بر اساس آدرس اینترنتی که کاربر به آن دسترسی دارد، پیاده سازی می کنیم. بیایید آن را در عمل ببینیم. فایل source/assets/js/views/Admin.vue را باز کرده و کد زیر را در آن قرار دهید:

<template>

        <div>

            <div class="container-fluid hero-section d-flex align-content-center justify-content-center flex-wrap ml-auto">

                <h2 class="title">Admin Dashboard</h2>

            </div>

            <div class="container">

                <div class="row">

                    <div class="col-md-3">

                        <ul style="list-style-type:none">

                            <li class="active"><button class="btn" @click="setComponent('main')">Dashboard</button></li>

                            <li><button class="btn" @click="setComponent('orders')">Orders</button></li>

                            <li><button class="btn" @click="setComponent('products')">Products</button></li>

                            <li><button class="btn" @click="setComponent('users')">Users</button></li>

                        </ul>

                    </div>

                    <div class="col-md-9">

                        <component :is="activeComponent"></component>

                    </div>

                </div>

            </div>

        </div>

    </template>




    <script>

    import Main from '../components/admin/Main'

    import Users from '../components/admin/Users'

    import Products from '../components/admin/Products'

    import Orders from '../components/admin/Orders'




    export default {

        data() {

            return {

                user: null,

                activeComponent: null

            }

        },

        components: {

            Main, Users, Products, Orders

        },

        beforeMount() {

            this.setComponent(this.$route.params.page)

            this.user = JSON.parse(localStorage.getItem('bigStore.  d fuser'))

            axios.defaults.headers.common['Content-Type'] = 'application/json'

            axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('bigStore.jwt')

        },

        methods: {

            setComponent(value) {

                switch(value) {

                    case "users":

                        this.activeComponent = Users

                        this.$router.push({name: 'admin-pages', params: {page: 'users'}})

                        break;

                    case "orders":

                        this.activeComponent = Orders

                        this.$router.push({name: 'admin-pages', params: {page: 'orders'}})

                        break;

                    case "products":

                        this.activeComponent = Products

                        this.$router.push({name: 'admin-pages', params: {page: 'products'}})

                        break;

                    default:

                        this.activeComponent = Main

                        this.$router.push({name: 'admin'})

                        break;

                }

            }

        }

    }

    </script>




    <style scoped>

    .hero-section { height: 20vh; background: #ababab; align-items: center; margin-bottom: 20px; margin-top: -20px; }

    .title { font-size: 60px; color: #ffffff; }

    </style>

در کد بالا چهار کامپوننت را وارد و استفاده می کنیم ولی هنوز آنها را ایجاد نکرده ایم. آنها به عنوان کامپوننت در داخل کامپوننت والد Admin.vue استفاده خواهند شد.

در تمپلیت خود، مسیریابی را برای جابجایی بین کامپوننت ها تعریف کردیم. هر لینک متد setComponent را فراخوانی می کند و سپس مقداری را به آن ارسال می کند. متد setComponent فقط کامپوننت را با استفاده از دستور switch مقداردهی می کند.

بیایید اولین کامپوننت را برای کامپوننت Admin ایجاد کنیم. فایل Main.vue را در دایرکتوری resources/assets/js/components/admin ایجاد کنید و موارد زیر را در آن قرار دهید:

<template>

        <div class="row">

            <div class="col-md-4 product-box d-flex align-content-center justify-content-center flex-wrap big-text">

                <a href='/admin/orders'>Orders ({{orders.length}})</a>

            </div>

            <hr>

            <div class="col-md-4 product-box d-flex align-content-center justify-content-center flex-wrap big-text">

                <a href='/admin/products'>Products ({{products.length}})</a>

            </div>

           <div class="col-md-4 product-box d-flex align-content-center justify-content-center flex-wrap big-text">

                <a href='/admin/users'>Users ({{users.length}})</a>

            </div>

        </div>

    </template>




    <script>

    export default {

        data() {

            return {

                user : null,

                orders : [],

                products : [],

                users : []

            }

        },

        mounted() {

            axios.get('/api/users/').then(response => this.users = response.data)

            axios.get('/api/products/').then(response => this.products = response.data)

            axios.get('/api/orders/').then(response => this.orders = response.data)

        }

    }

    </script>




    <style scoped>

    .big-text { font-size: 28px; }

    .product-box { border: 1px solid #cccccc; padding: 10px 15px; height: 20vh }

    </style>

در کد بالا ما  API های users ،orders و products را فراخوانی می‌کنیم و داده‌های آنها را برمی‌گردانیم. فایل Orders.vue را در source/assets/js/components/admin ایجاد کنید و موارد زیر را در آن قرار دهید:

<template>

        <div>

            <table class="table table-responsive table-striped">

                <thead>

                    <tr>

                        <td></td>

                        <td>Product</td>

                        <td>Quantity</td>

                        <td>Cost</td>

                        <td>Delivery Address</td>

                        <td>is Delivered?</td>

                        <td>Action</td>

                    </tr>

                </thead>

                <tbody>

                    <tr v-for="(order,index) in orders" @key="index">

                        <td>{{index+1}}</td>

                        <td v-html="order.product.name"></td>

                        <td>{{order.quantity}}</td>

                        <td>{{order.quantity * order.product.price}}</td>

                        <td>{{order.address}}</td>

                        <td>{{order.is_delivered == 1? "Yes" : "No"}}</td>

                        <td v-if="order.is_delivered == 0"><button class="btn btn-success" @click="deliver(index)">Deliver</button></td>

                    </tr>

                </tbody>

            </table>

        </div>

    </template>




    <script>

    export default {

        data() {

            return {

                orders : []

            }

        },

        beforeMount(){

            axios.get('/api/orders/').then(response => this.orders = response.data)

        },

        methods: {

            deliver(index) {

                let order = this.orders[index]

                axios.patch(`/api/orders/${order.id}/deliver`).then(response => {

                    this.orders[index].is_delivered = 1

                    this.$forceUpdate()

                })

            }

        }

    }

    </script>

در beforeMount ما تمام سفارش‌هایی را که قبل از رندر شدن کامپوننت انجام می‌شوند واکشی می‌کنیم. هنگامی که دکمه Deliver کلیک می شود، روش تحویل فعال می شود. ما برای تحویل سفارشات API ها را فراخوانی می کنیم. برای اینکه تغییر فورا در صفحه منعکس شود، [$forceUpdate](https://vuejs.org/v2/api/#vm-forceUpdate) را فراخوانی می کنیم.

فایل Users.vue را در source/assets/js/components/admin ایجاد کرده و کد زیر را در آن قرار دهید:

<template>

        <div>

            <table class="table table-responsive table-striped">

                <thead>

                    <tr>

                        <td></td>

                        <td>Name</td>

                        <td>Email</td>

                        <td>Joined</td>

                        <td>Total Orders</td>

                    </tr>

                </thead>

                <tbody>

                    <tr v-for="(user,index) in users" @key="index">

                        <td>{{index+1}}</td>

                        <td>{{user.name}}</td>

                        <td>{{user.email}}</td>

                        <td>{{user.created_at}}</td>

                        <td>{{user.orders.length}}</td>

                    </tr>

                </tbody>

            </table>

        </div>

    </template>




    <script>

    export default {

        data() {

            return {

                users : []

            }

        },

        beforeMount() {

            axios.get('/api/users/').then(response => this.users = response.data)

        }

    }

    </script>

در بالا ما تمام داده های user را واکشی می کنیم و سپس آن را در صفحه نمایش می دهیم. در مرحله بعد، فایل Products.vue را در source/assets/js/components/admin ایجاد کنید و کد تمپلیت زیر را در آن قرار دهید:

<template>

        <div>

            <table class="table table-responsive table-striped">

                <thead>

                    <tr>

                        <td></td>

                        <td>Product</td>

                        <td>Units</td>

                        <td>Price</td>

                        <td>Description</td>

                    </tr>

                </thead>

                <tbody>

                    <tr v-for="(product,index) in products" @key="index" @dblclick="editingItem = product">

                        <td>{{index+1}}</td>

                        <td v-html="product.name"></td>

                        <td v-model="product.units">{{product.units}}</td>

                        <td v-model="product.price">{{product.price}}</td>

                        <td v-model="product.price">{{product.description}}</td>

                    </tr>

                </tbody>

            </table>

            <modal @close="endEditing" :product="editingItem" v-show="editingItem != null"></modal>

            <modal @close="addProduct"  :product="addingProduct" v-show="addingProduct != null"></modal>

            <br>

            <button class="btn btn-primary" @click="newProduct">Add New Product</button>

        </div>

    </template>

در زیر آن کد اسکریپت زیر را قرار دهید:

<script>

    import Modal from './ProductModal'




    export default {

        data() {

            return {

                products: [],

                editingItem: null,

                addingProduct: null

            }

        },

        components: {Modal},

        beforeMount() {

            axios.get('/api/products/').then(response => this.products = response.data)

        },

        methods: {

            newProduct() {

                this.addingProduct = {

                    name: null,

                    units: null,

                    price: null,

                    image: null,

                    description: null,

                }

            },

            endEditing(product) {

                this.editingItem = null




                let index = this.products.indexOf(product)

                let name = product.name

                let units = product.units

                let price = product.price

                let description = product.description




                axios.put(`/api/products/${product.id}`, {name, units, price, description})

                     .then(response => this.products[index] = product)

            },

            addProduct(product) {

                this.addingProduct = null




                let name = product.name

                let units = product.units

                let price = product.price

                let description = product.description

                let image = product.image




                axios.post("/api/products/", {name, units, price, description, image})

                     .then(response => this.products.push(product))

            }

        }

    }

    </script>

در پراپرتی متد ها متدهای زیر را تعریف کردیم:

  1. ()newProduct - زمانی فراخوانی می شود که می خواهیم یک شی product لوکال جدید را ایجاد کنیم.
  2. ()endEditing - زمانی فراخوانی می شود که ویرایش یک محصول به پایان برسد.
  3. ()addProduct - زمانی که می خواهیم محصول جدیدی اضافه کنیم فراخوانی می شود.

ما یک کامپوننت ProductModal را import کردیم و در ادامه آن را ایجاد خواهیم کرد. modal برای ویرایش یک محصول موجود یا ایجاد یک محصول جدید استفاده خواهد شد. با دوبار کلیک کردن روی محصول، حالت ویرایش محصول فعال می شود.

فایل ProductModal.vue را در source/assets/js/components/admin ایجاد کرده و کد زیر را برای استایل آن قرار دهید:

<template>

        <div class="modal-mask">

            <div class="modal-wrapper">

                <div class="modal-container">

                    <div class="modal-header">

                        <slot name="header" v-html="data.name"></slot>

                    </div>

                    <div class="modal-body">

                        <slot name="body">

                           Name: <input type="text" v-model="data.name">

                            Units: <input type="text" v-model="data.units">

                            Price: <input type="text" v-model="data.price">

                            <textarea v-model="data.description" placeholder="description"></textarea>

                            <span >

                                <img :src="data.image" v-show="data.image != null">

                                <input type="file" id="file" @change="attachFile">

                            </span>

                        </slot>

                    </div>

                    <div class="modal-footer">

                        <slot name="footer">

                            <button class="modal-default-button" @click="uploadFile">

                                Finish

                            </button>

                        </slot>

                    </div>

                </div>

            </div>

        </div>

    </template>




    <style scoped>

    .modal-mask {

        position: fixed;

        z-index: 9998;

        top: 0;

        left: 0;

        width: 100%;

        height: 100%;

        background-color: rgba(0, 0, 0, .5);

        display: table;

        transition: opacity .3s ease;

    }

    .modal-wrapper {

        display: table-cell;

        vertical-align: middle;

    }

    .modal-container {

        width: 300px;

        margin: 0px auto;

        padding: 20px 30px;

        background-color: #fff;

        border-radius: 2px;

        box-shadow: 0 2px 8px rgba(0, 0, 0, .33);

        transition: all .3s ease;

        font-family: Helvetica, Arial, sans-serif;

    }

    .modal-header h3 {

        margin-top: 0;

        color: #42b983;

    }

    .modal-body {

        margin: 20px 0;

    }

    .modal-default-button {

        float: right;

   }

    .modal-enter {

        opacity: 0;

    }

    .modal-leave-active {

        opacity: 0;

    }

    .modal-enter .modal-container,

    .modal-leave-active .modal-container {

        -webkit-transform: scale(1.1);

        transform: scale(1.1);

    }

    </style>

سپس موارد زیر را بعد از تگ بسته شدن style قرار دهید:

<script>

    export default {

        props: ['product'],

        data() {

            return {

                attachment: null

            }

        },

        computed: {

            data: function() {

                if (this.product != null) {

                    return this.product

                }

                return {

                    name: "",

                    units: "",

                    price: "",

                    description: "",

                    image: false

                }

            }

        },

        methods: {

            attachFile(event) {

                this.attachment = event.target.files[0];

            },

            uploadFile(event) {

                if (this.attachment != null) {

                    var formData = new FormData();

                    formData.append("image", this.attachment)

                    let headers = {'Content-Type': 'multipart/form-data'}

                    axios.post("/api/upload-file", formData, {headers}).then(response => {

                        this.product.image = response.data

                        this.$emit('close', this.product)

                    })

                } else {

                    this.$emit('close', this.product)

                }

            }

        }

    }

    </script>

هنگامی که modal داده های یک محصول را دریافت می کند، هر فیلد را از قبل با داده ها پر می کند. وقتی تصویری را ضمیمه می‌کنیم و فرم modal را ارسال می‌کنیم، آپلود می‌شود و URL مربوط به تصویر به ما بازگردانده می‌شود.

attribute تصویر محصول را با url به روز می کنیم، سپس یک رویداد close منتشر می کنیم و محصول را با آن برمی گردانیم. اگر هیچ تصویری ضمیمه نشده باشد، یک رویداد close منتشر می‌کنیم و داده‌های محصول را به همراه آن برمی‌گردانیم. این کامپوننت modal نمونه ای است که در مستندات Vue در این آدرس آموزش داده شده است.

پرداختی ها

گزینه های پرداخت زیادی برای پلتفرم های تجارت الکترونیک وجود دارد. شما با توجه به به نیاز خود و آنچه در کشور شما موجود است انتخاب می کنید. بسیاری از پردازشگرهای پرداخت محبوب مانند Stripe دارای راهنمای عالی برای ادغام در یک برنامه جاوا اسکریپت یا PHP هستند که می توانید از آنها استفاده کنید. اگرچه این آموزش آن را پوشش نمی دهد، می توانید آن را به عنوان تمرینی برای تمرین در نظر بگیرید.

ساخت اپلیکیشن

ساخت اپلیکیشن خود را به پایان رساندیم. کار بعدی این است که برنامه Vue خود را کامپایل کرده و بک اند Laravel خود را ارائه دهیم. دستور ساخت اپلیکیشن را اجرا کنید.

 npm run prod

سپس، دستور را اجرا کنید تا برنامه را آماده استفاده شود:

php artisan serve

نتیجه گیری

در این آموزش از Laravel و Vue برای ساخت یک اپلیکیشن فروشگاه ساده استفاده کردیم. این راهنما یک پیاده سازی فروشگاه را ارائه می دهد و می تواند شروعی برای یک تحول در زمینه ساخت فروشگاه اینترنتی با لاراول و Vue باشد.

ما در ابتدا سمت بک اند (API) فروشگاه اینترنتی را آم

اده کردیم و سپس با استفاده از Vue سمت فرانتد آن را به اجرا درآوردیم. ابزارهای مختلفی برای راه اندازی این فروشگاه استفاده شده که به برقراری ارتباط بین فرانتد و بک اند کمک می کند. همچنین کد کامل برنامه در GitHub موجود است.

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

منبع: وب سایت Pusher

نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.