آموزش عملیات CRUD با لاراول و Vue

laravel-vuejs

در این آموزش قصد داریم نحوه انجام عملیات crud با vue در لاراول را به شما آموزش دهیم. لاراول یکی از فریم ورک های محبوب PHP است که  سرعت رشد، مقیاس پذیری و انعطاف پذیری بالایی را دارا می باشد.

Vue.js یکی از کتابخانه های جاوا اسکریپت است که به سرعت در حال رشد است و محبوبیت بسیاری در بین توسعه دهندگان front end دارد. فریمورک لاراول بصورت پیش فرض از کتابخانه Vue.js پشتیبانی به عمل می آورد. در این مقاله یک پروژه جداگانه برای بخش front end  مان ایجاد نمی کنیم، چون لاراول بصورت پیش فرض از Vue.js  پشتیبانی می کند.

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

بنابراین کامپوننت های Vue را داخل همان فولدر های لاراول ایجاد می کنیم.

در این پروژه با استفاده از لاراول و vue.js یک برنامه تک صفحه ای یا Single page Application یا  SPA را ایجاد می کنیم. کاربران در این برنامه می توانند اقدام به ارسال پست جدید، ویرایش، مشاهده و حذف پست ها بکنند.

برای بخش back end این پروژه از Api های لاراول استفاده می کنیم.

سرفصل های این آموزش

  1. مقدمه
  2. نصب لاراول
  3. نصب وابستگی های Vueو ویرایش پیکربندی های Vue
  4. ایجاد کامپوننت های Vue
  5. پیکربندی Vue-router
  6. ایجاد یک منوی پیمایشی
  7. ایجاد یک فرم
  8. ایجاد back-end برنامه در لاراول
  9. تعریف عملیات CRUD
  10. تعریف روت های مورد نیاز
  11. استفاده از Axios برای ارسال یک درخواست
  12. ارسال درخواست ویرایش و بروزرسانی
  13. حذف داده ها

۱- مقدمه

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

۲- نصب لاراول

laravel new vuelaravelcrud

نصب لاراول

حال به مسیر پروژه رفته و دستور زیر را برای نصب وابستگی های بخش front-end برنامه، اجرا کنید.

npm install

حال با دستور زیر برنامه را در ویرایشگر خودتان باز کنید. من در این جا از visual studio code استفاده می کنم.

code .

برای تنظیم پیکربندی مربوط به پایگاه داده برنامه، کدهای زیر را در فایل .env قرار دهید.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vuecrud
DB_USERNAME=root
DB_PASSWORD=root

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

حال با اجرای دستور زیر، asset های برنامه را کامپایل کنید.

npm run dev

کامپایل asset ها در لاراول

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

npm run watch

3- نصب وابستگی های Vue و ویرایش پیکربندی ها با لاراول و Vuejs

برای نصب vue-router و vue-axios دستورات زیر را اجرا کنید.

Vue-router برای مسیریابی کامپوننت های مختلفی که در یک برنامه vue.js نوشته اید، استفاده می شود و vue-axios برای ارسال درخواست به سرور.

npm install vue-router vue-axios --save

حال باید فایل App.js که در مسیر resource/js قرار دارد را مطابق زیر ویرایش کنید.

// App.js

require('./bootstrap');

window.Vue = require('vue');

import VueRouter from 'vue-router';
Vue.use(VueRouter);

import VueAxios from 'vue-axios';
import axios from 'axios';
Vue.use(VueAxios, axios);

Vue.component('example-component', require('./components/ExampleComponent.vue'));

const router = new VueRouter({ mode: 'history'});
const app = new Vue(Vue.util.extend({ router })).$mount('#app');

حال یک فایل با نام post.blade.php در فولدر resource/views ایجاد کنید و کدهای زیر را در آن قرار دهید.

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Laravel</title>
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">
        <link href="{{ mix('css/app.css') }}" type="text/css" rel="stylesheet" />
        <meta name="csrf-token" value="{{ csrf_token() }}" />
    </head>
    <body>
        <div id="app">
          <example-component></example-component>
        </div>
        <script src="{{ mix('js/app.js') }}" type="text/javascript"></script>
    </body>
</html>

حال باید روت های برنامه را در فایل web.php بنویسید. در این مقاله ما یک برنامه تک صفحه ای با لاراول و vue.js ایجاد می کنیم، بنابراین باید روت های زیر را تعریف کنیم تا بتوانیم هر آدرسی که به آن ارسال کردیم را به یک مسیر درست هدایت کنیم. در غیر اینصورت خظای 404 را دریافت می کنیم، چون در لاراول هیچ روتی تعریف نکرده ایم و تنها برای کامپوننت های vue برنامه مان روت تعریف کردیم.

با کد زیر می توانید از روت های لاراول و Vuejs به عنوان روت های vue استفاده کنید و همچنین می توانید بسته به url جاری، کامپوننت های مناسب vue را نمایش دهید.

<?php

Route::get('/{any}', function () {
  return view('post');
})->where('any', '.*');

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

۴- ایجاد کامپوننت های Vue

یک فولدر به نام components در مسیر resources/js ایجاد کرده و فایل های زیر را در آن بوجود بیاورید.

  • HomeComponent.vue
  • CreateComponent.vue
  • EditComponent.vue
  • IndexComponent.vue
// HomeComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Home Component</div>

              <div class="card-body">
                  I'm the Home Component component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>
// CreateComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Create Component</div>

              <div class="card-body">
                  I'm the Create Component component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>
// EditComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Edit Component</div>

              <div class="card-body">
                  I'm an Edit component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Example Component mounted.')
        }
    }
</script>
// IndexComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Index Component</div>

              <div class="card-body">
                  I'm an Index component.
              </div>
          </div>
      </div>
  </div>
</template>

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

۵- پیکربندی vue-router

کدهای زیر را داخل فایل app.js قرار دهید.

// app.js

require('./bootstrap');

window.Vue = require('vue');

import VueRouter from 'vue-router';
Vue.use(VueRouter);

import VueAxios from 'vue-axios';
import axios from 'axios';

import App from './App.vue';
Vue.use(VueAxios, axios);

import HomeComponent from './components/HomeComponent.vue';
import CreateComponent from './components/CreateComponent.vue';
import IndexComponent from './components/IndexComponent.vue';
import EditComponent from './components/EditComponent.vue';

const routes = [
  {
      name: 'home',
      path: '/',
      component: HomeComponent
  },
  {
      name: 'create',
      path: '/create',
      component: CreateComponent
  },
  {
      name: 'posts',
      path: '/posts',
      component: IndexComponent
  },
  {
      name: 'edit',
      path: '/edit/:id',
      component: EditComponent
  }
];

const router = new VueRouter({ mode: 'history', routes: routes});
const app = new Vue(Vue.util.extend({ router }, App)).$mount('#app');

در بالا چهار کامپونت قبلی را import کرده و همچنین برای برنامه روت تعریف کردیم. سپس یک آبجکت router ایجاد کرده و آن را به برنامه vue ارسال کردیم.

حال یک کامپوننت دیگر به نام App.vue در مسیر resources/js ایجاد کرده و کدهای زیر را به آن اضافه کنید.

// App.vue

<template>
    <div class="container">
        <div>
            <transition name="fade">
                <router-view></router-view>
            </transition>
        </div>
    </div>
</template>

<style>
    .fade-enter-active, .fade-leave-active {
      transition: opacity .5s
    }
    .fade-enter, .fade-leave-active {
      opacity: 0
    }
</style>

<script>

    export default{
    }
</script>

در بالا ما ویوی مربوط به router را ایجاد کردیم. کدهای فوق بسته به url وارد شده در مرورگر، کامپوننت مناسب را نمایش می دهند. یعنی اگر   آدرس /create وارد مرورگر شود، آنگاه کامپوننت CreateComponent نمایش داده می شود.

۶- ایجاد یک منوی پیمایشی با لاراول و Vuejs

کدهای زیر را در فایل App.vue بنویسید. با کدهای زیر، منوهای پیمایشی برنامه را ایجاد می کنم.

// App.vue

<template>
  <div class="container">
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
      <ul class="navbar-nav">
        <li class="nav-item">
          <router-link to="/" class="nav-link">Home</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/create" class="nav-link">Create Post</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/posts" class="nav-link">Posts</router-link>
        </li>
      </ul>
    </nav><br />
    <transition name="fade">
      <router-view></router-view>
    </transition>
  </div>
</template>

<style>
    .fade-enter-active, .fade-leave-active {
      transition: opacity .5s
    }
    .fade-enter, .fade-leave-active {
      opacity: 0
    }
</style>

<script>

    export default{
    }
</script>

فایل را ذخیره کرده و نتیجه را در خروجی بینید.

ساخت منوهای پیمایشی در vue.js

۷- ساخت یک فرم با لاراول و Vuejs

فایل CreateComponent.vue را باز کرده و کدهای زیر را در آن قرار دهید.در زیر از یک فرم بوت استرپ برای ایجاد یک پست جدید استفاده می کنیم.

// CreatePost.vue

<template>
  <div>
    <h1>Create A Post</h1>
    <form @submit.prevent="addPost">
      <div class="row">
        <div class="col-md-6">
          <div class="form-group">
            <label>Post Title:</label>
            <input type="text" class="form-control" v-model="post.title">
          </div>
        </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label>Post Body:</label>
              <textarea class="form-control" v-model="post.body" rows="5"></textarea>
            </div>
          </div>
        </div><br />
        <div class="form-group">
          <button class="btn btn-primary">Create</button>
        </div>
    </form>
  </div>
</template>

<script>
    export default {
        data(){
        return {
          post:{}
        }
    },
    methods: {
      addPost(){
        console.log(this.post);
      }
    }
  }
</script>

همان طور که در کدهای فوق می بینید، ما دو فیلد داریم: عنوان و محتوای پست.

همچنین یک متد با نام addPost() تعریف کردیم. بنابراین هنگامی که کاربر فرمی را Submit کند، ما مقادیر فیلدهای ورودی را به متد addPost() ارسال می کنیم.

سپس داده ها را توسط یک متد post به سرور لاراول ارسال کرده و لاراول داده ها را در پایگاه داده ذخیره می کند.

فایل را ذخیره کرده و آدرس زیر را در مرورگر وارد کنید. localhost:3000/create.

به این ترتیب وارد صفحه ای مطابق تصویر زیر می شوید.

درج یک پست با Vue در لاراول

۸- ایجاد back-end برنامه در لاراول

هدف اصلی فریم ورک لاراول در این مثال، ساخت api برای back-end پروژه است. برای این منظور یک مدل به نام post به همراه migration مربوط به آن ایجاد می کنیم.

php artisan make:model Post -m

حال کدهای زیر را در فایل [timestamp]_create_posts_table.php قرار دهید.

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
       $table->increments('id');
       $table->string('title');
       $table->text('body');
       $table->timestamps();
    });
}

سپس پایگاه داده را با دستور زیر migrate کنید.

php artisan migrate

سپس برای جلوگیری از بروز خطای mass assignment کدهای زیر را در مدل post.php قرار دهید.

<?php

// Post.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'body'];
}

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

php artisan make:controller PostController

برای توسعه Api از resource collection های لاراول استفاده می کنیم. پس ابتدا یک resource ایجاد می کنیم.

php artisan make:resource PostCollection

هنگام ساخت یک api شما به یک لایه انتقال برای قرار گرفتن بین مدل های Eloquent و پاسخ های json برگشت داده شده به کاربر، نیاز دارید.

کلاس های resource لاراول به شما اجازه می دهند تا بتوانید به سرعت از مدل های تان خروجی json بگیرید.

resource postCollection ایی که در بالا ایجاد کردیم، در مسیر app/Http/Resources/postCollection.php قرار می گیرد.

<?php

// PostCollection.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class PostCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return parent::toArray($request);
    }
}

۹- تعریف عملیات CRUD

ابتدا یک متد برای ذخیره داده ها در پایگاه داده MySQL ایجاد می کنیم.

<?php

// PostController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Resources\PostCollection;
use App\Post;

class PostController extends Controller
{
    public function store(Request $request)
    {
      $post = new Post([
        'title' => $request->get('title'),
        'body' => $request->get('body')
      ]);

      $post->save();

      return response()->json('success');
    }
}

حال توابع edit ،update ،index و delete را هم تعریف می کنیم.

<?php

// PostController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Resources\PostCollection;
use App\Post;

class PostController extends Controller
{
    public function store(Request $request)
    {
      $post = new Post([
        'title' => $request->get('title'),
        'body' => $request->get('body')
      ]);

      $post->save();

      return response()->json('successfully added');
    }

    public function index()
    {
      return new PostCollection(Post::all());
    }

    public function edit($id)
    {
      $post = Post::find($id);
      return response()->json($post);
    }

    public function update($id, Request $request)
    {
      $post = Post::find($id);

      $post->update($request->all());

      return response()->json('successfully updated');
    }

    public function delete($id)
    {
      $post = Post::find($id);

      $post->delete();

      return response()->json('successfully deleted');
    }
}

۱۰- تعریف روت های api

حال باید روت های مورد نیاز برنامه را در فایل routes/api.php ایجاد کنیم.

<?php

// api.php

use Illuminate\Http\Request;

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

Route::post('/post/create', 'PostController@store');
Route::get('/post/edit/{id}', 'PostController@edit');
Route::post('/post/update/{id}', 'PostController@update');
Route::delete('/post/delete/{id}', 'PostController@delete');
Route::get('/posts', 'PostController@index');

۱۱- استفاده از Axios برای ارسال یک درخواست

تا به اینجا بخش back-end  برنامه را ایجاد کردیم.

قدم بعد ارسال یک درخواست post به سرور لاراول است. فایل CreateComponent.php را باز کرده و کدهای زیر را در متد addPost قرار دهید.

// CreateComponent.vue

addPost(){
    let uri = 'http://vuelaravelcrud.test/api/post/create';
    this.axios.post(uri, this.post).then((response) => {
       this.$router.push({name: 'posts'});
    });
}

در بالا یک درخواست post به سرور ارسال کردیم و سرور داده ها را در پایگاه داده ذخیره کرد. حال می خواهیم قابلیت نمایش پست ها را پیاده سازی کنیم. برای این منظور فایل IndexComponent.vue را مانند زیر بروزرسانی کنید.

// IndexComponent.vue

<template>
  <div>
      <h1>Posts</h1>
        <div class="row">
          <div class="col-md-10"></div>
          <div class="col-md-2">
            <router-link :to="{ name: 'create' }" class="btn btn-primary">Create Post</router-link>
          </div>
        </div><br />

        <table class="table table-hover">
            <thead>
            <tr>
                <th>ID</th>
                <th>Item Name</th>
                <th>Item Price</th>
                <th>Actions</th>
            </tr>
            </thead>
            <tbody>
                <tr v-for="post in posts" :key="post.id">
                    <td>{{ post.id }}</td>
                    <td>{{ post.title }}</td>
                    <td>{{ post.body }}</td>
                    <td><router-link :to="{name: 'edit', params: { id: post.id }}" class="btn btn-primary">Edit</router-link></td>
                    <td><button class="btn btn-danger">Delete</button></td>
                </tr>
            </tbody>
        </table>
  </div>
</template>

<script>
  export default {
      data() {
        return {
          posts: []
        }
      },
      created() {
      let uri = 'http://vuelaravelcrud.test/api/posts';
      this.axios.get(uri).then(response => {
        this.posts = response.data.data;
      });
    }
  }
</script>

در بالا هنگامی که کامپوننت ساخته شد، یک درخواست برای بازیابی داده ها ارسال می کنیم.

Axios به طور پیش فرض داده های پست شده به back-end را داخل data می ریزد. سپس از این data به عنوان کلید resource ایی که ساختیم استفاده می کنیم. بنابراین برای دریافت داده های واقعی باید از response.data.data استفاده کنیم. اگر از axios استفاده نکنید، باید تنها از response.data استفاده کرد.

۱۲- ارسال درخواست ویرایش و بروزرسانی

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

سپس بعد از تغییر داده ها در textbox و textarea و زدن دکمه update، متد updatePost() را فراخوانی کرده و یک درخواست post به سرور ارسال و داده ها را بروزرسانی می کنیم.

// EditComponent.vue

<template>
  <div>
    <h1>Edit Post</h1>
    <form @submit.prevent="updatePost">
      <div class="row">
        <div class="col-md-6">
          <div class="form-group">
            <label>Post Title:</label>
            <input type="text" class="form-control" v-model="post.title">
          </div>
        </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label>Post Body:</label>
              <textarea class="form-control" v-model="post.body" rows="5"></textarea>
            </div>
          </div>
        </div><br />
        <div class="form-group">
          <button class="btn btn-primary">Update</button>
        </div>
    </form>
  </div>
</template>

<script>
    export default {

      data() {
        return {
          post: {}
        }
      },
      created() {
        let uri = `http://vuelaravelcrud.test/api/post/edit/${this.$route.params.id}`;
        this.axios.get(uri).then((response) => {
            this.post = response.data;
        });
      },
      methods: {
        updatePost() {
          let uri = `http://vuelaravelcrud.test/api/post/update/${this.$route.params.id}`;
          this.axios.post(uri, this.post).then((response) => {
            this.$router.push({name: 'posts'});
          });
        }
      }
    }
</script>

۱۳- حذف داده ها با لاراول و Vuejs

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

برای اینکار کدهای زیر را در فایل IndexComponent.vue قرار دهید.

// IndexComponent.vue

<template>
  <div>
      <h1>Posts</h1>
        <div class="row">
          <div class="col-md-10"></div>
          <div class="col-md-2">
            <router-link :to="{ name: 'create' }" class="btn btn-primary">Create Post</router-link>
          </div>
        </div><br />

        <table class="table table-hover">
            <thead>
            <tr>
                <th>ID</th>
                <th>Item Name</th>
                <th>Item Price</th>
                <th>Actions</th>
            </tr>
            </thead>
            <tbody>
                <tr v-for="post in posts" :key="post.id">
                    <td>{{ post.id }}</td>
                    <td>{{ post.title }}</td>
                    <td>{{ post.body }}</td>
                    <td><router-link :to="{name: 'edit', params: { id: post.id }}" class="btn btn-primary">Edit</router-link></td>
                    <td><button class="btn btn-danger" @click.prevent="deletePost(post.id)">Delete</button></td>
                </tr>
            </tbody>
        </table>
  </div>
</template>

<script>
  export default {
      data() {
        return {
          posts: []
        }
      },
      created() {
      let uri = 'http://vuelaravelcrud.test/api/posts';
      this.axios.get(uri).then(response => {
        this.posts = response.data.data;
      });
    },
    methods: {
      deletePost(id)
      {
        let uri = `http://vuelaravelcrud.test/api/post/delete/${id}`;
        this.axios.delete(uri).then(response => {
          this.posts.splice(this.posts.indexOf(id), 1);
        });
      }
    }
  }
</script>

در بالا توسط رویداد click در vue ، id پست جاری را گرفته و پستی که آن id دارد را از پایگاه داده حذف می کنیم. همچنین توسط متد splice آن داده ها را از آرایه سمت کلاینت هم حذف می کنیم.

به این ترتیب آموزش مان به پایان رسید.

نویسنده شوید
دیدگاه‌های شما (5 دیدگاه)

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

عاوی دولوپر
29 مهر 1401
دوستان تو متد deletePost this.posts.splice(this.posts.indexOf(id), 1); کد بالا اشتباه هست و از این طریق نمی تونید اون سطر پاک کنید چون ایندکس آف درست کار نمی کنه پیشنهاد من از فیلتر ها استفاده کنید this.posts = this.posts.filter(item => item.id != id);

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

حسین آسیابی
03 فروردین 1400
سلام کامپوننت ExampleComponent رو کلا بالا توضیح ندادین!

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

Amir`
24 آبان 1399
کد های بالا مشکل دارن! در کل آموزش مبحث بسیار خوبیه ولی مطالب پر از اشکال و باگه

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

امیرحسین
03 مهر 1399
سلام ممنون از اموزش خوبتون توی این اموزش هیچ اشاره ای به csrf_fild نشده لطفا این مورد رو برسی کنید چون قطعا به مشکل میخوره..

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

Amin
14 فروردین 1399
من کارهای بالا رو انجام دادم ولی متاسفانه اطلاعات نمایش داده نمیشه و ضفحه خالی میاد مو به مو انجام دادم

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